K.37 — Multi-signature + pejabat auto-select · phone standardization · assignment history
Deployed: ahu-ai-ocr:60e1e9b5 @ x056.ahu-demo.ocr.val.id · 2026-07-21
Suites: backend 2824 · frontend 1034 (0 fail)
1. Multi-signature documents — the pejabat's ink is now chosen, not guessed
The verify service's /verify silently used its single best-scoring crop — on an ijazah carrying the student's AND the principal's signatures (or a notarized doc where the notaris co-signs), the WRONG ink could win the comparison. Now:
- The backend runs
/detectitself and keeps every candidate (SignerMatch.signatureDocCandidates, the chosen one marked) — nothing is thrown away. - The pejabat's signature is selected by proximity to their printed name: extraction already locates the
doc_signer_namebox, and people sign next to their printed name. Same-page candidates first, then nearest center distance with vertical distance weighted double. No name box → highest detector score (old behavior). - The chosen box is pinned onto
/verifyviadoc_box, so the comparison provably runs on the selected ink. The manual-draw override still wins over everything. - Review shows "N tanda tangan lain terdeteksi — tanda tangan pejabat dipilih otomatis dari posisi nama tercetak" with the redraw remedy if the pick is wrong.
Live e2e (fresh apostille run, cbffca7a…): the pipeline logged 2 candidate(s), picked page 1 and persisted both candidates with the selection mark; verify ran OK on the pinned box.
Explicitly still blocked (recon verdicts): verifying the NOTARIS signature needs a notaris-specimen source — none exists today (the apostille registry holds certifying officers only; SABH notaris data has no wired specimen store). Detecting the cap/stempel as its own object needs a new detection class in the Python service (today the scorer only strips stamps to isolate ink). Both are clean follow-ups now that multi-candidate plumbing exists.
2. Phone standardization
lib/phone-normalize.ts: every OCR disguise ("0812 3456 7890", "+62-815-2299-801 3", "(0274) 512345") → one canonical form with country code, mobile/landline kind, and the landline area code detected (longest-match table: 021/022/…/0274/…). Display grouped (+62 812-3456-7890, +62 21 555-0123), e164 as the joinable key. Wired into the contact-info extraction cleanup; unparseable input passes through untouched.
3. Assignment history (verifikator)
Submission.assignedVerifikator pointer + append-only AssignmentEvent (fromAssignee → toAssignee, movedBy, reason, timestamp):
POST /api/verifikator/submissions/:id/assign— assign / transfer / return-to-pool (no-op moves record nothing);GET /api/verifikator/submissions/:id/assignment-history— newest-first trail: "dipindah oleh siapa, dari mana".
This is the groundwork the SLA/FIFO escalation and cuti features stand on (both P1 in the K.35 triage).
Commits
60e1e9b5 |
all three features + migration 20260721060000 + tests |