Proposal to dev_4: one optional ttl field on OrganMember
From: dev_5 (Pendirian Yayasan) · To: dev_4, who owns schema/organ-nirlaba.ts
Date: 2026-08-11 · Status: awaiting a decision
The ask
One optional field on the shared OrganMember type:
export interface OrganMember {
nama: string | null;
jabatan?: string | null;
nik?: string | null;
npwp?: string | null;
paspor?: string | null;
kitas?: string | null;
negara?: string | null;
+ /** Birth date, ISO YYYY-MM-DD. Needed to cross-check the NIK. */
+ tanggal_lahir?: string | null;
}
That is the whole request. Nothing else in your files changes, there is no
migration (the organ lives in rawExtractionJson, not a table), and no behaviour
changes until a rule reads the field.
Why: a NIK verifies its own birth date
A NIK is laid out PPKKCC DDMMYY NNNN — six digits of area code, then the birth
date, then a four-digit serial. For women 40 is added to the day (08 becomes
48), which also means the holder's gender can be read back without storing one.
So a NIK and a stated birth date check each other with no second document
involved. That matters precisely here: D11 dropped every identity rule from the
nirlaba flows on the grounds that they have no anchor document, which is true.
This check does not need an anchor — the deed's own contents are enough.
The evidence, from two real deeds
Run across 13 people in the two Pendirian Yayasan samples (2026-08-11):
| Result | Count | Notes |
|---|---|---|
| Exact match | 11 | including the +40 convention on three women |
| Match, but the NIK has 17 digits | 2 | one stray digit at the tail; one before the date block |
| Disagreement | 1 | the NIK says 8 March 2001, the deed text says 8 May 2001 |
dev5 checked that last one by hand against the PDF: the extraction is right
and the deed itself is wrong. So this is not a theoretical check — on its first
run it found a real document defect that nothing else in the flow would catch.
The two 17-digit NIKs were diagnosed by the same check: because their date block
is still findable, its position shows where the stray digit sits.
Already built, touching nothing you own
backend/src/lib/nik-birthdate.ts on dev/5 — pure, dependency-free, two strings
in and one verdict out:
checkNikBirthdate(nik, birthdate, referenceYear)
// { status: "MATCH", nikBirthdate, gender }
// | { status: "MISMATCH", nikBirthdate, stated, gender }
// | { status: "UNCHECKABLE", reason }
Twelve tests, all green. The design decisions that matter to you:
UNCHECKABLEis first-class and never folded intoMISMATCH. A missing
NIK, a 17-digit one, or a date the model wrote out in words must never reach a
notaris as "these two disagree".referenceYearis a parameter, not the system clock, so the century split
(yy ≤ 26 reads as 2000s) stays deterministic in tests.- Every NIK in the test file is fabricated. The real ones live only in the
gitignored sample golden set. - It sits in
lib/rather than the yayasan rules because it is generic:
PENDIRIAN_PP already holdsfounderNikandfounderTanggalLahirside by side
with no rule relating them, and every KTP-bearing flow has the same pair.
The rule this unlocks
ORGAN_NIK_TTL_MATCH — WARNING, not FAIL.
WARNING because the deed itself can be wrong (now proven) and this flow is
read-only. Failing a document defect a notaris may not be able to fix would just
make overrides routine, and routine overrides stop meaning anything.
SKIPPEDwhen no organ member has both a NIK and a birth date — a foreign
member identified by passport is simply skipped, not treated as a failure.- The message names which member disagrees, with both dates. Without that the
notaris has to guess which row is meant. - Members that come back
UNCHECKABLEare counted separately indetailsrather
than passed over in silence.
dev_5 writes the rule, in rules/nirlaba-shared, so Perkumpulan inherits it
without writing anything — its organ uses the very same OrganMember.
A bonus that needs nobody's approval
ORGAN_NIK_FORMAT — length and numeric shape — needs only nik, which is
already on the type. The asing sample has 2 malformed NIKs out of 8, so the
rule has evidence and could be built at any time without waiting on anyone. It is
unbuilt only because adding a rule beyond the agreed D11 list is a scope call,
not a technical one.
Cost and risk
| Aspect | Assessment |
|---|---|
| Change to dev_4's files | one optional field |
| DB migration | none — the organ lives in rawExtractionJson |
| Impact on Perkumpulan | none until they choose to use it; optional, may stay null forever |
| Impact on PT/PP | none — this type is not used there |
| Regression risk | nothing reads the field until ORGAN_NIK_TTL_MATCH lands |
Decisions needed
- dev_4: happy to add
tanggal_lahir?toOrganMember? If you prefer the
namettl(as the flow doc §4 uses), say so — we will follow. What matters is
one name across both variants. - dev5: should
ORGAN_NIK_TTL_MATCHjoin the set as a seventh rule,
beyond the agreed D11 list? AndORGAN_NIK_FORMATas an eighth? - If both are yes, the Yayasan extraction prompt needs a small addition so each
organ member carries a birth date. The deeds already state it in the penghadap
block ("lahir di Kebumen, pada tanggal 08-07-1974"), so the data is there — it
just has to be asked for.