think
16px
820px

Apostille Smoke Wave — Done Report (2026-07-12)

Status: ALL DONE & DEPLOYED — staging ahu-ai-ocr:a14cada3 (192.168.83.20:3520). Both suites green (backend 2525, frontend 970); every fix re-verified live with scripted browser checks.


Review page …/81354581…/review

Q: Why "Nama pejabat penandatangan tidak terbaca dari dokumen"?
That demo submission's document (an ijazah PNG) has extractionStatus = ERROR — extraction failed entirely, so there was never a signer name to read. It only reached the verifikator because I force-submitted it with the debug bypass while seeding demo data; a real applicant can't submit while a document is un-extracted (the gate blocks it). Fixes shipped anyway:

  • The signer block now distinguishes the two cases: extraction ERROR → "Dokumen gagal diekstraksi, sehingga data pejabat penandatangan tidak tersedia"; extraction OK but no name → "…biasanya karena nama tidak tercetak di dekat tanda tangan atau hasil pindai kurang jelas".
  • When editable, the remedy is now a prominent button — "Cari pejabat di registri / isi manual" (opens the existing registry-search modal) — instead of the tiny footer link.

Upload page /apostille

  • Jalur Percepatan is now a plain checkbox row (checkbox + one-line description). The big amber banner card is gone.
  • KTP preview: the picked KTP shows an inline thumbnail (images directly; PDFs render page 1) — click it or the eye icon for the full preview.
  • Step 1 → 2 slowness answered: it is neither extraction nor classification — the wait is the multipart file upload itself (KTP + N documents travel to the server in one request; the server responds immediately after storing them, and classification runs in the background with its own UI on step 2). Fix: a new XHR-based upload path streams real progress, and while uploading the whole form is replaced by a dedicated "Mengunggah N berkas… %" panel with a progress bar — no more spinning button. At 100% it switches to "Menyiapkan permohonan…" for the (sub-second) row creation, then navigates to the klasifikasi board, which already had its own "Mengklasifikasi dokumen…" state.

Klasifikasi board …/klasifikasi

  • Empty lanes hidden: the board now shows only categories that hold documents. All categories appear as drop targets the moment you start dragging a card (the header copy says so). 12 empty dashed lanes gone.

Review page …/cee52830…/review

  • "12 isian belum dikonfirmasi" / couldn't submit — root cause found, and it was a real bug (not spesimen). The submit gate was unsatisfiable from BOTH directions:
    1. The projector exposed the doc_signer_name ref as a gate-counted field, but it is never rendered as a confirmable row (the signer has its own block + modal) — so one item per document could never be confirmed. Now excluded from the gate absolutely via a new contract flag (confirmable:false), including under the manualConfirmAll mode staging runs.
    2. The backend /submit counted every non-LOCKED KTP field — including flagged fields outside the 11 curated identity keys the UI shows (your KTP's empty golongan_darah would have 409'd invisibly). It now counts exactly the curated keys + the 4 holder fields per document — the same set the UI renders.
    Verified live: the button now enables with "Semua bagian terkonfirmasi".
  • Submit bar is full-page-width (moved out of the 62% column; the verifikator decision panel too — PT/PP parity).
  • Progress header counts documents now: "Bagian 1/2 … 2/2 selesai" — the Dokumen Apostille/Legalisasi section is a first-class segment, and "Isian berikutnya" walks into it.
  • Extra: when submit is blocked, the bar shows per-section chips ("Dokumen Apostille/Legalisasi: 1") that scroll-and-expand to the culprit section, and each document card gets a "Konfirmasi semua isian benar (N)" bulk action.
  • "Negara tujuan … Layanan Apostille" restyled — a bordered chip + colored service badge instead of the plain text line.
  • Zoom no longer breaks the page. Root cause: zooming set a fixed pixel width inside a shrink-0 flex column with no min-width:0, so the column grew to fit, the viewer re-measured the wider container, multiplied by the zoom again — a runaway loop until the image covered the page. min-w-0 added to the PDF column on all 14 review pages (the bug was latent everywhere); verified at 130% the viewer stays in its column and scrolls internally.

Jalur Percepatan without payment in scope — suggestions

The Rp500.000 is currently display-only. Three ways to make percepatan honest without building billing:

  1. Payment-reference field + verifikator attestation (recommended). Applicant fills an optional "Kode Billing / NTPN" text field at submit (payment itself happens on the real PNBP/SIMPONI rails, outside our app). The verifikator sees it as an advisory row and — for PERCEPATAN rows only — must tick "bukti pembayaran sudah diperiksa" before APPROVE (stored in the audit trail like decision notes). Zero external dependency, fully auditable, ~1 day of work.
  2. Pure attestation (lightest). Skip the reference field; just gate the PERCEPATAN approve behind the verifikator checkbox. Payment verification stays 100% out-of-band. Half a day.
  3. Dormant payment-status seam (most future-proof). Add a fastTrackPaymentStatus (UNPAID → CLAIMED → VERIFIED) column + a manual "tandai terverifikasi" admin/verifikator action now, and a webhook endpoint stub — when a PNBP/SIMPONI integration becomes in-scope it plugs into the same states with no schema change. ~2 days.

Option 1 and 3 compose (1 is essentially 3's manual half). Say the word and I'll build whichever.

Also in this wave

  • New shared apiUpload (XHR) helper with progress + the same error envelope/401 handling as apiFetch.
  • Contract gains ReviewFieldVM.confirmable?: boolean (single shared contract/review.ts used by both packages).
  • One pre-existing flaky FE test (klasifikasi kebab-reclassify) repeatedly tripped its 5s timeout under deploy-gate CPU load — raised to 15s.