think
16px
820px

Desain: Kuorum RUPS Ke-1 / Ke-2 (deteksi hybrid AI + konfirmasi notaris)

Status: ✅ Fase 1 IMPLEMENTED (backend penuh + toggle UI 3 flow V2; typecheck & test hijau) · Tanggal: 2026-07-14
Konteks: Ambang kuorum kehadiran RUPS berbeda antara rapat pertama dan kedua (UUPT). Sistem sekarang selalu memakai ambang rapat pertama karena tidak tahu sebuah akta itu RUPS ke-1 atau ke-2. Dokumen ini merancang cara AI mengusulkan tingkat rapat dan notaris mengonfirmasi, lalu ambang menyesuaikan.

Keputusan yang sudah dikunci bersama Efran:
- Bentuk field: angka rups_ke = 1 | 2 (RUPS ke-3 oleh pengadilan → di luar scope).
- Default: kalau notaris belum konfirmasi & AI tak menemukan penanda → RUPS pertama (ambang ketat, konservatif).
- Model: hybrid — AI mengusulkan, notaris mengonfirmasi/override.
- Scope: ketiga kategori kuorum — Perubahan non-AD, Perubahan AD, dan Ps.89 (Penggabungan / Peleburan / Pengambilalihan).
- Peleburan: cara ceknya disamakan dengan flow lain (cek roster AktaPemegangSaham di akta peleburan, ambang ps89, toggle rups_ke per-submission). Ini sesuai perilaku AHU Online yang juga cek kuorum atas satu PT representatif saja — bukan gap (§4.1).


1. Dasar hukum — ambang kehadiran

Hanya kehadiran yang dihitung (persetujuan tidak dihitung, sesuai keputusan sebelumnya).

Kategori Pasal RUPS ke-1 RUPS ke-2
Perubahan data biasa (non-AD) Ps.86 > ½ ≥ ⅓
Perubahan Anggaran Dasar (AD) Ps.88 ≥ ⅔ ≥ ⅗
Penggabungan / Peleburan / Pengambilalihan Ps.89 ≥ ¾ ≥ ⅔

AD = PAD ∪ PPAD. Non-AD = perubahan data saja.


2. Bagaimana AI tahu ini RUPS ke-1 atau ke-2?

RUPS kedua wajib menyebut dirinya rapat kedua dan merujuk rapat pertama yang gagal kuorum (Ps.86(5)-(6), 88(2), 89(2)). Jadi penandanya hampir selalu eksplisit di akta / BA RUPS:

Penanda di dokumen Kesimpulan
"Rapat Kedua" / "RUPS Kedua" / "rapat yang kedua" ke-2
"rapat pertama tidak/belum mencapai kuorum" ke-2
merujuk tanggal + panggilan rapat pertama yang gagal ke-2
kutipan "Pasal 86 ayat (5)/(6)", Ps.88(2), Ps.89(2) ke-2
tidak ada penanda apa pun ke-1 (default)

Kenapa tetap butuh konfirmasi manusia: kadang hanya akta RUPS kedua yang dinotariskan (BA pertama tak diserahkan), atau wording tak baku. AI hanya bikin default cerdas; notaris yang memutuskan.


3. Arsitektur (untuk 3 flow yang sudah punya rule)

Berlaku untuk Perubahan, Penggabungan, Akuisisi — ketiganya sudah menjalankan rupsQuorumRule dengan ctx.rupsQuorum yang berisi roster + kehadiran satu PT.

flowchart LR A[Akta di-OCR] --> B[AI ekstrak:
rups_ke + rups_ke_bukti] B --> C[Submission.rupsKe
default = usulan AI] C --> D[Toggle notaris:
Pertama / Kedua] D --> E[ctx.rupsQuorum.rupsKe] E --> F[rups-quorum.ts:
pilih ambang ke-1 / ke-2
per kategori]

3.1 Ekstraksi (AI mengusulkan)

src/schema/akta-perubahan.ts — tambah di samping jenis_rups / tanggal_rups / kehadiran_rups:
- rups_ke: 1 | 2 | null — usulan AI (null bila ragu)
- rups_ke_bukti: string | null — kutipan frasa penanda (audit + bantu konfirmasi notaris)

Prompt: instruksikan mencari penanda di §2; tak ada penanda → 1.

3.2 Persistensi (nilai terkonfirmasi)

  • rups_ke hasil AI menempel di akta (seperti jenis_rups).
  • Nilai yang dipakai validasi = yang dikonfirmasi notaris → kolom baru Submission.rupsKe Int? (default mengikuti usulan AI). Pola sama seperti changeTypeResult / selectedJenisPerubahan.

3.3 Context

src/flow-engine/context/pt-akta-context.ts (~baris 385) — tambah rupsKe ke ctx.rupsQuorum:
Submission.rupsKe → fallback akta field rups_ke → fallback 1.

3.4 Rule — refactor ke kategori semantik

src/flow-engine/rules/pt-akta/rups-quorum.ts. Masalah: sekarang Penggabungan/Akuisisi memanggil rupsQuorumRule({ quorumThreshold: 3/4 }) — angka "buta" yang tak tahu fallback ke-2 nya ⅔.

Ganti opsi angka jadi kategori semantik, mis.:

rupsQuorumRule({ adAware: true })   // Perubahan: pilih non-AD/AD dari scope, tiap-tiap punya ke-1/ke-2
rupsQuorumRule({ category: "ps89" }) // Penggabungan / Akuisisi / Peleburan

Tabel ambang internal (kategori, rupsKe) → { threshold, comparator }:

