Audit Verifikator — PT & Apostille/Legalisasi (2026-07-11)
TL;DR: mesin verifikator-nya solid di jalur utama (queue PT ter-merge 7 flow, decision state-machine + voting, advisory LLM, audit trail) — tapi ada 4 tema masalah nyata: (T1) lubang integritas/audit pada aksi paling sensitif, (T2) verifikator punya DUA inbox terpisah yang tidak konsisten, (T3) tiga implementasi paralel untuk panel yang sama + tiga flow verifikator TANPA PDF viewer, (T4) struktural: keluarga PP — prioritas #1-mu — belum punya permukaan verifikator sama sekali. Semua temuan sudah kuverifikasi langsung di kode (file:line di bawah). Belum ada yang kuubah — menunggu pilihanmu di bagian Paket Rekomendasi.
Peta kondisi sekarang
flowchart LR
subgraph Inbox1["Inbox 1: /verifikator (GET /api/verifikator/submissions)"]
Q1[PERUBAHAN · AKUISISI · PENGGABUNGAN\nPELEBURAN · PEMBUBARAN · BERAKHIRNYA\n+ PERBAIKAN_DATA_PT di-merge]
end
subgraph Inbox2["Inbox 2: /apostille/daftar (GET /api/apostille/list)"]
Q2[APOSTILLE + LEGALISASI]
end
Q1 -->|"/verifikator/review/:id (3 flow)"| R1[VerifikatorReviewPage\nadvisory LLM + diff + voting + PDF]
Q1 -->|"/verifikator/{pembubaran,berakhirnya,peleburan}/:id"| R2[3 halaman kompak\nTANPA PDF viewer]
Q1 -->|"/verifikator/perbaikan/:id"| R3[Halaman perbaikan\npanel advisory/audit/decision SENDIRI]
Q2 -->|"/apostille/permohonan/:id/review"| R4[ApostilleReviewPage mode verifikator\nReviewEngine + signature AI + decide]
PP[Keluarga PP\npendirian/perubahan/pembubaran/perbaikan] -.->|TIDAK ADA jalur verifikator| X((∅))
- Decision recording: PT-family →
VerifikasiPerubahan + VerifikasiDecisionLog + VerifikasiAuditEvent (lengkap). Perbaikan → VerifikasiPerbaikan + decision log (audit parsial). Apostille → ApostilleVerification upsert (tanpa log, tanpa audit event).
- Advisory: 3 implementasi paralel — engine LLM+deterministik server-side (perubahan-family;
ai-advisory-engine.ts), advisory LLM terpisah untuk perbaikan (llm/perbaikan-advisory.ts), dan sintesis deterministik client-side untuk apostille (frontend/src/lib/apostille-advisory.ts, hanya 1 dari 4 cek engine).
- Role: role verifikator murni UX (jotai + localStorage); server shadow-mode — semua endpoint decide bisa dipanggil siapa pun hari ini, dan
/api/apostille + /api/perbaikan bahkan tidak diberi policy VERIFIKATOR untuk saat enforcement dinyalakan nanti.
T1 — Integritas & audit (taruhan paling tinggi)
| # |
Temuan |
Lokasi |
Kenapa penting |
| 1.1 |
POST /api/apostille/:id/decide tidak menulis audit event apa pun (0 panggilan recordAuditEvent; tidak ada decision-log) — keputusan APPROVE/REJECT/REVISE apostille tak terlacak |
routes/apostille.ts:866-922 |
Satu-satunya flow yang keputusannya invisible di audit trail |
| 1.2 |
POST /:id/override-validation — meng-override FAIL bisa TANPA alasan dan TANPA audit event, dan endpoint-nya AUTHED (bukan verifikator-scoped), tanpa atribusi siapa pelakunya |
routes/submissions.ts:1830-1861, route-policies.ts:43 |
Ini aksi kepatuhan paling sensitif di seluruh sistem (melewati gate FAIL) — dipakai PT, PP, dan Apostille sekaligus |
| 1.3 |
Catatan keputusan Perbaikan opsional (note: z.string().optional()) padahal PT & Apostille mewajibkannya — tiga kebijakan berbeda untuk aksi yang sama |
schema/perbaikan-corrections.ts:69-70 |
Konsistensi bukti keputusan |
| 1.4 |
VerifikasiPerbaikan.verifikatorUserId mati — kolom ada tapi tidak pernah ditulis; keputusan perbaikan selamanya tanpa atribusi |
prisma/schema.prisma:1383, routes/perbaikan.ts:798-877 |
Audit |
| 1.5 |
5 panggilan audit di perbaikan.ts melewati helper recordAuditEvent → kehilangan requestId/IP/user-agent/principal |
routes/perbaikan.ts:449,531,555,618,644 |
Jejak lebih lemah diam-diam di tabel yang sama |
| 1.6 |
REVISE tidak konsisten me-reset state: perbaikan me-reset override FAIL (fix "M7"), perubahan-family TIDAK (verifikator.ts:276-289); apostille REVISE tidak me-reset konfirmasi field → re-submit bisa lolos gate "semua terkonfirmasi" dengan konfirmasi basi |
routes/verifikator.ts:276-289, routes/apostille.ts:908 |
Kelas bug yang persis sama dengan yang M7 perbaiki di perbaikan |
| 1.7 |
verifikatorId hardcoded "verifikator-dev" di SEMUA halaman FE |
VerifikatorReviewPage.tsx:305 dkk. |
Wajar untuk PoC no-auth, tapi patut dicatat: audit trail tak pernah bisa membedakan manusia |
T2 — Dua inbox, satu verifikator
| # |
Temuan |
Lokasi |
| 2.1 |
Apostille tidak masuk queue verifikator — dua inbox terpisah (/verifikator + /apostille/daftar), dua link sidebar, tanpa hitungan gabungan |
routes/verifikator.ts:384-386 vs routes/apostille.ts:279-314 |
| 2.2 |
Queue PT tidak polling (data statis sampai reload) sementara list apostille polling 5 dtk |
hooks/use-verifikator.ts:210-228 |
| 2.3 |
Tidak ada filter jenis flow di queue PT padahal me-merge 7 tipe |
VerifikatorDashboardPage.tsx:39-59 |
| 2.4 |
GET /api/apostille/list tanpa pagination (kontras queue PT page/limit) |
routes/apostille.ts:279-314 |
| 2.5 |
Tidak ada konsep SLA/prioritas di queue PT (apostille percepatan sudah punya slaDeadline + sort prioritas; PT hanya sort risk — dan baris Perbaikan selalu di ekor sort risk karena tanpa aiRecommendation) |
routes/verifikator.ts:456-464,553 |
| 2.6 |
Copy salah label: header queue & help modal bilang "Triage submisi Perubahan PT" padahal isinya 7 flow; judul halaman review bersama hardcoded "Review Perubahan PT" bahkan untuk Akuisisi/Penggabungan (payload-nya memang tak membawa type) |
VerifikatorDashboardPage.tsx:201-204, VerifikatorReviewPage.tsx:335 |
| 2.7 |
Tidak ada badge jumlah pending di sidebar |
sidebar.tsx:99-118 |
T3 — Pengalaman review tidak seragam
| # |
Temuan |
Lokasi |
| 3.1 |
Pembubaran, Berakhirnya, Peleburan: verifikator TIDAK BISA melihat PDF sumber sama sekali (tiga halaman itu tak mengimpor ReviewPdfViewer) — memeriksa "buta" dari snapshot field |
VerifikatorPembubaranReviewPage.tsx dkk. |
| 3.2 |
3 implementasi Decision Panel (verifikator/review, perbaikan raw-<button> dengan aturan catatan berbeda, apostille), 2 Audit Trail Panel, 3 advisory — visual & perilaku beda-beda |
components/verifikator/review/* vs components/perbaikan/* vs components/apostille/* |
| 3.3 |
Tidak ada tombol kembali-ke-queue di halaman perbaikan & apostille (verifikator "terdampar" setelah memutus) |
VerifikatorPerbaikanReviewPage.tsx, ApostilleReviewPage.tsx |
| 3.4 |
Read-back keputusan (banner "apa yang terjadi") hanya rapi di Apostille; PT sub-flow bergantung render internal DecisionPanel |
apostille-verifikator-blocks.tsx:112-123 |
| 3.5 |
Advisory LLM sengaja di-nol-kan untuk pembubaran/berakhirnya/peleburan ("perubahan-shaped diff report" tidak cocok) — verifikator flow ini tanpa "Saran AI" |
pembubaran.ts:262 dkk. |
| 3.6 |
Tidak ada keyboard shortcut / bulk triage di mana pun (murni klik satu-satu) |
— |
T4 — Struktural
| # |
Temuan |
| 4.1 |
Keluarga PP tidak punya permukaan verifikator SAMA SEKALI — semua PP flow selesai lewat finalize milik pemohon (atau konfirmasi email BO), tanpa queue, tanpa decide, tanpa audit keputusan. Padahal prioritasmu PP > Apostille > PT. |
| 4.2 |
Enforcement role server-side masih shadow (by design sampai token issuance ada); tapi policy map-nya pun belum siap: /api/apostille & /api/perbaikan (yang memuat endpoint decide) cuma AUTHED — begitu enforcement nyala, notaris legal memanggil decide. Butuh policy per-route yang lebih halus dari prefix-mount. |
Paket rekomendasi (pilih yang mau dikerjakan)
| Paket |
Isi |
Dampak |
Effort |
| P1 — Integritas & Audit (backend) |
Audit event + decision log untuk apostille /decide; override-validation wajib alasan + audit event + atribusi; catatan perbaikan wajib + verifikatorUserId ditulis; 5 panggilan audit perbaikan → recordAuditEvent; reset override saat REVISE perubahan-family + reset konfirmasi saat REVISE apostille; policy VERIFIKATOR untuk kedua endpoint decide (siap-enforcement, tetap shadow sekarang) |
Tinggi (kepatuhan/bukti) |
~1 sesi |
| P2 — Satu Inbox |
Apostille masuk GET /api/verifikator/submissions (atau list terpadu baru): satu queue lintas 8 flow + kolom Jalur/SLA, filter jenis flow, polling, pagination apostille, badge pending di sidebar, perbaiki copy salah label + judul type-aware |
Tinggi (UX harian verifikator) |
~1 sesi |
| P3 — Review UX Seragam |
PDF viewer untuk pembubaran/berakhirnya/peleburan; perbaikan pindah ke panel bersama (advisory/audit/decision); banner read-back keputusan seragam; tombol kembali-ke-queue di semua halaman |
Sedang-tinggi |
~1–1,5 sesi |
| P4 — Verifikator PP (baru) |
Rancang + bangun staging verifikator untuk keluarga PP (model keputusan + queue + decide + audit, pola VerifikasiPerubahan), masuk ke inbox terpadu |
Tinggi (prioritas #1-mu, gap terbesar) |
~2 sesi; butuh keputusan scope: PP flow mana saja & apakah menggantikan finalize milik pemohon atau menjadi tahap setelahnya |
| P5 — Lanjutan |
Advisory terpadu (apostille server-side + persisted; advisory non-diff untuk pembubaran/berakhirnya/peleburan); keyboard shortcuts + bulk triage; identitas verifikator nyata (bareng token issuance) |
Sedang |
~1–2 sesi |
Rekomendasiku kalau disuruh memilih urutan: P1 → P2 → P3 (P1 menambal lubang bukti/kepatuhan yang murah tapi penting; P2 mengubah pengalaman harian verifikator paling terasa; P3 menyeragamkan). P4 paling strategis tapi butuh keputusan desainmu (PP selama ini sengaja self-service — menambah verifikator mengubah alur produknya). P5 menyusul.
Semua temuan diverifikasi manual pada commit e7abdd11. Belum ada perubahan kode.