Willy Anwar was never a shareholder — extraction fixed, SABH parity confirmed, PR #2 verdict
Deployed: ahu-ai-ocr:e4932373 @ 192.168.83.20:3520 · 2026-07-20
Submission fixed live: f210eb31 review — PEMEGANG_SAHAM_BUKTI_SETOR now PASS: "4 pemegang saham cocok"
1. Is Willy Anwar a shareholder? No — and yes, we were wrong.
Read straight from the deed's OCR (the attached Akta Pendirian PT Virtue Digital Indonesia):
The end-of-akta allotment ("Untuk pertama kalinya telah diambil bagian… oleh para pendiri") lists exactly four parties:
| Founder | Lembar | Nominal | % |
|---|---|---|---|
| PT MGM INTEGRA TEKNOLOGI | 2.750 | Rp 275.000.000 | 55 |
| FARIZ ISKANDAR | 1.500 | Rp 150.000.000 | 30 |
| DANNY | 500 | Rp 50.000.000 | 10 |
| ERLANGGA BUDI SANGGRAMA | 250 | Rp 25.000.000 | 5 |
| Total | 5.000 | Rp 500.000.000 | 100 |
Willy Anwar appears in the deed exactly twice, and neither is a shareholding:
1. Penghadap #1 — "bertindak selaku Direktur dari dan oleh karenanya sah bertindak untuk dan atas nama PT MGM INTEGRA TEKNOLOGI" — he signs for the corporate shareholder, he isn't one himself;
2. Komisaris Utama in the pengurus appointment.
Our LLM extraction had emitted him as a fifth pemegang saham row with 0 lembar / 0% / Rp 0 (confidence 1!), which is what tripped PEMEGANG_SAHAM_BUKTI_SETOR — the validator then honestly reported "WILLY ANWAR: tidak ada di Bukti Setor" about a man who owes the Bukti Setor nothing.
The fix (e4932373)
- Transform guard: when the share-holding rows alone already total 100% (±1) — or exactly the issued share count — a share-less row is provably an artifact (UUPT Ps. 7(2): every founder must take shares; ownership totals 100 across ALL holders) and is dropped. Rows whose numbers are all null are never dropped: an unreadable scan keeps its name-only roster.
- Prompt hardened: pemegang_saham = only the allotment-list parties; a penghadap acting "untuk dan atas nama" a company is NOT a shareholder; direksi/komisaris are not shareholders unless allotted; never emit a 0-share person.
- 4 regression tests on the exact Virtue roster (drop via %-route, drop via share-count route, keep-when-incomplete, keep-when-all-null).
- Live data healed: the phantom row deleted from f210eb31 + revalidated → all modal rules PASS; the roster shows 4 holders; Willy remains (correctly) in Pengurus as Komisaris Utama.
2. SABH parity: must komisaris/direksi be a shareholder? No.
From the legacy SABH source (ahu-online/protected/modules/sabh), with the receipts:
- SABH stores ONE combined persons array with two independent checkboxes per person —
pemegang sahamanddireksi atau komisaris(views/perseroan/_formPemegangSaham.php:507-526). The only per-person rule: at least one of the two must be checked (_formPemegangSaham.php:77-82). A shareless komisaris/direktur is fully valid. - Conversely, a 0-share shareholder cannot exist: client-side rejects
< 1lembar ("Jumlah saham tidak boleh 0 (nol) lembar",_formPemegangSaham.php:84-93), and server-sideTransaksiPerseroan.countPemegangSaham()(line 6392) only counts a person as a shareholder at all if their share count is > 0. Our new transform guard mirrors exactly this semantics. - Roster-level rules SABH has that we don't (parity gaps, roadmap candidates):
1. ≥ 2 shareholders for an ordinary PT (cekJabatan/validationPemegangSaham.js:124; BUMN/BUMD exempt);
2. the roster must contain at least one Direktur and one Komisaris (validationPemegangSaham.js:164,193);
3. shares must sum exactly to modal ditempatkan — we have this one (SHARES_SUM_CONSISTENCY).
3. PR #2 (dev/2 → refactor/tidyup-foundation): NOT safe to merge — not merged.
The PR already contains the current branch tip, so the merge itself is conflict-free — but the branch is red. Full gate on the PR head (schema synced):
Backend: 20 failures (main tree at the same base: 0), in four clusters:
1. Fitness ceilings burst — document-processor.ts 2580 > 2576, routes/perubahan.ts 1784 > 1763, routes/submissions.ts 2502 > 2500 ("split before growing").
2. POST /api/submissions family (~10 tests) — creations now return 409 "Temp file metadata not found": ownerId stamping (the auth work's own test), selectedJenisPerubahan creation (5), BERAKHIRNYA/PEMBUBARAN create routing, AKTA_PERALIHAN dispatch. A textually-clean merge with semantically conflicting route changes.
3. RUPS attendance registry fallback (3) — the rups-quorum.ts rework broke the registry-roster fallback (BERAKHIRNYA + PEMBUBARAN specs, operator-edit merge-preserve).
4. Parity oracles (4) — perubahan/akuisisi flow- and rematch-parity: the V2 rules changed without the legacy fork halves, so fork-vs-v2 equivalence fails.
Frontend: 12 failures — the FlowStepLayout refactor broke its own test suite (10) plus back-button navigation tests on two extraction pages.
The dev/2 team should fix these on their branch (the failing suites name the exact spots); once both suites are green the merge is mechanically safe — base is already contained. I did not touch their branch.