01 — P-pub Birth-Gate Promotion Authority Pack (2026-05-28)
Doc 01 — Branch B: P-pub Birth-Gate Promotion Authority Pack
Verdict: DEFER — warn→block promotion is UNSAFE today. Full authority pack below. IU birth must not break.
1. Live evidence (read-only, 2026-05-28)
1.1 Birth-gate trigger wiring on information_unit
| Trigger | Timing | Function | Note |
|---|---|---|---|
trg_aa_iu_gateway_write_guard |
BEFORE INSERT OR UPDATE | fn_iu_gateway_write_guard |
canonical-writer allowlist |
trg_iu_birth_gate_layer1 |
BEFORE INSERT only | fn_iu_birth_gate_layer1 |
identity/vocab/P-id/P-pub checks |
trg_iu_birth_gate_layer2 |
CONSTRAINT, AFTER INS/UPD, DEFERRABLE INITIALLY DEFERRED | fn_iu_birth_gate_layer2 |
U5/U6 anchors at commit |
trg_birth_information_unit |
AFTER INSERT | fn_birth_registry_auto |
Đ0-G birth registry |
trg_iu_enacted_immut |
BEFORE DELETE OR UPDATE | fn_iu_enacted_immut |
enacted immutability |
1.2 P-pub checks inside fn_iu_birth_gate_layer1 (BEFORE INSERT)
v_val := NEW.identity_profile->>'publication_authority_ref';
IF v_val IS NULL OR v_val = '' THEN
RAISE WARNING 'Birth gate L1 PILOT-ONLY: P-pub1 missing — production sẽ BLOCK'; -- WARN ONLY
END IF;
v_val := NEW.identity_profile->>'publication_type_ref';
IF v_val IS NULL OR v_val = '' THEN
RAISE WARNING 'Birth gate L1 PILOT-ONLY: P-pub2 missing — production sẽ BLOCK'; -- WARN ONLY
ELSE
IF NOT EXISTS (SELECT 1 FROM dot_config WHERE key='vocab.publication_type.'||v_val)
THEN RAISE EXCEPTION 'publication_type "%" not in vocab', v_val; -- HARD if present-but-invalid
END IF;
END IF;
- P-pub1 (
publication_authority_ref): warn-only, no vocab validation even when present. - P-pub2 (
publication_type_ref): warn-only when absent; hard-block when present but not invocab.publication_type.*.
1.3 fn_iu_create signature & body (the root cause)
Signature: (p_canonical_address, p_title, p_body, p_actor, p_unit_kind, p_section_type, p_owner_ref, p_publication_type, p_parent_ref).
- Builds
identity_profile = {title, owner_lookup_ref, primary_section_type_ref}. - Adds
publication_type_refonly ifp_publication_typepassed (validated againstvocab.publication_type.*). - Never sets
publication_authority_ref. → Every IU born viafn_iu_createis missing P-pub1.
1.4 Live data profile (219 IUs)
| Field | Set | Missing |
|---|---|---|
publication_authority_ref |
86 (all = incomex_council) |
133 |
publication_type_ref |
146 (all = law) |
73 |
1.5 Vocabulary present
| Key namespace | Keys | Values |
|---|---|---|
vocab.publication_authority.* |
1 | incomex_council |
vocab.publication_type.* |
2 | law, design_doc |
2. Why promotion is unsafe NOW
fn_iu_createwrites nopublication_authority_ref. Promote P-pub1 warn→block and every new IU birth fails immediately — including every split child created during compose/split/merge. This is a total birth outage.- Vocabulary too thin for honest assignment. Only
incomex_councilexists as an authority. The 133 authority-less IUs are not all council-published (some are design docs). Blind-assigningincomex_councilto all would be a false governance claim, violating Đ31 integrity. - Blast radius is bounded but real.
trg_iu_birth_gate_layer1is BEFORE INSERT only → existing 133/73 rows do not break on UPDATE/retire/supersede. The break is confined to new INSERTs (births + split/merge children). This is what makes a staged path feasible, but does not make an immediate flip safe. - P-pub2 present-but-invalid already hard-blocks. Only the absent case is warn. So a type-vocab expansion must precede any type backfill.
3. Authority pack — required changes (in dependency order)
3.1 fn_iu_create signature change (PREREQUISITE)
Add a publication-authority parameter and write it into identity_profile:
fn_iu_create(..., p_publication_type text, p_publication_authority text DEFAULT NULL, p_parent_ref uuid)
-- validate p_publication_authority against vocab.publication_authority.* when block-stage active
-- v_identity := v_identity || jsonb_build_object('publication_authority_ref', btrim(p_publication_authority))
Plus the split/merge child constructors (fn_iu_piece_split, fn_iu_piece_merge) must propagate publication_authority_ref from the source IU or accept it explicitly.
3.2 Publication-authority vocabulary expansion
vocab.publication_authority.* currently has only incomex_council. Before block, register the full lawful set via APR/Đ32. Candidate set (illustrative, ratify via Council):
incomex_council(exists),incomex_super_admin,department_lead,external_regulator,system_generated,pilot_provisional. Authorities are governance objects — registration must go through Đ32 high, not auto-seed.
3.3 216→219-row backfill plan (now 133 authority-less, 73 type-less)
- Do NOT auto-assign. Build a classification worksheet: for each of the 133 authority-less IUs, derive the lawful authority from its
unit_kind/owner_ref/ source provenance. - Likely mapping:
unit_kind=law_unit+ existingpublication_type_ref=law→incomex_council; design docs → adesign_doc/internal authority. - Backfill is a governed UPDATE batch under a manifest + review_decision (production builder from Branch C), audited, reversible (snapshot prior
identity_profile). - The 73 type-less rows similarly need
publication_type_refset from the (expanded)vocab.publication_type.*before P-pub2 blocks.
3.4 Đ38/Đ39/Đ32 sign-off path
- Đ38 (IU law): P-pub fields are identity attributes of an IU → enforcement change is an Đ38 birth-contract amendment.
- Đ39 (KG): publication_authority feeds provenance of every IU edge (A8) → consistency required.
- Đ32 (approval): vocab expansion + backfill + warn→block flip each require an approval at
highrisk class with sovereign cross-sign.
3.5 Config-gated staged enforcement (RECOMMENDED)
Introduce a dot_config staged enforcement key, e.g. iu_create.ppub.enforcement_mode ∈ {warn, block_new, block_all}:
warn(today): RAISE WARNING only.block_new: hard-block new INSERTs once §3.1 (fn writes authority) + §3.2 (vocab) ship; existing rows untouched (matches BEFORE-INSERT-only trigger).block_all: only after §3.3 backfill completes; add a BEFORE UPDATE guard if full enforcement on mutation is desired. The L1 trigger reads this key and chooses WARN vs EXCEPTION per check. This makes the flip a config flip (reversible) rather than a code redeploy.
3.6 Rollback plan
- Code: keep prior
fn_iu_birth_gate_layer1+fn_iu_createbodies; revert isCREATE OR REPLACEback to baseline (md5-pinned). - Config: set
enforcement_mode=warnto instantly disable blocking. - Backfill: per-row prior
identity_profilesnapshotted → restorable via governed UPDATE.
3.7 Test plan
- In BEGIN..ROLLBACK: set
enforcement_mode=block_new; attemptfn_iu_createwithout authority → must RAISE; with valid authority → must succeed; with invalid authority → must RAISE (vocab). - Prove existing-row UPDATE still succeeds under
block_new(BEFORE INSERT only). - Split/merge child creation carries authority → succeeds.
- Negative: unknown authority value blocked; empty string blocked.
- Verify
fn_iu_gate_verify_closedunaffected; commit nothing in test; separate committed apply only after sign-off.
3.8 Exact condition when warn→block becomes safe
block_new is safe when ALL hold:
fn_iu_create+ split/merge constructors write a validpublication_authority_ref.vocab.publication_authority.*covers every authority the pilot can emit (ratified Đ32).iu_create.ppub.enforcement_modekey exists and L1 honors it.- Test plan §3.7 passes in-transaction.
block_all is additionally safe only when:
5. All 133 authority-less + 73 type-less rows backfilled under governed manifest/review_decision.
6. Production review_decision builder (Branch C) is live (backfill needs a real Đ32 approval).
→ Branch B depends on Branch C for the backfill approval and on a vocab-expansion Council pass. Sequence: vocab expand → fn_iu_create patch → block_new → backfill → block_all.
4. Outcome
- Applied this run: nothing (correctly — flip is unsafe).
- Deliverable: this authority pack + staged enforcement design.
- Next macro:
IU_PPUB_BIRTH_GATE_SAFE_PROMOTION_OR_STAGED_AUTHORITY_*X(doc 06, macro 1).