Kategori ke-1 ke-2
nonAd > 0.5 >= 1/3
ad >= 2/3 >= 3/5
ps89 >= 3/4 >= 2/3

3.5 UI toggle

Radio "RUPS: ● Pertama ○ Kedua" pre-filled dari rups_ke AI, di panel yang sudah menampilkan kehadiran RUPS. Override → simpan ke Submission.rupsKe lewat endpoint kecil. Tampilkan rups_ke_bukti sebagai hint ("Terdeteksi: '…rapat kedua…'").


4. Peleburan — disamakan dengan flow lain (keputusan Efran)

Keputusan: cara ceknya disamakan dengan flow yang sudah ada, tidak bikin mesin per-sumber. Peleburan diperlakukan sama seperti Penggabungan/Akuisisi — cek satu roster terhadap ambang ps89, dengan toggle rups_ke per-submission.

Konkretnya, samakan dengan pola §3 pada roster AktaPemegangSaham di akta AKTA_PELEBURAN (roster PT baru — satu-satunya roster kehadiran yang tersedia dari ekstraksi):

  1. Aktifkan matcher kehadiran untuk Peleburan — tambah AKTA_PELEBURAN ke filter jenis akta di matchRupsAttendance (src/services/rups-attendance-matcher.ts) dan set rupsAttendance: true di rematch config peleburan-pt.ts. Ini mengisi kehadiranRups di roster akta peleburan, persis seperti Perubahan/Akuisisi.
  2. Tambah rulerupsQuorumRule({ category: "ps89" }) ke PELEBURAN_RULE_SET di peleburan-pt.ts.
  3. Toggle rups_ke — pakai Submission.rupsKe yang sama (per-submission), tampil di UI ekstraksi Peleburan seperti flow lain.

Dengan ini Peleburan masuk Fase 1 — tidak ada fase terpisah.

4.1 Cek single-PT MEMANG cara AHU Online — bukan kompromi (klarifikasi Efran 2026-07-14)

Ternyata pendekatan single-check kita sama persis dengan cara AHU Online. Di sistem asli tidak ada kuorum/RUPS terpisah per PT sumber. Walau secara hukum (Ps.89) tiap PT yang melebur seharusnya punya RUPS sendiri, implementasi AHU hanya mencatat kehadiran RUPS untuk SATU PT — yang dipilih notaris sebagai representatif (perseroan_check):

  1. actionIndex — pilih PT hasil peleburan pakai nama baru atau reuse nama salah satu PT yang melebur.
  2. actionLebur — input daftar 2+ PT (perseroans[] + NPWP), lalu pilih satu sebagai perseroan_check (PT representatif).
  3. actionPersyaratanUtama — tabel "Kehadiran RUPS" muncul, tapi datanya (pemegang saham/direksi/komisaris) hanya dari perseroan_check. PT sumber lain hanya tercatat nama + NPWP, tanpa tabel kehadiran.
  4. actionDetail — isi data lengkap PT hasil peleburan (mirip pendirian).
  5. Submit → createLeburTransaksi() — PT lain langsung UPDATE status_transaksi = 5 (dianggap bubar) satu batch, tanpa approval/kuorum terpisah. Daftar PT melebur disimpan sebagai satu JSON di pivot peleburan_perseroan (metadata, bukan data kuorum).

Konsekuensi desain: cek kuorum single-PT terhadap satu roster = perilaku yang benar, sesuai sistem. Pendekatan "per-sumber" yang sempat dikhawatirkan sebagai gap bukan gap — sistem asli memang tidak melakukannya.

Satu-satunya perbedaan halus: AHU memakai roster registri milik perseroan_check, sedangkan PoC ini memakai roster hasil ekstraksi akta peleburan (AktaPemegangSaham = pemegang saham PT hasil peleburan). Untuk PoC ini roster akta adalah analog terdekat yang tersedia dan sudah memadai; menyelaraskan ke roster perseroan_check = penyempurnaan opsional, bukan keharusan.


5. Fasing

Semua masuk satu fase — keempat flow (Perubahan non-AD/AD, Penggabungan, Akuisisi, Peleburan) memakai cek seragam yang sama. Peleburan cuma butuh dua sambungan tambahan (aktifkan matcher + daftarkan rule).

Cek single-PT untuk Peleburan = perilaku yang sesuai AHU Online (§4.1), bukan penundaan. Penyelarasan roster ke perseroan_check = penyempurnaan opsional.


6. Ringkasan perubahan file (perkiraan)

  • src/schema/akta-perubahan.ts — field + prompt rups_ke / rups_ke_bukti
  • prisma/schema.prismaSubmission.rupsKe Int?
  • src/flow-engine/context/pt-akta-context.ts — isi rupsQuorum.rupsKe
  • src/flow-engine/rules/pt-akta/rups-quorum.ts — kategori semantik (nonAd/ad/ps89) + tabel ambang ke-1/ke-2
  • src/flow-engine/flows/{perubahan,penggabungan,akuisisi}-pt.ts — ganti opsi rule ke kategori semantik
  • src/flow-engine/flows/peleburan-pt.ts — tambah rupsQuorumRule({ category: "ps89" }) + rupsAttendance: true
  • src/services/rups-attendance-matcher.ts — tambah AKTA_PELEBURAN ke filter jenis akta
  • endpoint konfirmasi + komponen toggle RUPS ke-1/ke-2 (dipakai bersama keempat flow)
  • tes unit rule (matriks kategori × ke-1/ke-2) + tes matcher Peleburan

Catatan: dokumen desain, bukan implementasi. Menyusul setelah Efran ACC. Peleburan disamakan dengan flow lain; pendekatan per-sumber ditunda (§4.1).