KB-A5E1

Pre-Birth Admission Control — 02 Permit Model Options

10 min read Revision 1
pre-birth-admissionarchitecture2026-06-03

02 — Pre-Birth Permit / Admission Model — Options & Recommendation

Goal: define the simplest robust rule — "no valid birth/admission permit → no production object insert" — for critical families, reusing existing machinery (doc 01), reversible, owner-gated. Design only; nothing applied.


0. Design constraints (from law + live evidence)

  1. Reuse-first. The codebase already has a permit-ledger pattern (iu_gate_transition + fn_iu_gate_open), a deferred-finalize pattern (IU layer2), and a BEFORE gate (fn_birth_gate). A new model must compose these, not duplicate them.
  2. Decouple from the BORN ledger. birth_registry is the post-fact truth (1.12M rows, all 'born'). Pre-birth reservation is a different lifecycle phase and must not be mixed into it.
  3. Reversible by default. Any new object must be droppable/detachable with a one-line rollback and zero data migration on existing rows.
  4. Fail-closed only where clean. Blocking is permitted only for a family that is backlog-clean and explicitly approved; everywhere else, report-only.
  5. Idempotent + auditable. Mirror Điều 32: approval_id, TTL/expiry, actor, reason, single-use, command log.

Option 1 — Extend birth_registry with reservation states

Add states RESERVED → BORN → FAILED → EXPIRED → RETIRED to the existing status column; a row is inserted at RESERVED before object creation and promoted to BORN on finalize.

Impact assessment (live):

  • 1,121,537 existing rows are all status='born' with no CHECK. Introducing a state machine means either (a) adding a CHECK that must accept the legacy 'born' value forever, or (b) backfilling — touching 1.12M rows. Both raise migration + rollback risk.
  • The UNIQUE(entity_code)-alone defect poisons reservations too: a RESERVED row for (PIV-101, pivot_definitions) collides with an existing (PIV-101, pivot_results) row → reservation silently impossible via the same ON CONFLICT path. The defect must be fixed first and the AFTER trigger (166 tables) reworked to understand RESERVED rows (it currently does ON CONFLICT (entity_code) DO NOTHING — it would now hit RESERVED rows and skip the BORN promotion).
  • fn_birth_registry_auto would need a rewrite on all 166 tables' worth of behavior: instead of "insert BORN if absent," it must "find matching RESERVED permit, promote to BORN, else create/flag." High blast radius.
  • Two semantics in one table. Queries, views (v_birth_orphan, v_birth_phantom), and the 1402 certified rows all assume birth_registry row == a born object. Reservation rows would have to be excluded everywhere — a pervasive, error-prone change.

Verdict: REJECTED for normal operation. Conflates pre-birth and post-birth in the highest-value, highest-volume table; forces a 1.12M-row migration and a 166-table trigger rewrite; the entity_code-alone defect makes reservations themselves collide. Rollback is not a one-liner.


A new, initially-empty table that holds reservations issued before object creation. The existing fn_birth_gate (BEFORE INSERT) is extended to consume a permit; a new DEFERRABLE CONSTRAINT TRIGGER (modeled on IU layer2) finalizes at commit. birth_registry semantics are untouched.

2.1 Proposed shape (design — NOT created)

birth_admission_permit
  permit_id            uuid PK default gen_random_uuid()
  collection_name      text NOT NULL
  entity_code          text NOT NULL            -- the code the object WILL have
  request_hash         text NOT NULL            -- idempotency key (see B-design)
  requested_by_dot     text                     -- DOT that requested it
  requested_by_actor   text NOT NULL            -- human/service principal
  species_code         text                     -- decided BEFORE insert
  composition_level    text
  governance_role      text                     -- decided BEFORE insert (governed/observed/excluded)
  approval_id          uuid                      -- required for governed families (Điều 32 parity)
  status               text NOT NULL default 'RESERVED'
                         CHECK (status IN ('RESERVED','CONSUMED','FINALIZED','FAILED','EXPIRED','REVOKED'))
  expires_at           timestamptz NOT NULL      -- TTL (watchdog expires)
  consumed_at          timestamptz               -- set when the matching insert fires
  finalized_birth_id   bigint                    -- FK→birth_registry.id, set at commit
  failure_reason       text
  created_at           timestamptz default now()
  -- KEYS:
  UNIQUE (collection_name, entity_code) WHERE status IN ('RESERVED','CONSUMED')  -- one live permit per identity
  UNIQUE (request_hash)                                                          -- replay protection

