think
16px
820px

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 reviewPEMEGANG_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 saham and direksi 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 < 1 lembar ("Jumlah saham tidak boleh 0 (nol) lembar", _formPemegangSaham.php:84-93), and server-side TransaksiPerseroan.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 burstdocument-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.