P3D Phase 5C2-R0 Resume Plan — TAC → Information Unit Migration — Design (Read-Only Mapping/Dry-Run)
P3D Phase 5C2-R0 Resume Plan — TAC → Information Unit Migration — Design
Date: 2026-05-14 Author: Claude Opus 4.7 (1M, xhigh) Mode: DESIGN ONLY / READ-ONLY MAPPING & DRY-RUN ONLY Status: DISPATCH-CANDIDATE for GPT/Opus/User review — NOT YET AUTHORIZED FOR EXECUTION Supersedes: rev4 hybrid pilot migration prompt as the active artifact for next gate (rev4 is now reference pattern only) Controlling prompt:
knowledge/dev/laws/dieu44-trien-khai/prompts/agent-phase5c2-r0-resume-plan-design-only-2026-05-14.mdrev 1 Authorizing reviews: Opus accept (reviews/opus-review-agent-readonly-investigation-iu-current-position-2026-05-14.md) + GPT dispatch (reviews/gpt-review-agent-readonly-investigation-iu-current-position-accept-dispatch-5c2-r0-2026-05-14.md)
0. Hard boundaries (governing this design + future R0 execution artifact)
NO execution.
NO DB write.
NO DDL.
NO DML (INSERT/UPDATE/DELETE/TRUNCATE/MERGE).
NO mutating function execution (no live fn_iu_create / fn_iu_save / fn_iu_apply_edit_draft).
NO TAC writes.
NO IU migration rows.
NO bulk migration.
NO UI cutover (Nuxt Laws Page continues reading TAC; no route change).
NO Directus permission / role / config change.
NO schema mutation.
NO trigger / function patch.
NO birth-system change.
NO Qdrant / vector mutation, no reindex.
NO outbox worker / event emission creation.
NO old rev4 prompt execution.
NO rollback execution.
If any check would require mutation → mark BLOCKED_BY_NO_MUTATION_BOUNDARY and continue.
1. Position reconciliation (verified live 2026-05-14)
| Fact | Source |
|---|---|
| Birth System COMPLETE live | Agent readonly-investigation report §2.A + GPT/Opus accept reviews |
birth_registry: 285,965 rows, 0 NULL jsonb_profile, 3 triggers (Rev3 I-6) |
live PG |
fn_birth_onboarding_full_scan_hc + DOT-BIRTH-ONBOARD-FULLSCAN-HC active |
live PG |
fn_birth_registry_auto md5 1f729b3571a74963089bb3ef388217f3 (unchanged) |
live PG |
DOT-119 v2 md5 5883bce405b86ab436e885cf16fd22de (no-clobber) |
live file |
description_policy: 0 NULL; information_unit + unit_version = structured_exempt |
live PG |
identity_profile ON information_unit YES / OFF birth_registry (Rev3 §3 placement) |
live PG |
Pack 22 native create + gateway: 11 IU functions present; trg_aa_iu_gateway_write_guard + trg_aa_uv_gateway_write_guard enforced; iu_create.gateway.mode=enforced |
live PG |
Pack 23 minimum edit workflow: unit_edit_draft/unit_edit_comment live; iu_edit.policy.default_mode=require_review |
live PG |
| Pack 2B P1 PASS 2026-05-05 (1 pilot row retained) | KB report + live PG |
TAC live: 3 publications (DIEU-28 v2.0, DIEU-32 v1.1, DIEU-35 v5.2); DIEU-35 = 36 members, render_order 0..35, 12 distinct section_types |
live PG |
IU/UV target: 12 IU rows / 19 UV rows (all pilot.* / test/* addresses; no TAC-mapped rows) |
live PG |
Vector: 1 Qdrant collection production_documents (6,084 points, green); IU vector collection NOT yet |
live |
vector_efficiency_alert = NONE |
hardening report 2026-05-11 + live |
phase5c2_resume_allowed=true, phase5c2_execution_allowed=false, bulk_migration_allowed=false, ui_cutover_allowed=false.
2. Live TAC source state (DIEU-35 focus)
2.1 Schema of source tables
tac_publication (3 rows): id uuid PK, doc_code text NN, version text NN, publication_type text, name text NN, owner text NN, description text, lifecycle_status text NN, enacted_at tstz, council_score, approved_by, risk_tier, publication_profile jsonb, created_at NN, updated_at NN
tac_logical_unit (86 rows): id uuid PK, canonical_address text NN UNIQUE, doc_code text NN, parent_id uuid NULLABLE (self-ref), sort_order integer NN, section_type text NN, section_code text, owner text NN, identity_profile jsonb NN, tier text, lifecycle_status text NN, created_at NN, updated_at NN
tac_unit_version (86 rows): id uuid PK, logical_unit_id uuid NN FK→tac_logical_unit, version_number integer NN, title text NN, body text NULLABLE, description text, content_hash text NULLABLE, lifecycle_status text NN, review_state text NN, length_flag text NN, length_exception_reason text, content_profile jsonb NN, editor text, provenance text NN, vector_sync_status text NN, vector_synced_at tstz, vector_chunk_count int NN, created_at NN, updated_at NN, enacted_at tstz
tac_publication_member (86 rows): id uuid PK, publication_id uuid NN FK→tac_publication, logical_unit_id uuid NN FK→tac_logical_unit, unit_version_id uuid NN FK→tac_unit_version, render_order integer NN, created_at NN
2.2 DIEU-35 live profile
| Probe | Value |
|---|---|
| Publication row | doc_code=DIEU-35, version=v5.2, lifecycle_status=proposed |
| Members | 36 (matches handoff baseline; reference only — not a gate) |
render_order |
min=0, max=35, distinct=36, count=36 (contiguous 0..35) |
owner NULL count |
0 (all 36 members have non-null owner on tac_logical_unit) |
parent_id NULL |
1 (root: D38-DIEU35-ROOT) ; 35 non-root with hierarchy |
| Versions per logical unit | 1 (sample: first 5 logical units = 1 UV each) |
| section_type distribution (12 distinct) | governance_process=12, technical_spec=8, paragraph=5, checklist=2, process=2, appendix=1, article=1, changelog=1, definition=1, heading=1, instruction_block=1, principle=1 |
| Sample canonical_addresses | D38-DIEU35-ROOT, D38-DIEU35-S0, D38-DIEU35-S1, D38-DIEU35-S2, D38-DIEU35-S3, D38-DIEU35-S4 |
| Collision with existing IU canonical_address | 0 |
2.3 Join path (FK-derived; rev4 §5D)
tac_publication.id ─FK← tac_publication_member.publication_id
tac_publication_member.logical_unit_id ─FK→ tac_logical_unit.id
tac_publication_member.unit_version_id ─FK→ tac_unit_version.id
tac_unit_version.logical_unit_id ─FK→ tac_logical_unit.id
tac_logical_unit.parent_id ─self-FK→ tac_logical_unit.id (nullable)
A 4-table join keyed by publication_id filtered by doc_code='DIEU-35' yields all 36 members with their LU + UV in one go.
3. Live IU target state
3.1 Schema (information_unit)
19 columns: id uuid PK NN, canonical_address text NN, unit_kind text NN, lifecycle_status text NN, content_anchor_ref text NULLABLE, version_anchor_ref uuid NULLABLE, owner_ref text NN, parent_or_container_ref uuid NULLABLE, conformance_status text NN, identity_profile jsonb NN, created_at NN, updated_at NN, created_by text NN, updated_by text NN, deleted_at NULLABLE, sort_order integer NULLABLE, doc_code text NULLABLE, section_type text NULLABLE, section_code text NULLABLE.
Critical update vs canonical-contract design §E (2026-05-10): the IU table already contains unit_kind, sort_order, section_type, section_code, doc_code, and conformance_status columns. The "ADD" actions previously listed in §E (unit_kind / sort_order / section_type) no longer apply — Phase 2 DDL of the EVOLVE plan has been silently completed. The migration mapping below works against the current live schema, not the older "must-add" list.
3.2 Schema (unit_version)
16 columns: id uuid PK NN, unit_id uuid NN, body text NN, content_hash text NN, version_seq integer NN, lifecycle_status text NN, content_profile jsonb NN, created_at NN, created_by text NN, title text NULLABLE, description text NULLABLE, review_state text NULLABLE, provenance text NULLABLE, editor text NULLABLE, enacted_at tstz NULLABLE, updated_at NN.
3.3 Current row inventory (12 IU / 19 UV)
| canonical_address | unit_kind | lifecycle_status | origin |
|---|---|---|---|
pilot.iu0.test-001 |
design_doc_section |
draft |
Pack 2B P1 |
pilot.p2.20260506-045033.e0ae7ec5 |
design_doc_section |
draft |
Pack 22 P2 |
pilot.p3.p1.20260506-070623.cbb29036 |
design_doc_section |
draft |
Pack 22 P3-P1 |
pilot.p3.p2.20260506-090954.756b19b8 |
design_doc_section |
draft |
Pack 22 P3-P2 |
pilot.p3.p2.20260506-092734.32ffb654 |
design_doc_section |
draft |
Pack 22 P3-P2 |
test/p3a/gateway-verify-20260507-040349 |
design_doc_section |
draft |
Pack 23 P3A |
test/p3c3/pilot-20260507-121549 |
design_doc_section |
draft |
Pack 23 P3C3 |
test/p3c3/pilot-20260507-121642 |
design_doc_section |
draft |
Pack 23 P3C3 |
test/p3c3/pilot-20260507-121717 |
design_doc_section |
draft |
Pack 23 P3C3 |
test/p3c4/pilot-20260507-125720 |
design_doc_section |
draft |
Pack 23 P3C4 |
test/p3d1/pilot-20260508-005109 |
design_doc_section |
draft |
Pack 23 P3D1 |
test/p3d2/pilot-20260508-015552 |
design_doc_section |
draft |
Pack 23 P3D2 |
All 12 rows use unit_kind='design_doc_section' and pilot.*/test/* namespaces. None use unit_kind='law_unit' and none collide with DIEU-35 source addresses (D38-DIEU35-*).
3.4 Functions live (signatures)
| Function | Signature | Returns |
|---|---|---|
fn_iu_create |
(p_canonical_address text, p_title text, p_body text, p_actor text, p_unit_kind text, p_section_type text, p_owner_ref text, p_publication_type text, p_parent_ref uuid) |
jsonb |
fn_iu_create_plan |
same 9-arg | jsonb |
fn_iu_verify_invariants |
(p_addr text) |
jsonb |
fn_iu_save |
(p_address text, p_body text, p_actor text, p_title text, p_reason text, p_mode text) |
(see Pack 23 P3C4) |
fn_content_hash |
(p_body text) |
text |
fn_iu_gateway_write_guard() |
no args | trigger |
fn_birth_registry_auto() |
no args | trigger |
fn_iu_create is the canonical writer named in dot_config.iu_create.gateway.canonical_function.
3.5 Gateway + policy
iu_create.gateway.mode = enforcediu_create.gateway.canonical_function = public.fn_iu_create(text,text,text,text,text,text,text,text,uuid)iu_create.gateway.allowed_marker_values = fn_iu_create,fn_iu_apply_edit_draftiu_create.gateway.direct_insert_policy = block_after_guardiu_create.gateway.marker_key = app.canonical_writertrg_aa_iu_gateway_write_guardBEFORE INSERT OR UPDATE oninformation_unit→fn_iu_gateway_write_guardtrg_aa_uv_gateway_write_guardBEFORE INSERT OR UPDATE onunit_version→fn_iu_gateway_write_guardiu_edit.policy.default_mode = require_review
3.6 Birth coverage
trg_birth_information_unit AFTER INSERT ON public.information_unit FOR EACH ROW EXECUTE FUNCTION fn_birth_registry_auto('__birth_synthetic_id__')— exact 18c definition.unit_versionhas no independent birth trigger.species_collection_map:information_unit_atom | information_unit | is_primary=true.birth_registryforcollection_name='information_unit': 12 rows, allspecies_code='information_unit_atom', allcomposition_level='atom', 0 NULL species.
3.7 Vocab seeds (dot_config)
All 5C2 vocab keys are present live:
vocab.publication_authority.incomex_council = incomex_council✓vocab.publication_type.law = law✓ (was a known gap on 2026-05-05; now seeded)vocab.unit_kind.law_unit = law_unit✓ (was a known gap on 2026-05-05; now seeded)vocab.section_type.*covers all 13 types present in TAC:appendix, article, changelog, checklist, definition, governance_process, heading, instruction_block, paragraph, principle, process, section, technical_spec— i.e. every distinctsection_typevalue used by DIEU-35 members already has a vocab seed (12/12 covered).
4. Rev4 revalidation matrix
| # | rev4 assumption (2026-05-12) | Live truth (2026-05-14) | Status | Required patch for 5C2-R0 |
|---|---|---|---|---|
| R-1 | rev4 is a MIGRATION WRITE prompt: mode = MIGRATION WRITE — parallel IU pilot |
Phase 5C2-R0 must be READ-ONLY mapping/dry-run only; no writes | DIFFERS | R0 inverts mode: read-only mapping artifact + SAVEPOINT-only dry-run (no COMMIT). Drop §9 Migration Transaction; keep §5–§8 + §11 rollback design as reference for future R1 |
| R-2 | rev4 §0 TAC→UI preservation block | Still authoritative; TAC remains canonical UI source during pilot per handoff §2.1 | UNCHANGED | Carry forward verbatim |
| R-3 | rev4 §2 multidimensionality guard (one coordinate species=information_unit_atom, lớp=atom, no global 6-lớp logic) |
Confirmed by terminology-and-multidimensional-entity-db-principle-2026-05-12.md and live species_collection_map |
UNCHANGED | Keep |
| R-4 | rev4 §4 G0-3..5: fn_iu_create, fn_content_hash, fn_iu_verify_invariants exist |
All present live with signatures shown in §3.4 | CONFIRMED | None |
| R-5 | rev4 §6 G0-10: species_collection_map: ≥1 is_primary=true for 'information_unit' |
Live: 1 row (information_unit_atom, information_unit, true) |
CONFIRMED | None |
| R-6 | rev4 §6 G0-11: 0 NULL species in birth_registry for information_unit |
Live: 12 IU births, 0 NULL species | CONFIRMED | None |
| R-7 | rev4 §6 G0-12..14: vocab seeds for publication_authority.incomex_council, publication_type.law, unit_kind.law_unit |
All 3 seeded live (§3.7) | CONFIRMED | None |
| R-8 | rev4 predates Birth Rev3 (ELD columns canonical_address, owner, jsonb_profile; identity_profile per-entity-kind) |
Birth Rev3 RATIFIED; birth_registry has the 3 ELD columns + jsonb_profile NOT NULL invariant |
DIFFERS / NON-BLOCKING | Add explicit acknowledgement that IU rows produced by fn_iu_create write identity_profile on information_unit, not on birth_registry. The birth trigger leaves canonical_address=NULL, owner=NULL, jsonb_profile='{}' per Rev3 PLACEHOLDER_AT_BIRTH contract; enrichment is a separate workstream and is NOT part of 5C2 |
| R-9 | rev4 predates Pack 22 gateway enforcement (BEFORE INSERT/UPDATE on IU + UV) | Gateway ENFORCED live; dot_config.iu_create.gateway.mode=enforced, 10 policy keys |
DIFFERS / NON-BLOCKING | R0 mapping must go through fn_iu_create canonical writer (already the rev4 plan); explicitly forbid direct INSERT INTO information_unit/unit_version in any future R1 execution artifact |
| R-10 | rev4 predates Pack 23 iu_edit.policy.default_mode=require_review flip |
Live: require_review |
DIFFERS / NON-BLOCKING | Migration creates v1 official UV via fn_iu_create (whose result status=created is a one-shot canonical create, not an edit). For any subsequent correction to a migrated row, the AI path is fn_iu_save which under require_review will produce drafts. R1 execution plan must declare it won't depend on auto-apply behavior |
| R-11 | rev4 predates live 12-row IU pilot namespace (pilot.*, test/*) |
12 pilot/test rows exist with unit_kind=design_doc_section |
DIFFERS / NON-BLOCKING | Collision check is live-zero (§2.2 last row). 5C2 will create rows in namespace D38-DIEU35-* (TAC source addresses) → no name collision. PROHIBIT touching/deleting pilot rows |
| R-12 | rev4 §5E fn_iu_create shape discovery via SAVEPOINT test |
fn_iu_create already returns jsonb per pg_get_function_result — no SAVEPOINT test needed |
SIMPLIFIES | R0 drops SAVEPOINT shape probe; uses static contract from §3.4 |
| R-13 | rev4 §3 LOG_FILE=<logging_convention_path>/p3d-pack1-phase5c2-dieu35-<ts>.log, <logging_convention_path>=/opt/incomex/logs |
Unverified by R0 (no FS check needed for a design); preserved for R1 | UNVERIFIED | Future R1 must verify or report LOGGING_DIR_UNKNOWN |
| R-14 | rev4 §11 post-commit ROLLBACK uses exact captured IDs only, "pattern matching PROHIBITED" | Live IU is pilot.*/test/* namespace, so any future migration namespace collision is impossible — exact-key rollback remains the only safe strategy |
UNCHANGED | R0 mandates exact-key rollback design only; rev4 §11 carries forward |
| R-15 | rev4 §13 Entity Living DB hooks (relations, dependencies, history, labels, metrics post-migration) | Aligned with Đ44 multidimensional principle §3 | UNCHANGED | R0 explicitly documents which JSONB keys will carry these hooks (§5.4 below) |
| R-16 | rev4 §10B render fidelity vs live TAC during run (not baseline snapshot) | Compatible with R0 — R0 mapping uses live TAC at run time, not the 36 baseline (which remains reference-only) |
UNCHANGED | Keep |
| R-17 | rev4 baseline_member_count_reference=36 as REFERENCE ONLY | Live D35 count is 36; matches | CONFIRMED | None |
| R-18 | rev4 §12 hard boundaries: no DDL, no TAC writes, no tac_publication_member writes, no UI cutover |
Same hard boundary set | UNCHANGED | Carry forward verbatim |
| R-19 | rev4 §0 parallel_iu_pilot_only keeps Nuxt unchanged |
Live Nuxt reads TAC via /knowledge/laws CTE (per TAC-UI baseline review PASS 2026-05-12) | UNCHANGED | Keep |
| R-20 | rev4 assumption: tac_logical_unit.owner IS NULL → ABORT |
Live D35: 0 NULL owners | CONFIRMED | None |
Net effect: rev4 remains conceptually sound but is a WRITE prompt. R0 inverts to read-only mapping/dry-run, drops the SAVEPOINT shape probe (statically resolved), and explicitly integrates Birth Rev3 + Pack 22 gateway + Pack 23 require_review. No assumption blocks R0.
5. TAC → IU mapping design (R0 read-only)
5.1 Source query (single 4-table join, parameterised by doc_code)
-- READ-ONLY. No CTAS, no temp table, no INSERT. Result is the dry-run mapping payload.
SELECT
pm.render_order AS src_render_order,
p.id AS src_publication_id,
p.doc_code AS src_doc_code,
p.version AS src_publication_version,
p.publication_type AS src_publication_type,
lu.id AS src_logical_unit_id,
lu.canonical_address AS src_canonical_address,
lu.parent_id AS src_parent_id,
lu.sort_order AS src_sort_order,
lu.section_type AS src_section_type,
lu.section_code AS src_section_code,
lu.owner AS src_owner,
lu.lifecycle_status AS src_lu_lifecycle,
lu.identity_profile AS src_lu_identity_profile,
uv.id AS src_unit_version_id,
uv.version_number AS src_version_number,
uv.title AS src_title,
uv.body AS src_body,
uv.content_hash AS src_content_hash,
uv.content_profile AS src_content_profile,
uv.review_state AS src_review_state,
uv.lifecycle_status AS src_uv_lifecycle
FROM tac_publication p
JOIN tac_publication_member pm ON pm.publication_id = p.id
JOIN tac_logical_unit lu ON lu.id = pm.logical_unit_id
JOIN tac_unit_version uv ON uv.id = pm.unit_version_id
WHERE p.doc_code = 'DIEU-35'
ORDER BY pm.render_order;
5.2 Per-row fn_iu_create call template (dry-run via fn_iu_create_plan ONLY in R0)
R0 must only construct the call payload; R1 will dispatch fn_iu_create_plan then fn_iu_create inside a transaction. R0 emits the call string as a deterministic payload to compare to the source row.
-- READ-ONLY DRY-RUN. R0 ARTIFACT EMITS THIS AS TEXT, DOES NOT EXECUTE.
SELECT public.fn_iu_create_plan(
p_canonical_address := <src_canonical_address>,
p_title := <src_title>,
p_body := <src_body>,
p_actor := 'agent:p3d-phase5c2-r0',
p_unit_kind := 'law_unit', -- vocab seeded
p_section_type := <src_section_type>, -- vocab seeded (all 12 types)
p_owner_ref := <src_owner>, -- NN on source; 0 NULL in D35
p_publication_type := 'law', -- vocab seeded
p_parent_ref := NULL -- D3a hybrid: hierarchy is JSON-only at R0
);
Notes:
p_unit_kindandp_publication_typeare vocab-validated byfn_iu_create(already confirmed in Pack 22 README).vocab.unit_kind.law_unitandvocab.publication_type.lawboth seeded live.p_section_typeflows through 12 distinct values; all 12 are seeded undervocab.section_type.*.p_parent_refis NULL at thefn_iu_createboundary. The TAC hierarchy (35 of 36 rows haveparent_id) is recorded inidentity_profileJSONB instead (D3a hybrid carrier), avoiding the need for a 2-pass insert with parent UUID lookup.
5.3 D3a hybrid identity_profile JSON shape (post-create UPDATE in R1, R0 just designs)
After fn_iu_create returns, R1 will issue one UPDATE information_unit SET identity_profile = identity_profile || $patch per row (the canonical writer marker is set inside fn_iu_create; this UPDATE in R1 fires the gateway, so the marker must be re-set — see §5.6). R0 designs the patch shape; nothing is written.
Required keys (10):
{
"tac_provenance": {
"src_publication_id": "<uuid>",
"src_publication_doc_code": "DIEU-35",
"src_publication_version": "v5.2",
"src_publication_type": "<text>",
"src_logical_unit_id": "<uuid>",
"src_unit_version_id": "<uuid>",
"src_version_number": <int>,
"src_render_order": <int>,
"src_sort_order": <int>,
"src_section_code": "<text|null>",
"src_review_state": "<text>"
},
"tac_hierarchy": {
"src_parent_id": "<uuid|null>",
"src_parent_canonical_address": "<text|null>",
"depth_from_root": <int>
},
"publication_authority_ref": "incomex_council",
"publication_authority_vocab_key": "vocab.publication_authority.incomex_council",
"rendering": {
"render_order": <int>,
"section_type": "<text>"
}
}
Merge semantics: UPDATE … SET identity_profile = COALESCE(identity_profile,'{}'::jsonb) || $patch (idempotent — re-running R1 on the same row yields the same value, an R0 requirement).
5.4 Per-row UV provenance patch (R1 design; R0 only specifies)
After fn_iu_create writes UV v1, R1 patches unit_version to record the original TAC UV content_hash + provenance text:
{
"source_kind": "tac_unit_version",
"src_unit_version_id": "<uuid>",
"src_version_number": <int>,
"src_content_hash": "<sha256>",
"src_provenance": "<text-from-tac_unit_version.provenance>",
"migrated_by": "agent:p3d-phase5c2-r0",
"migrated_at": "<utc>"
}
Stored on unit_version.content_profile. Plain-text unit_version.provenance is set to tac:DIEU-35:<src_unit_version_id>.
5.5 Collision / coexistence policy
- TAC source
canonical_addressnamespace isD38-DIEU35-*. IU current namespace ispilot.*/test/*. Live collision count = 0 (verified). The mapping uses TAC's address verbatim (the EVOLVE design intent of P38-XC §1.2 / IU-0 §2.1). - The 12 existing pilot/test IU rows MUST NOT be touched (no UPDATE/DELETE). They are pre-existing pilot state.
- If R1 ever observes a collision (e.g. a re-run scenario),
fn_iu_createreturnsstatus='exists_complete'(idempotent) or one of theexists_missing_*health states — R1 will report and abort the transaction. Pattern-matching deletion is PROHIBITED.
5.6 Gateway interaction (Pack 22)
fn_iu_createis the canonical writer and already setsapp.canonical_writer = 'fn_iu_create'internally; the gateway trigger allows the resulting INSERTs intoinformation_unit/unit_version.- The subsequent
UPDATE information_unit SET identity_profile = identity_profile || $patch(§5.3) hits the gateway BEFORE-UPDATE trigger. R1 must setSET LOCAL "app.canonical_writer" = 'fn_iu_apply_edit_draft'(an allowed marker periu_create.gateway.allowed_marker_values=fn_iu_create,fn_iu_apply_edit_draft) for that one UPDATE. R0 documents this; R1 must compile and log the exactSET LOCALstatement. - Direct
INSERT INTO information_unit/INSERT INTO unit_versionis forbidden anywhere in R0 or R1.
5.7 Birth integration (Rev3 contract)
fn_iu_createcauses onebirth_registryrow per IU viatrg_birth_information_unit→fn_birth_registry_auto('__birth_synthetic_id__').unit_versionis subordinate → 0 birth rows for UV.- Per Rev3, the birth row has
canonical_address=NULL,owner=NULL,jsonb_profile='{}'::jsonbat insert (PLACEHOLDER_AT_BIRTH / REQUIRED_AT_BIRTH container). 5C2-R1 will NOT enrich these — that is a separate DEFERRED_ENRICHMENT workstream. species_codedefaults to'information_unit_atom'viaspecies_collection_map(verified live).
5.8 Pack 23 require_review integration
- The migration uses
fn_iu_create(one-shot canonical create withstatus='created'), notfn_iu_save(which under require_review returns drafts). Therefore migrating a row does not produce a draft. - Post-migration corrections via the AI path will go through
fn_iu_save→fn_iu_create_edit_draft(require_review) → human reviewer →fn_iu_apply_edit_draft. This is by design and orthogonal to R1. - Pack 23
iu_edit.policy.default_mode=require_reviewMUST NOT be touched by R0 or R1.
6. Migration options
6.1 Option R0 — Read-only mapping / dry-run (RECOMMENDED)
| Aspect | R0 |
|---|---|
| Scope | Generate the R0 mapping artifact: live source query result + per-row fn_iu_create_plan payload + planned identity_profile/content_profile patches; verify zero collisions; verify all vocab seeds; classify each of 36 rows. |
| Writes | NONE. No SQL execution against information_unit/unit_version/birth_registry/tac_*/dot_config. |
| Dry-run depth | (a) Read-only source query — executed. (b) fn_iu_create_plan calls — NOT executed in R0 because even _plan is allowed-marker-checked and reads dot_config; safer to only construct the call strings and submit the artifact for review. (c) Hash comparison via fn_content_hash('<src_body>') — could be executed read-only on a SAVEPOINT-protected SELECT, but cleaner to defer to R1. |
| UI impact | NONE. TAC pipeline unchanged; Nuxt unchanged. |
| Birth impact | NONE. No new birth rows. |
| Hardcode risk | NONE — every assumption is live-introspected per §2/§3. |
| Scale risk | NONE — read-only on 86 rows. |
| Rollback concept | N/A (no writes). |
| Validation | KB report verifies every column mapping; GPT/Opus/User review the artifact. |
| Required approval | GPT review of this design → Opus review → User GO. |
6.2 Option R1 — DIEU-35 pilot execution (future, after R0 review)
| Aspect | R1 |
|---|---|
| Scope | Execute the mapping in a single BEGIN/COMMIT block: per-row fn_iu_create then identity_profile/content_profile patches; assert 36 IU + 36 UV + 36 birth (+pre-existing 12 IU); render fidelity 0 drift. |
| Writes | Bounded — 36 IU rows, 36 UV rows, 36 birth rows. No DDL. No TAC writes. No vocab additions (already seeded). |
| Risks | (a) Gateway marker mismatch → rejection on UPDATE. (b) Source row anomaly (NULL body, missing UV) → ABORT. (c) Address collision → IDEMPOTENT exists path. |
| Validation (in-tx) | D1 captured count = source count. D2 render_order contiguous 0..35. D3 publication_authority_ref='incomex_council' on all. D4 birth: 36 rows, 0 NULL species, all species='information_unit_atom', composition='atom'. D5 content_hash match. D6 fn_iu_verify_invariants PASS per row. D7 TAC source counts unchanged. D8 rollback keys captured to KB + VPS log. |
| Rollback concept | Exact captured IDs only — birth_registry rows by entity_code IN ANY($captured_iu_entity_codes), then unit_version by id = ANY($captured_uv_ids), then information_unit by id = ANY($captured_iu_ids). PATTERN-MATCHING DELETION FORBIDDEN. |
| UI impact | NONE (TAC remains canonical UI source). |
| Birth impact | +36 birth rows under existing Rev3 contract. |
| Hardcode risk | NONE if R0 artifact is the source of truth. |
| Scale risk | LOW — 36-row pilot. |
| Required approval | GPT/Opus/User review of R0 artifact → R1 prompt drafted referencing R0 → GPT final review → User GO. |
6.3 Option R2 — Controlled batch (DIEU-28, DIEU-32) (future, after R1 PASS)
| Aspect | R2 |
|---|---|
| Scope | Apply the same R1 pattern to DIEU-28 (27 members) and DIEU-32 (23 members) sequentially; one publication per transaction. |
| Writes | Bounded — 27 + 23 = 50 IU + 50 UV + 50 birth across two transactions. |
| Risks | Same as R1 plus risk of cross-publication address collisions; live check needed. |
| Required approval | Separate GPT/User GO per publication. |
6.4 UI cutover — out-of-scope future pack
| Aspect | UI cutover |
|---|---|
| Scope | Switch /knowledge/laws Nuxt reader from tac_* to information_unit/unit_version. |
| Status | Explicitly forbidden by GPT review §"Hard boundaries"; requires its own design pack after R2 PASS. |
| Required approval | Separate design pack + GPT/User review; not authorized by this plan. |
6.5 Recommendation
5C2-R0 (read-only mapping / dry-run). Reasons:
- The mapping itself is a non-trivial design surface (10-key
identity_profilepatch, gateway marker discipline on UPDATE, exact-key rollback strategy). Reviewing it before any write is the cheapest possible safety gate. - Live schema diverges from rev4 + canonical-contract assumptions in non-blocking ways (unit_kind/sort_order/section_type already on
information_unit). A read-only mapping is the clean way to demonstrate this to GPT/Opus/User without committing to an execution semantics. - R0 + GPT/Opus/User review costs little; R1 without R0 review would require speculative execution under hard-boundary risk.
→ recommended_option = 5C2-R0_READONLY_MAPPING_DRYRUN.
7. Validation criteria (designed for R1 execution; R0 has no execution)
For the future R1 execution artifact, the validation gates must include all of the following (live, in-transaction, before COMMIT):
| ID | Gate | Method |
|---|---|---|
| V-1 | Row accounting: captured_iu_count = source_count_live |
SELECT array_length($captured_iu_ids,1); SELECT count(*) FROM tac_publication_member pm JOIN tac_publication p ON p.id=pm.publication_id WHERE p.doc_code='DIEU-35' |
| V-2 | Render fidelity: every captured IU has identity_profile->'rendering'->>'render_order' integer equal to its source render_order; the multiset 0..35 is preserved |
SELECT (identity_profile->'rendering'->>'render_order')::int FROM information_unit WHERE id = ANY($captured_iu_ids) ORDER BY 1 |
| V-3 | Content hash fidelity: every captured UV content_hash equals fn_content_hash(uv.body) and equals the source tac_unit_version.content_hash when the source non-null |
UV-by-UV SELECT comparison |
| V-4 | Authority on all: identity_profile->>'publication_authority_ref' = 'incomex_council' for all 36; 0 mismatches |
filter scan |
| V-5 | Birth coverage: 36 birth rows added; 0 NULL species_code; all species_code='information_unit_atom'; all composition_level='atom' |
birth_registry filtered by entity_code IN |
| V-6 | TAC source untouched: pre/post-tx counts for the 4 TAC tables identical | snapshot counts captured in §9B |
| V-7 | fn_iu_verify_invariants PASS for every captured canonical_address |
per-row JSONB result.all_pass=true |
| V-8 | Gateway integrity: trg_aa_iu_gateway_write_guard and trg_aa_uv_gateway_write_guard still attached post-COMMIT; no marker leakage outside the transaction |
pg_trigger introspection + SHOW app.canonical_writer should be empty in a fresh session |
| V-9 | Pre-existing 12 pilot/test IU rows untouched | id-list comparison before/after |
| V-10 | Rollback-key artifact written to KB AND VPS log (both paths) | KB upload + log-line presence |
If any V-1..V-7 fails inside the transaction → ROLLBACK. V-8..V-10 are post-COMMIT post-conditions; failure triggers immediate exact-key rollback per §8.
8. Exact-key rollback strategy (for future R1 — never executed in R0)
-- ROLLBACK ONLY, AFTER FAILED POST-COMMIT VALIDATION. EXACT KEYS ONLY.
BEGIN;
SET LOCAL "app.canonical_writer" = 'fn_iu_apply_edit_draft'; -- allowed marker
DELETE FROM birth_registry
WHERE collection_name = 'information_unit'
AND entity_code = ANY($captured_iu_entity_codes_text_array); -- "information_unit::<uuid>"
DELETE FROM unit_version
WHERE id = ANY($captured_uv_ids_uuid_array);
DELETE FROM information_unit
WHERE id = ANY($captured_iu_ids_uuid_array);
COMMIT;
Constraints:
- Pattern-matching deletion (e.g.
WHERE canonical_address LIKE 'D38-DIEU35-%') is PROHIBITED. Reason: it could destroy unrelated rows in a future state. Only the exact UUIDs captured at create time are valid rollback keys. - The rollback key bundle MUST be written to BOTH (a) a KB report at
knowledge/dev/laws/dieu44-trien-khai/reports/p3d-pack1-phase5c2-dieu35-pilot-migration-rollback-keys-<utc>.mdAND (b) a VPS log line inLOGGING_DIR/p3d-pack1-phase5c2-dieu35-<utc>.logBEFORE COMMIT in R1. If either write fails, R1 ABORTs (no COMMIT). This dual-write makes rollback recoverable even if KB upload fails post-COMMIT. - Post-rollback verify:
SELECT count(*) FROM information_unit WHERE id = ANY($captured_iu_ids)must be 0; same for UV and birth_registry. Source TAC counts must equal pre-R1 snapshot. - Direct manipulation of
birth_registryrequires the allowed marker becausebirth_registryis reachable through the same gateway logic for some scenarios — set the marker explicitly. (R1 prompt will live-verify whether the marker is needed for birth_registry DELETE and adjust accordingly.)
9. R0 deliverable spec
The R0 execution agent (a future, separate read-only artifact) MUST emit:
- A live source-row mapping table (36 rows for DIEU-35): one line per source row containing
src_render_order,src_canonical_address,src_section_type, the constructedfn_iu_createcall payload, the plannedidentity_profilepatch, and the plannedcontent_profilepatch. - A read-only gate report showing all G0-1..G0-14 (rev4 §6) checks PASS against the current live PG state.
- A vocab coverage report: every distinct
src_section_typein the 36 D35 rows has a matchingvocab.section_type.*key (live-verified — currently 12/12). - A collision report: zero overlap between the 36 source
canonical_addressvalues and the 12 existing IU rows (live-verified — currently 0). - A rev4 revalidation matrix (this design's §4) attached or referenced.
- A rollback-key skeleton: the exact SQL template from §8 with placeholders for captured UUIDs.
The R0 agent MUST NOT execute fn_iu_create_plan or any other live function in this pack; the call payload is a STRING artifact only. (Even _plan is gateway-marker-checked; defer to R1.)
10. What R0 does NOT do
- Does not call
fn_iu_create,fn_iu_create_plan,fn_iu_save,fn_iu_apply_edit_draft,fn_iu_edit,fn_iu_comment,fn_iu_comment_edit_draft,fn_iu_edit_plan,fn_iu_create_edit_draft,fn_iu_verify_invariants,fn_birth_onboarding_full_scan_hc, or any other mutating function. - Does not write to
information_unit,unit_version,birth_registry,tac_*,dot_config,system_health_checks,unit_edit_draft,unit_edit_comment, or any other table. - Does not change Directus permissions, Nuxt code, or Qdrant collections.
- Does not enrich
birth_registry.canonical_address,birth_registry.owner, orbirth_registry.jsonb_profilefor any row (separate DEFERRED_ENRICHMENT workstream). - Does not modify
iu_edit.policy.default_modeor anyiu_create.gateway.*key. - Does not seed new vocab keys (none are needed; all 12 section_types covered).
- Does not touch the 12 existing pilot/test IU rows.
- Does not install the IU Qdrant collection (vector work explicitly out-of-scope).
11. Required next gate
After this design is uploaded:
- GPT review — accept this design or request patches.
- Opus review — independent cross-check (no-hardcode / scale / law-boundary).
- User GO — explicit authorization.
Only after all three sign off may an R0 execution prompt (separate doc) be drafted to run the read-only mapping + dry-run. R1 prompt remains downstream of R0 review.
12. Governance status
phase5c2_resume_allowed=true
phase5c2_execution_allowed=false
phase5c2_r0_design_authorized=true (by GPT review 2026-05-14)
phase5c2_r0_execution_allowed=false (pending GPT/Opus/User review of THIS plan)
phase5c2_r1_execution_allowed=false
bulk_migration_allowed=false
ui_cutover_allowed=false
vector_work_allowed=false
schema_mutation_allowed=false
birth_system_changes_allowed=false
old_rev4_execution_allowed=false
rev4_reference_only=true
parallel_design_tracks_allowed=no
next_recommended_action=GPT_OPUS_USER_REVIEW_PHASE5C2_R0_PLAN
P3D Phase 5C2-R0 Resume Plan | Design Only | 2026-05-14 | Claude Opus 4.7 xhigh | Live-evidence-led. No mutation.