think
16px
820px

Persona Split Wave — Done Report (2026-07-11)

Scope (Efran, 5 items): ① split verifikator PT vs Apostille/Legalisasi · ② notaris dashboard = notaris transactions only · ③ admin role + admin UI · ④ debug menus out of the notaris view · ⑤ friendlier notaris dashboard.

Status: ALL DONE & DEPLOYED — staging ahu-ai-ocr:8462be94 live on x056.ahu-demo.ocr.val.id (192.168.83.20:3520). Both suites green (backend 2521 pass / 0 fail, frontend 970 pass / 0 fail); migrations auto-applied; every persona verified live with headless screenshots + API checks.


The new persona model

flowchart LR subgraph Personas N[Notaris] VP[Verifikator PT] VA[Verifikator Apostille] P[Perseroan] U[Umum] A[Admin] end N -->|"/dashboard (friendly home, PT-family only)"| PT[PT flows] VP -->|"/verifikator?domain=pt"| PTQ[PT review queue] VA -->|"/verifikator?domain=apostille"| APQ[Apostille queue] U -->|/apostille| AP[Apostille filing] P -->|/pp/*| PP[PP flows] A -->|"/admin (all transactions) + /admin/settings"| ALL[Everything + config + debug tools]
Role Home Sees
notaris /dashboard (new friendly home) PT family only (?family=notaris)
verifikator_pt /verifikator PT queue only (?domain=pt)
verifikator_apostille /verifikator Apostille queue only (?domain=apostille)
perseroan /pp/pendirian/klasifikasi PP flows (unchanged)
umum /apostille Their apostille filings (unchanged)
admin /admin Everything + configuration + relocated debug tools

The localStorage role key is versioned to userRole.v2 — a stale pre-split "verifikator" value can't satisfy any guard, so old browsers simply restart at the notaris default instead of bouncing in redirect loops.

① Verifikator split (W1)

  • Frontend: UserRole gains verifikator_pt / verifikator_apostille; role switcher, ROLE_HOME, RoleGuard blocks, and sidebar navs are per-team. The queue page (VerifikatorDashboardPage) scopes itself from the persona: per-team title/copy, the PT team loses the APOSTILLE flow option, the apostille team loses the PT-specific jenis (PAD/PPAD) and flow filters entirely. The sidebar pending badge counts that team's backlog only.
  • Backend: GET /api/verifikator/submissions takes domain=pt|apostille (pt = every non-APOSTILLE flow — future notaris domains like yayasan/perkumpulan land on that side). Role type, the VERIFIKATOR route policy, and requireVerifikatorRole(c, action, domain) know both teams; apostille decide/spesimen-upload require the apostille domain, perbaikan decide/advisory the PT domain. The legacy bare "verifikator" role stays accepted so already-issued tokens keep working.
  • Route guards: apostille review/list = umum + verifikator_apostille + admin; the 5 PT verifikator review pages = verifikator_pt + admin; the shared /verifikator queue = both teams + admin.
  • Dev login (/login) now mints all four roles and syncs the persona.

② Notaris scope (W2)

GET /api/submissions?family=notaris filters to the 10 PT-family types (pendirian, perubahan, perbaikan, akuisisi, penggabungan, peleburan, pembubaran, berakhirnya, laporan RUPS, peralihan PP→PT). Yayasan and perkumpulan will simply join NOTARIS_FAMILY_TYPES when those flows land. The notaris dashboard reads this scope — PP and Apostille rows are gone from the notaris view (verified live: scoped call returns PT types only, unscoped returns PP too).

③ Admin role + UI (W3)

  • /admin — Semua Transaksi: the former dense DashboardPage, promoted: family filter tabs (Semua/PT/PP/Apostille), labels for all 15 flow types, per-family row actions (PT rows keep the step indicator + destinationFor routing; PP/Apostille rows link to their canonical pages via the new shared lib/submission-actions.ts). Stat cards + document work-queue + perbaikan list stay here.
  • /admin/settings — Konfigurasi Sistem: new AppSetting KV table (migration 20260711080000_app_setting); DB override wins over env default, "Kembalikan" deletes the override. Current knobs: fast-track daily quota, window open/close hour (WIB) — read by the fast-track gates through services/app-settings.ts (15 s in-process cache, invalidated on write), so quota/window changes apply without a redeploy. Plus read-only system posture: SECURITY_ENFORCE mode, OCR provider, signature-verify AI (Terhubung), specimen MinIO (belum — manual-upload fallback).
  • API: /api/admin mounted with an admin-only route policy + in-handler requireAdminRole guard on the PUT (same shadow/enforce semantics as everything else). Live round-trip verified: PUT quota 5 → source: db → PUT null → back to source: env.
  • Admin passes every RoleGuard block, so the all-transactions table can deep-link into any family's working/review pages.

④ Debug menus out of the notaris view (W4)

Notaris sidebar is now just Dashboard · Permohonan Baru · Perbaikan Data PT · Proses Dokumen. Analitik, Uji Klasifikasi, and Pengaturan (now "Data & Penyimpanan") moved into the admin nav, and their routes are admin-guarded — a notaris deep-linking /settings gets bounced home. (Pengaturan also left the umum/perseroan/verifikator navs; configuration is the admin's job.)

⑤ Friendly notaris home (W5)

New NotarisDashboardPage: greeting + date, three big CTAs (Permohonan PT Baru / Perbaikan Data PT / Proses Dokumen), a "Perlu Tindakan Anda" strip (rows waiting on the notaris: siap ditinjau, kendala, perlu perbaikan, pilih perusahaan — capped at 6 with an overflow note), and "Permohonan Saya" with human status chips (Siap Ditinjau / Menunggu Verifikasi / Perlu Perbaikan / Selesai / Kendala) + the familiar mini step indicator. No OCR-accuracy numbers, no work queues, no staff metrics.

Verification

  • Suites: backend 2521 pass (incl. new w-admin-and-scoping.test.ts: domain scoping ×3, family scoping ×2, admin-settings round-trip ×3), frontend 970 pass (LoginPage + sidebar tests updated to the split roles/versioned key). Architecture fitness gates green.
  • Live (staging :3520): AppSetting table present; ?domain=pt → 0 rows / ?domain=apostille → the 1 demo apostille row; ?family=notaris → PT types only; admin-settings PUT/revert round-trip; headless screenshots of all four affected personas (notaris home, admin overview, admin config, both verifikator queues) all render correctly with their scoped navs.

Deliberately noted

  • Roles are still a UX separator, not security — RoleGuard is localStorage-based by design (PoC, no auth). The backend guards are real but run in shadow mode until SECURITY_ENFORCE=on; the JWT role model is already split-aware for that day.
  • The pre-split "verifikator" JWT role remains accepted everywhere both teams are; drop it whenever old tokens no longer matter.
  • Admin currently reuses the generic 20-row submissions list; if the admin table needs pagination/search over full history, that's a follow-up endpoint.