PP & Apostille completeness audit — 2026-06-29

Grounded audit (3 parallel investigators over the real code + cross-checked vs the spec/memories, then synthesized). Verdict:

Module Verdict
PP (Perseroan Perorangan) Done (for a PoC) — all 5 in-scope flows end-to-end + tested; the 3 absent flows are intentional registry/form-only
Apostille / Legalisasi 🟡 Mostly done — the spine is complete, but 3 real (non-cosmetic) defects remain before it’s trustworthy

⚠️ Verify-before-acting: this session repeatedly found audit claims that were already fixed in code (NPWP16, bbox over-capture, paddle y-bucket). The specific file:line claims below are worth a 30-second confirm before you act on them — though the two headline Apostille defects match what I independently saw earlier.


PP — flow-by-flow

Flow Status Note
Pendirian PP ✅ done Route + validator + processor + BO CRUD + 3-gate finalize + packet ZIP + OCR + FE review + ~10 tests
Perubahan PP ✅ done Largest flow (1659-line route) but widest test set (~17 route+service+FE); per-jenis gating, diff packet, dissolution guard
Perbaikan Data PP ✅ done Edit/confirm + KBLI/BO CRUD + MENUNGGU_KONFIRMASI owner step w/ lazy-expiry; e2e + unit + FE tests
Pembubaran PP ✅ done 4-gate finalize + owner konfirmasi + double-dissolve guard; tested
Peralihan PP→PT ✅ done Akta-upload start, asal-PP match, live dissolution re-check, isolated rules; FE review-page test missing (medium)
Cek Kemiripan ⚪ intentional Embedded as pp-nama-filter.ts validation in Pendirian+Perubahan; standalone surface out of scope — not a gap
Laporan Keuangan PP ⚪ intentional Registry/form-only per design; no OCR surface
BO standalone ⚪ intentional BO fully implemented as an in-flow sub-resource; standalone registry surface intentionally not built

PP remaining = pure polish: one FE review-page test (Peralihan), a hardcoded/guessed PP_MODAL_MAX capital ceiling (TODO open question), and a stale “Stubs for later tasks” comment above fully-implemented code in perbaikan-processor.ts:56.


Apostille / Legalisasi — flow-by-flow

Flow Status Note
Upload (submit-form + legacy /start) ✅ done Country→service derivation, KTP + multi-doc zones, both AP+LGL. Redirect ?token is stored but never decoded (feeds the NIK gap)
Classify (LLM + KTP/translation overrides + board) ✅ done* Service-category gate FE+BE; deterministic overrides; OCR cached. 290-line classifier has ZERO unit test (high)
Extract (generic LLM + doc-number/word-date recovery) ✅ done Grounded fallbacks implemented + tested (21 cases). Multi-page extraction quality untested (medium)
Signer pre-match + AMBIGUOUS/UNMATCHED resolution ✅ done* Fail-soft MariaDB registry (circuit breaker + timeout), full pick/set/manual UI. resolve-signer MANUAL-preservation invariant untested (high)
Terjemahan two-axis sworn-translation ✅ done Translator-as-signer override gated on cert/reg, rides the same resolveSigner pipeline; covered in extract tests
Applicant review (manual confirm, bbox, edits-history) ✅ done* Auto-confirm intentionally removed (verified); shared PT FieldCorrection/PdfViewer; no page test (medium)
Submit gate (READY → IN_VERIFICATION) ✅ done* Hard+soft gates (NO_DOCUMENTS / VALIDATION_FAILED / SECTIONS_NOT_CONFIRMED / SIGNER_UNRESOLVED). Route gates have NO test (high)
Verifikator decide (APPROVE/REVISE/REJECT) + finalize ✅ done Transactional transitions + read-back banner; tested (6 cases). REJECT & APPROVE both land COMPLETED (no distinguishing badge); verifikatorId hardcoded (no auth — PoC)
List / verifikator queue 🟡 gap Queue NOT status-filtered — verifikator sees CREATED/EXTRACTING/READY rows and “Verifikasi” routes them to a not-yet-submitted request
Cross-validation (13 rules) 🟡 gap 12 rules live + tested (65 cases); but AP_T2_NIK_VS_KTP is permanently SKIPPEDaccountNik hardcoded null at apostille-cross-validator.ts:511, so the redirect-identity FAIL never fires
Trained-model classifier backend ⚪ intentional APOSTILLE_CLASSIFIER=model throws a by-design error; llm is the working path

* = done but with a test gap.


Top gaps to call Apostille “done”

  1. Dead identity gateAP_T2_NIK_VS_KTP can never fire: accountNik is hardcoded null (apostille-cross-validator.ts:511) and the redirect ?token is stored but never decoded. Plumb the NIK from the token so the redirect-account-vs-KTP identity FAIL actually enforces.
  2. Unfiltered verifikator queueApostilleListPage / GET /list lists pre-submission rows; “Verifikasi” can land a verifikator on a not-yet-submitted request. Filter to IN_VERIFICATION for the verifikator role.
  3. Thin tests where it matters — the 17-endpoint apostille.ts route file has only 2 route tests (signer set/pick, doc add/delete/reclassify all untested); apostille-classifier.ts (290 lines, branchy overrides) and apostille-resolve-signer.ts (MANUAL-preservation) have zero unit tests. (PP has ~52 backend + 10 FE for contrast.)
  4. Multi-page extraction unverified — OCR/extract flattens the whole buffer to one word stream with no page-scoping + no multi-page fixture — risky for Legalisasi packets.
  5. Minor — no apostille page-component tests; REJECT/APPROVE both terminate at COMPLETED with no distinguishing list badge.

Recommendation