P3D — Birth System B3-A2 Blocker Resolution Design
P3D — Birth System B3-A2 Blocker Resolution Design
Date: 2026-05-12 Mode: DESIGN ONLY — no mutation, no trigger install Source: B3-A readiness rerun report (PARTIAL_ACCEPTED)
A. birth_registry self-birth risk
Problem
birth_registry = bảng sổ khai sinh. Nếu gắn trg_birth_birth_registry → fn_birth_registry_auto → INSERT birth_registry → trigger fires again → infinite recursive loop. PG sẽ hit max trigger recursion depth rồi error, hoặc tệ hơn, chạy n lần trước khi PG kill.
birth_registry hiện có 2 trigger khác (trg_birth_auto_certify, trg_birth_change_flag_matrix) — nhưng KHÔNG có contract trigger fn_birth_registry_auto. Hiện tại an toàn. Nguy hiểm nếu B3-A cắm trigger mù.
3 options
| Option | Description | Pro | Con | Recommendation |
|---|---|---|---|---|
| 1. EXEMPT (SYSTEM-MANAGED REASON) | Mark birth_registry coverage_status='BIRTH_EXEMPT_SYSTEM_LOG_OR_AUDIT' and keep coverage_scope_status within the existing CHECK constraint (normally 'IN_SCOPE' unless live policy design adds a new allowed value). Use coverage_exemption_reason to state SYSTEM_MANAGED semantics. | Simple, no code change, birth_registry is the system itself, compatible with existing B3-P CHECK constraints | Loses birth-of-birth tracking | RECOMMENDED |
| 2. Self-birth guard in function | Add IF TG_TABLE_NAME = 'birth_registry' THEN RETURN NEW; END IF; to fn_birth_registry_auto |
Allows trigger on birth_registry (for uniformity) | Modifies contract function — risky, hardcodes table name | Not recommended |
| 3. pg_trigger_depth() guard | Add IF pg_trigger_depth() > 1 THEN RETURN NEW; END IF; |
Generic, no table name hardcode | Suppresses ALL nested triggers, not just birth_registry recursion. Could mask bugs. | Not recommended |
Recommendation: Option 1 — EXEMPT
birth_registry IS the birth system. It doesn't need to birth itself. Nôm na: sổ khai sinh không cần khai sinh chính nó. Nó TỒN TẠI để ghi khai sinh cho người khác.
Policy storage impact: UPDATE collection_registry SET coverage_status='BIRTH_EXEMPT_SYSTEM_LOG_OR_AUDIT', keep coverage_scope_status within the live allowed set from chk_collection_registry_coverage_scope_status (current allowed values from B3-P DDL: IN_SCOPE, USER_EXCLUDED, FUTURE_SCOPE, ORPHAN_REGISTRY; recommended current value = IN_SCOPE), and set coverage_exemption_reason='SYSTEM_MANAGED: self-referential birth system table — recursive trigger risk' WHERE collection_name='birth_registry'. Do not write literal SYSTEM_MANAGED into coverage_scope_status unless a separate DDL/design first extends the CHECK constraint.
Boundary: birth_registry NEVER gets fn_birth_registry_auto trigger. Existing 2 triggers (auto_certify, change_flag_matrix) stay.
B. Variant function bindings (3 collections)
Situation
| Collection | Trigger | Function | OID |
|---|---|---|---|
| governance_relations | trg_birth_governance_relations | fn_birth_registry_auto_id | 66750 |
| law_dot_enforcement | trg_birth_law_dot_enforcement | fn_birth_registry_auto_id | 66750 |
| law_jurisdiction | trg_birth_law_jurisdiction | fn_birth_registry_auto_id | 66750 |
These tables HAVE birth triggers — they ARE birthing. But through a variant function (fn_birth_registry_auto_id), not the contract function (fn_birth_registry_auto, OID 39232).
Why the variant may exist
The _id suffix may indicate a PK-shape adaptation, but this must not be accepted from naming alone. Before the whitelist becomes executable policy, B3-A2 must run a read-only source/equivalence probe using pg_get_functiondef() for both fn_birth_registry_auto and fn_birth_registry_auto_id, plus sample birth rows from the 3 variant-bound tables. If the variant differs in semantics beyond entity-id extraction, GPT must review again before any validator accepts it as a contract sibling.
3 options
| Option | Description | Pro | Con | Recommendation |
|---|---|---|---|---|
| 1. Migrate to contract | DROP variant trigger + CREATE with contract function | Uniformity | May break if PK column differs. Needs function source investigation. Risk. | Not recommended now |
| 2. Conditional whitelist as sibling | Document fn_birth_registry_auto_id as a candidate contract sibling, but validators accept both functions only after read-only source/equivalence probe PASS + GPT review. | Zero code change if proven equivalent. These 3 tables keep working. Honest about reality. | Two "contract" functions to maintain; unsafe if accepted without source proof | RECOMMENDED, CONDITIONAL |
| 3. Defer | Keep as blocker, don't resolve | No decision | Blocks B3-A PASS forever | Not recommended |
Recommendation: Option 2 — Conditional whitelist as sibling
Recommendation is conditional, not immediately executable: whitelist fn_birth_registry_auto_id only after a read-only equivalence probe proves it is a safe contract sibling for the 3 bound tables. Until that probe is reviewed, B3-A3 must treat the 3 variant-only bindings as accepted-by-design-but-not-yet-validator-green.
Validation impact after probe PASS: B3-A trigger install prompt and onboarding gate health check may accept BOTH function OIDs as valid birth bindings:
contract_function_oids = [fn_birth_registry_auto OID, fn_birth_registry_auto_id OID]
These are discovered live (not hardcoded OIDs), by proname lookup:
SELECT oid FROM pg_proc WHERE proname IN ('fn_birth_registry_auto', 'fn_birth_registry_auto_id');
Future consolidation: If fn_birth_registry_auto is refactored to handle all PK types, the 3 variant triggers can be migrated then. Not urgent.
C. Clean trigger install candidates
After resolving A (birth_registry EXEMPT via allowed policy values) and B (variant whitelist after source/equivalence probe PASS):
From 10 missing-trigger collections:
- birth_registry → EXEMPT (resolved in A)
- 9 remaining = clean candidates for trigger install
9 clean candidates (from readiness rerun, as evidence — Agent must re-verify live before install):
apr_action_types
apr_approvals
apr_request_types
binding_registry
dot_domain_rules
field_type_equivalences
normative_relations
nrm_approval_rules
nrm_doc_type_config
All have: physical table ✅, species mapping ✅, coverage_status=BIRTH_REQUIRED ✅, IN_SCOPE ✅, no existing trigger (contract or variant) ✅.
B3-A3 trigger install for these 9 can proceed after GPT approves this design + resolves A and B.
B3-A3 prompt must:
- Re-verify all conditions live (not use "9" as hardcoded count)
- Accept BOTH contract function OIDs as valid
- Exclude birth_registry (EXEMPT)
- Single transaction + advisory lock
- Verify by function OID binding
- Report exact trigger names for rollback
D. Out-of-scope follow-ups
D1. B3-A-DUP: 18 duplicate trigger pairs
18 collections have 2 triggers both calling fn_birth_registry_auto → double birth_registry rows per INSERT. This is tech debt that may cause data quality issues (duplicate birth records).
Separate workstream. Design:
- Identify which trigger to keep (trg_birth_* convention) and which to drop (birth_trigger_* legacy)
- Verify no other logic differences between pairs
- Single transaction DROP of legacy triggers
- Verify birth counts before/after
D2. B3-DESC: 57 description_policy='unclassified'
Separate semantic classification workstream. Not a coverage_status issue. Not a B3-A blocker.
D3. Phase 5C2 / UI cutover
Still paused. Not in scope.
E. Anti-hardcode + species/layer rules
- Species = meta-class outside 6 composition classes
- New species must be born in entity_species before mapping
- All counts in this design = evidence snapshots from readiness rerun, NOT execution gates
- B3-A3 prompt must re-query live PG
- Contract function OIDs discovered by proname, not hardcoded integers
B3-A2 Blocker Resolution Design | DESIGN ONLY | 2026-05-12