think
16px
820px

K.31 — Spesimen tangguhan CORRECTED: attestation by a REGISTERED officer (+ 8 UI fixes)

Deployed: ahu-ai-ocr:7971b686 @ 192.168.83.20:3520 (x056.ahu-demo.ocr.val.id) · 2026-07-16
Live demo submission: 4bff4444-106f-4f24-a455-f8fd94d757a0 (review) · board demo: bef. start submission with both lanes
Suites: backend 2703 (0 fail) · frontend 1015 (0 fail)


1. Your correction, applied

"The spesimen tangguhan that you provided is just Mulyoto's signature again, but the actual issue is that Mulyoto is not found and is not a registered pejabat. Therefore the spesimen tangguhan should be owned by a pejabat that is registered who acknowledges that the document (e.g. Ijazah SMA) is valid."

You were right, and the fix is structural, not cosmetic. K.30 modeled the attachment as the unregistered officer's own specimen sheet — self-certification, which proves nothing. The corrected model:

  • Valid evidence = a "Keterangan Keabsahan" (FORMULIR 02 pattern) signed by a REGISTERED officer. The registry's own F02 form is exactly this: an authorized official attests that the named (retired/unregistered) officer really signed the document.
  • The sheet's "Nama Pejabat" (top) names the document's signer → still the link key that maps the sheet to the right document.
  • The closing-block signer (the attester) is who the AI verifies: the ink on the sheet is compared against the attester's registry specimen — a comparison that can actually succeed, because the attester IS in the registry.
  • A FORMULIR 01 / legacy self-sheet still auto-links to its signer, but grades INVALID — the UI says so in red and tells the applicant what a valid attachment looks like.
flowchart LR subgraph sheet["Keterangan Keabsahan (F02) — uploaded in step 1"] top["Nama Pejabat: Drs. Mulyoto
(unregistered signer)"] bottom["Closing: SARI NURLISTIANA, S.Sos., M.Si.
(ATTESTER + her ink)"] end doc["Ijazah SMA 2006
signed by Mulyoto"] reg[("Registry
spesimen 290996591
= Nurlistiana's signature")] top -- "namesAgree → LINK" --> doc bottom -- "parseAttesterName
resolveAttester" --> reg bottom -. "AI verify: sheet ink
vs HER registry specimen" .-> reg

Live result on staging: attester parsed off the real OCR, resolved to SARI NURLISTIANA (290996591, her own registry row), verify ran sheet-ink-vs-her-specimen → 44% advisory ("bandingkan manual" — honest, since the sample signature is synthetic).

Your test submission 4e2a4329 (the old self-sheet) now correctly shows the red "Lampiran tidak membuktikan keabsahan" state — that attachment was Mulyoto certifying Mulyoto.

2. The 8 UI asks

# Ask What shipped
1 Sheet missing from step-1 Classification Sheets are doc-set items again → the board shows a "Spesimen Pejabat (Lampiran)" lane with the card; the review render skips them instead (they're attachments, not targets)
2 Raw SPESIMEN_PEJABAT in UIs Backend mirror apostille-category-labels.ts — viewer tabs and every server-produced label now read "Spesimen Pejabat (Lampiran)"
3 "Lihat lampiran" opened a new page Now switches the in-page viewer to the sheet tab (setActiveDocumentId)
4 Clicking the lampiran section should preview The whole attachment row is clickable → same in-page preview
5 Floating irrelevant bbox on the sheet analyzeTangguhanSheet deletes the generic extractor's field/bbox artifacts on sheets — the viewer renders clean
6 Tanggal Dokumen edit = date picker + reason Edit dialog uses a native date input for date fields (isDatePickerField/toISODate/isoToStored, the pendirian idiom); stored format unchanged
7 Valid tangguhan ≠ "Ditandai: tidak terdaftar"; section BLUE Blue card, badge "Keterangan keabsahan terlampir", copy names the attester, waiver button hidden (the evidence carries the doc, not a manual ack)
8 RED if not found AND no tangguhan Red card (was amber) with red "Cocokkan dari registri" / "Pejabat tidak terdaftar — lanjutkan"; an invalid attachment is also red, with the Formulir-02 remedy spelled out

Card states now: blue = valid attestation · red = no evidence (un-acked) or worthless attachment · slate = acked-unregistered (the school-principal waiver, unchanged).

3. Bugs the live e2e caught (each pinned by a unit test before the fix shipped)

  1. Date-above-signature layouts — the city+date stripper removed everything after "Garut, 16 Juli 2026", including the attester's name that follows it in OCR order. Now strips the phrase only; jabatan words ("Plt. Kepala Bidang…") became stopwords.
  2. ALL-CAPS names — the live sheet prints "SARI NURLISTIANA, S.Sos., M.Si."; the mixed-case-only parser saw stamp noise and returned garbage ("Sos M Si"). Added a caps fallback that walks backward from the degree cluster and stops at the first stopword — only fires when no mixed-case name exists.
  3. "Sarip" beat "Sari Nurlistiana"namesAgree is OCR-tolerant (edit distance 1), so a stranger one letter away also "agreed", and unranked pool order picked him. Candidates now rank by exact-token overlap first.
  4. Legalisasi blocked the sheet — the service gate only allowed Perdagangan under Legalisasi, so an attestation alongside a trade document was rejected as "wrong service". SPESIMEN_PEJABAT is now service-neutral (it backs a signer, not a service). The helper moved to apostille-guards.ts — the fitness ceiling on routes/apostille.ts forced the split, as designed.

4. Mechanics

  • Schema dance #3 (your colleague's uncommitted schema/routes hunks): their diffs parked → my columns (sheetAttesterName, sheetAttesterSpesimenId, migration 20260716050000) committed alone → their hunks restored → client regenerated with both.
  • Verify pair swap: for a tangguhan run the "document" side is the SHEET file and the specimen side is the attester's registry file; a manual box drawn on the ijazah no longer steers the sheet comparison; the focus click on the signer card targets the sheet.
  • AP_SIGNER_UNREGISTERED warning now distinguishes "keterangan keabsahan oleh pejabat terdaftar X" from "lampiran terlampir, BUKAN pengesahan pejabat terdaftar".
  • Corrected sample for your own testing: https://x056.think.val.id/keterangan-keabsahan-mulyoto.pdf (F02 naming Mulyoto, attested by the real registry officer Sari Nurlistiana).

5. Commits

Commit What
7f2368bf schema: tangguhan attester columns (committed alone)
b9d04817 the corrected model, backend + frontend + tests
1b6832f6 attester parse survives date-above-signature layouts
0ebcf650 specimen sheet is service-neutral
d1f1eafa service-category rule moves to apostille-guards (fitness split)
39882ef7 ALL-CAPS attester names (staged-OCR proven)
7971b686 rank attester candidates by exact-token overlap