PLAN — Implementasi flow Berakhirnya Status Badan Hukum PT di ai-ahu
Untuk: agen implementasi (Opus 4.8, jalan di VM) atas worktree
/home/efran/remote-development/poc-ahu-ai/ahu-ocr-dev/dev_3—
branch kerja:fix/berakhirnya-status-badan-hukum(sudah dibuat; verifikasi dengan
git branch --show-currentsebelum mulai). Self-contained; semua path diverifikasi
2026-07-14, temuan sample ditambahkan 2026-07-15.
Sumber kebenaran desain:docs/superpowers/specs/2026-07-02-berakhirnya-status-pt-design.md
(316 baris, sudah direview). Baca spec itu SEBELUM mulai; plan ini adalah urutan
eksekusinya + temuan tambahan dari SABH legacy dan inspeksi sample yang tidak ada di spec.
Ditulis: 2026-07-14 · Diperbarui: 2026-07-15.Data verifikasi (BUKAN data latih): 30 PDF nyata (6 kelas × 5) di
~/remote-development/poc-ahu-ai/sample-berakhirnya/— sengaja di luar worktree,
dokumen hukum riil, JANGAN pernah masuk git. Pakai untuk verifikasi tiap tahap.
PLAN-unduh-data-berakhirnya.mdTIDAK perlu dijalankan.
Latar
Fitur berakhirnya status PT belum dibangun — bundelnya ditolak sebelum Submission dibuat,
dan 5 dari 6 dokumen syaratnya tak dikenal GPU classifier (jatuh ke fallback AKTA
diam-diam). Root cause lengkap per dokumen:
obsidian/Bugs/BUG - Enam dokumen berakhirnya status tidak terklasifikasi.md.
Penting: retraining GPU classifier OUT OF SCOPE (spec eksplisit; lihat juga
obsidian/Decisions/ADR-0012 - ...md). Semua fix di lapisan aplikasi.
Kerjakan bertahap dan berurutan — tiap tahap selesai + test hijau sebelum lanjut.
Tahap 1–2 aman di-ship sendiri; Tahap 3 mengandung launch switch.
Tahap 0 — Baseline sebelum menyentuh kode (wajib, ±30 menit)
Lempar 30 PDF sample ke POST /classify (classifier existing) dan catat per file:
label GPU + confidence + reason seam (kalau terpicu) + potongan rawText. Simpan hasilnya
sebagai file di luar worktree (mis. ~/remote-development/poc-ahu-ai/baseline-berakhirnya.json).
Tujuan:
1. Bukti before/after untuk reviewer — tanpa baseline, klaim "fix bekerja" tidak terukur.
2. Kalibrasi regex — khususnya cek rawText PaddleOCR pada 10 kliping koran: fragmen
teks apa yang benar-benar selamat OCR menentukan keyword axis 149/152 yang realistis.
Tahap 1 — Hentikan fallback diam-diam (kecil, mandatory)
Spec §5 item 4 menandai ini mandatory: hari ini mapToDocumentType
(backend/src/routes/submissions.ts:34-59) diakhiri return "AKTA"; // fallback, sehingga
surat permohonan / laporan likuidasi / kliping koran menjadi "akta" dan dibakar lewat
extractor akta 122B.
- Tambah mapping eksplisit di
mapToDocumentType:
-SURAT_PERMOHONAN→"SURAT_PERMOHONAN"
-LAPORAN_LIKUIDASI→"LAPORAN_LIKUIDASI"
-BUKTI_PENGUMUMAN,BUKTI_PENGUMUMAN_149,BUKTI_PENGUMUMAN_152→"BUKTI_PENGUMUMAN"
(KetigaDocumentTypesudah ada di enum Prisma —contract/enums.gen.ts:16-45— tidak
perlu migrasi.) - Tambah branch di
processDocument(backend/src/services/document-processor.ts:2345-2545)
untuk ketiga tipe di atas supaya TIDAK masuk cabang "7. Akta handling". Pola contoh:
caseBUKTI_PENGUMUMANyang sudah ada (:2510, OCR-only via PaddleOCR dengan
unwarp:false, orient:false). UntukSURAT_PERMOHONANdanLAPORAN_LIKUIDASIcukup
OCR + simpan rawText dulu (extractor menyusul di Tahap 3). getProcessingOrder: beri ketiga tipe order non-0 supaya tidak bersaing sebagai
"primary deed".
Verifikasi: unit test untuk mapToDocumentType (tiga label baru + label tak dikenal —
putuskan bersama reviewer apakah fallback AKTA diganti error/flag eksplisit; minimal
log warning). bun test regression hijau, khususnya
backend/src/services/__tests__/supporting-doc-classifier.test.ts.
Tahap 2 — Rule batas tanggal (permintaan user, server-side)
Temuan dari SABH legacy (~/work/ahu-online, view
protected/modules/sabh/views/perseroan/_formPerubahanStatusDetail.php:454-528): dua batas
tanggal di form SABH hanya ditegakkan client-side jQuery datepicker — server SABH cuma
validasi format dd-MM-yyyy dan strtotime yang gagal malah jadi null diam-diam
(TransaksiPerseroan.php:2258). ai-ahu harus menegakkannya server-side.
Buat backend/src/flow-engine/rules/berakhirnya/ (ikuti pola rules flow peleburan yang
sudah ada; severity ikut konvensi flow-engine):
BSBHP_AKTA_RECENCY—tanggal_akta_notaris∈ [hari-ini − 30 hari, hari ini].
(SABH:minDate: -30, maxDate: 0.)BSBHP_PENGUMUMAN_152_WINDOW—tanggal_surat_kabar_152∈
[tanggal_rups,tanggal_rups+ 30 hari] (Pasal 152(3) UUPT). Bilatanggal_rups
tidak terekstrak → WARN "tidak dapat divalidasi", bukan FAIL.BSBHP_PENGUMUMAN_ORDER— urutan pengumuman 149 mendahului akta/RUPS pembubaran,
sesuai desain spec §4.3 (mengutip UU 40/2007 Ps.147(1)/152(3)).
Sumber tanggal = hasil ekstraksi OCR (focused pass), bukan input manual — rule berjalan
setelah ekstraksi, persis pola peleburanPtFlow. Tanggal dokumen ditulis notaris dalam
format Indonesia — pakai util parsing tanggal yang sudah ada di repo (cari parseIndoDate
/ setara di backend/src/services/; jangan tulis parser baru kalau sudah ada).
Verifikasi: unit test pola backend/src/flow-engine/__tests__/pembubaran-*.test.ts:
dalam-batas, luar-batas (kedua arah), RUPS kosong, tanggal tak ter-parse.
Tahap 3 — Flow penuh (ikut spec §4–5)
Urutan eksekusi (launch switch PALING AKHIR):
- Enum
BERAKHIRNYA_STATUS_PTkeSubmissionType(contract/enums.gen.ts:58-72—
perhatikan file*.gen.ts: cari generator-nya, jangan edit hasil generate langsung). flows/berakhirnya-status-pt.tsdibackend/src/flow-engine/flows/—
requiredDocsdari spec §4.2:
["AKTA", "BERITA_ACARA_RUPS", "SURAT_PERMOHONAN", "LAPORAN_LIKUIDASI", "BUKTI_PENGUMUMAN", "KTP", "NPWP"]
+REQUIRED_SUPPORTING_DOCSFAIL kalau kurang:AKTA_BERAKHIRNYA,BERITA_ACARA_RUPS,
SURAT_PERMOHONAN,LAPORAN_LIKUIDASI,BUKTI_PENGUMUMAN_149(≥1),
BUKTI_PENGUMUMAN_152(≥1),KTP,NPWP+ rules Tahap 2 + focused pass
BUKTI_PENGUMUMAN(ganti stubPEMBUBARAN_PT: {engine:"v1-fork"}diregistry.ts:31-32
TIDAK termasuk scope ini — biarkan).
⚠️ Checklist harus mengizinkan satu dokumen memenuhi DUA syarat sekaligus — bukti
dari sample nyata (2026-07-15): PT Bukit Lentera memakai PDF yang sama persis untuk
BERITA_ACARA_RUPSdanLAPORAN_LIKUIDASI(BAR yang memuat penerimaan pertanggung-
jawaban likuidator). Jangan implementasikan "6 syarat = 6 file berbeda"; syarat dianggap
terpenuhi bila ADA dokumen (boleh sama) yang cocok.- Board:
BERAKHIRNYA_DOC_TYPESdibackend/src/routes/klasifikasi.ts(pola
PELEBURAN_DOC_TYPES:96-192) — kliping koranmaxCount: 2(satu per pasal;
board peleburan cap-nya 1, jangan ditiru), + label display diLABEL_MAP. - Update keyword seam
supporting-doc-classifier.ts— temuan dari inspeksi 30 sample
(2026-07-15), tulis unit test-nya di test file yang sudah ada:
- Anchor nama PT untuk kliping koran. Sample koran adalah HALAMAN KORAN UTUH berisi
banyak boks pengumuman PT lain (pengambilalihan, reksadana, likuidasi PT lain) —
keyword scan atas seluruh halaman salah sasaran. Nama PT pemohon sudah diketahui dari
submission: cari fragmen rawText yang memuat nama perseroan, klasifikasi semantik
hanya pada window teks di sekitarnya (± beberapa ratus karakter — kalibrasi dari
baseline Tahap 0, karena PaddleOCR memecah teks koran jadi fragmen).
- Perluas axis 149: sekarangKREDITOR|PEMBUBARAN; sample nyata 149 ada yang berupa
pengumuman pengakhiran pailit via putusan pengadilan → tambah
PAILIT|KURATOR|PUTUSAN|KEBERATAN|TAGIHAN. (Axis 152 sudah tepat:
HASIL LIKUIDASI|PERTANGGUNGJAWABAN— boleh tambahHASIL AKHIR.)
- Aliasing sirkuler → BAR: 3 dari 5 sample BAR sebenarnya "KEPUTUSAN SIRKULER"
(pengganti rapat fisik, sah menurut UUPT) — patternKEPUTUSAN SIRKULER|SIRKULER PARA PEMEGANG SAHAM+ konteks pembubaran/likuidasi harus disarankan sebagai
BERITA_ACARA_RUPS.
- Varian laporan likuidasi: format sangat beragam (pernyataan 1 halaman bermeterai,
laporan bilingual kantor hukum puluhan halaman, "surat pernyataan dan neraca") tapi
semantik konvergen — pastikan keyword menutup
PERNYATAAN .* HASIL AKHIR .* LIKUIDASIdanLAPORAN PERTANGGUNGJAWABAN LIKUIDATOR.
- Kliping koran tetap suggest-only / never auto-accept — jangan diubah. - Extractor:
-bukti-pengumuman-extract.ts— prompt sekarang hardcode Pasal 127(2) peleburan;
parametrisasi per pasal (149: pengumuman pembubaran + panggilan kreditor; 152: hasil
akhir likuidasi). WirerecoverDocNumberViaLlm(ada diapostille-generic-extract.ts)
untuknomor_pengumuman— spec §7 mengasumsikan ini tapi belum di-wire.
- Extractor barusurat-permohonan-extract.ts(nomor + tanggal surat) dan
laporan-likuidasi-extract.ts(nama likuidator, nomor/tanggal surat, sisa kekayaan) —
daftar field dari risetdocs/research/sabh-pt-untouched-flows-2026-07-01.md§Berakhirnya.
Daftarkan diFOCUSED_PASS_HANDLERS(flow-engine/focused-passes.ts). TITLE_RULESdiakta-txn-classifier.ts:57: tambah
{ re: /^\s*AKTA\s+BERAKHIRNYA/im, txnType: "berakhirnya" }(spec open question 6 —
resolve sebagai: tambah). Tambahkan juga keyAKTA_BERAKHIRNYAkeAKTA_SUBTYPE_MAP
dibackend/src/ocr/classifier.ts(sekarang throw).- Company lookup: dissolved PT tersaring
status_transaksi = 1
(backend/src/services/company-lookup.ts, spec §7 "Dissolved PT invisible") — tambah
paramallowedStatusTransaksidan set dari flow berakhirnya. Tanpa ini submission stall
di pemilihan perusahaan dengan daftar kosong tanpa error. - Launch switch, komit terpisah paling akhir:
BUILT_AKTA_TXN_TYPES+="berakhirnya"
(akta-txn-classifier.ts:36-42) +inferSubmissionType(submissions.ts:100-122)
mengembalikanBERAKHIRNYA_STATUS_PT.
Verifikasi Tahap 3: unit test flow (pola pembubaran-context.test.ts /
pembubaran-schema.test.ts); e2e: upload set sample dari
~/remote-development/poc-ahu-ai/sample-berakhirnya/ (mulai dari 1 perusahaan = 6 PDF
dengan prefix nomor transaksi sama, lalu kelima-limanya) → semua terklasifikasi benar di
board, rules tanggal menyala, submission terbentuk. Bandingkan dengan baseline Tahap 0
sebagai bukti before/after. Regression penuh bun test.
Jebakan yang sudah diketahui (jangan ulangi)
- Confident-wrong GPU: GPU melabeli dokumen tak dikenal ke kelas terdekat >0.6 —
koreksinya adalah seam + operator board, jangan naikkan threshold global. - Kliping koran = permukaan OCR terlemah: PaddleOCR memecah teks koran; nilai yang tak
verbatim-present di rawText akan ditolak grounding (→ null). CekrawTextdulu sebelum
menyalahkan extractor. refineAktaLabelbisa mem-promote BA RUPS keAKTA_BERAKHIRNYA(BA RUPS-lah yang memuat
frasa "berakhirnya") — pastikan setelah Tahap 3 promosi ini tidak membuat bundel kehilangan
BERITA_ACARA_RUPS-nya di checklist.- Flow ini di SABH bernama
PerubahanStatus*tapi judul bisnisnya "Berakhirnya Status
Badan Hukum" — flow yang sama, jangan dibangun dua kali (riset 2026-07-01 baris 5 vs 8). - Sample 30 PDF = data verifikasi, BUKAN data latih — jangan usulkan fine-tune/retrain
dari sample ini, dan jangan pindahkan/copy ke dalam worktree (dokumen hukum riil,
risiko ke-commit). - 149 vs 152 tidak akan pernah terpisah secara visual — sample membuktikan keduanya
halaman koran utuh yang identik bentuknya; pembedanya murni semantik di boks yang
memuat nama PT pemohon.
Out of scope branch ini (follow-up terpisah, jangan dikerjakan)
- Template surat permohonan/pernyataan yang di-generate sistem — pola sudah ada di repo
(services/surat-keterangan-generator.ts, docxtemplater → LibreOffice → PDF +
scripts/sk-roundtrip-diag.tssebagai guard drift). Akan dibuatkan spec sendiri setelah
flow ini ship; butuh keputusan produk (wajib vs opsional bagi notaris). - Slot upload bernama ala
FIELD_TO_DOCTYPE(routes/perbaikan.ts:34) — mengubah
klasifikasi dari "tebak jenis" jadi "verifikasi isi slot", sekaligus menyelesaikan
dokumen multi-peran secara natural. Layak diangkat ke reviewer sebagai arah desain,
tapi spec 2026-07-02 memakai bundle+board — jangan menyimpang dari spec di branch ini. - Retraining GPU classifier — permanen out of scope (ADR-0012).
PLAN-unduh-data-berakhirnya.md— tidak blocking; hanya untuk eval set besar nanti.
Konteks vault (opsional)
obsidian/Concepts/Klasifikasi Tiga Tingkat.md— arsitektur klasifikasiobsidian/Components/Akta Txn Classifier.md,Supporting Doc Classifier.mdobsidian/Flows/Flow - Permohonan berakhirnya status badan hukum (SABH legacy).md—
detail alur + batas tanggal SABH