Note the composite (collection_name, entity_code) uniqueness — the permit layer is born free of the entity_code-alone defect. This also surfaces the birth_registry composite-unique fix as an explicit prerequisite (the permit and the BORN row must agree on the same key shape).

2.2 How it plugs into existing machinery (reuse map)

Need Reuse
Issue permit w/ approval + TTL + ledger mirror fn_iu_gate_openiu_gate_transition; new fn_birth_permit_request(...) writes the permit + a command-log row via fn_dot_iu_command_log
BEFORE-insert "permit required" check extend fn_birth_gate: after the 5 shape checks, for an enforced family, require a RESERVED permit matching (TG_TABLE_NAME, code), mark it CONSUMED. No permit → behave per mode (warning/blocking).
Finalize at commit, no partial state new DEFERRABLE INITIALLY DEFERRED CONSTRAINT TRIGGER fn_birth_permit_finalize (pattern = fn_iu_birth_gate_layer2): at commit, assert the consumed permit now has a matching BORN row; set finalized_birth_id, status='FINALIZED'; else RAISE (whole tx rolls back).
Expire stale reservations new fn_birth_permit_watchdog() (pattern = fn_iu_gate_watchdog): RESERVED past expires_atEXPIRED.
Visibility of failed/expired new read-only view v_birth_admission_permit_status (RESERVED/CONSUMED/FINALIZED/EXPIRED/FAILED counts + tail).

2.3 Why Option 2 wins

  • Reversible: DROP TABLE birth_admission_permit + detach the gate extension + drop the constraint trigger = full rollback, zero touch to the 1.12M-row birth_registry.
  • Non-coupling: birth_registry keeps meaning "born objects." Permits are a separate, droppable lifecycle store.
  • Hook already exists: fn_birth_gate is already BEFORE INSERT on the three cleanest families (dot_tools, collection_registry, meta_catalog). We extend, not install.
  • Finalize already proven: the IU layer2 deferred constraint trigger is a live, working precedent for "nothing partial survives commit."
  • Permit ledger already proven: iu_gate_transition shows the exact issue/expire/audit pattern works in this DB under Điều 32.
  • Sidesteps the defect at the permit layer and forces the birth_registry composite-unique fix to be done deliberately (prerequisite), not as a hidden coupling.

2.4 Residual risks

  • Two writes (permit + object) — but the deferred constraint guarantees atomicity at commit (no orphan permit, no permit-less born row for enforced families).
  • Requires the composite-unique fix on birth_registry before the FINALIZED↔BORN assertion is reliable for collision-prone families.
  • Requires registrar creds to issue permits operationally (today ABSENT).

Option 3 — Current AFTER-trigger + stronger scanner only

Keep fn_birth_registry_auto as the creator; add more/scheduled scanners to detect permit-less or unborn objects faster.

Verdict: REJECTED for normal operation; RETAINED for legacy/backfill + emergency. A scanner is, by definition, after-the-fact: it can find a permit-less object only after it already exists. It can never make "no permit → no insert" true (the open-goal law's "automation-first: safety must not depend on remembering to run something later"). It remains the correct tool for (a) the pre-cutoff backlog sweep (doc 31 design), (b) emergency reconciliation, and (c) detecting bypass attempts (doc 07). It is necessary but not sufficient; promoting it to the normal mechanism is the exact anti-pattern this macro exists to end.


Recommendation

Adopt Option 2 (separate birth_admission_permit table), composed from the existing IU layered-trigger + Điều 32 gate-token-ledger patterns, with the BORN-side birth_registry composite-unique fix as a hard prerequisite. Option 1 rejected (couples lifecycles, 1.12M-row migration, defect bleed). Option 3 demoted to legacy/backfill/emergency only.

Pilot family: dot_tools (rationale in doc 04): already has the BEFORE gate, orphan-clean, creation already DOT-driven/governed, controlled volume, and making DOT creation permit-first is maximally on-strategy. Enforcement (blocking) is applied for that family only after the permit table + composite-unique fix land under owner approval — never as a global flip.

Exact risks to accept before any apply: (1) composite-unique migration on a 1.12M-row table (online, but must be rehearsed); (2) the gate extension must preserve the kill-switch + null-code semantics for non-enforced families so nothing else breaks; (3) registrar creds must exist to issue permits or the pilot blocks legitimate creation; (4) the deferred constraint must fail-closed without deadlocking concurrent inserts.

Back to Knowledge Hub knowledge/dev/reports/architecture/pre-birth-admission-control-and-sequential-dot-workflow-2026-06-03/02-pre-birth-permit-model-options.md