KB-2417

P3D Phase 5C2-R0 Resume Plan — TAC → Information Unit Migration — Design (Read-Only Mapping/Dry-Run)

38 min read Revision 1
p3dpack1phase5c2r0resume-plantac-to-iudesign-onlyno-execution2026-05-14dieu35

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.md rev 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 = enforced
  • iu_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_draft
  • iu_create.gateway.direct_insert_policy = block_after_guard
  • iu_create.gateway.marker_key = app.canonical_writer
  • trg_aa_iu_gateway_write_guard BEFORE INSERT OR UPDATE on information_unitfn_iu_gateway_write_guard
  • trg_aa_uv_gateway_write_guard BEFORE INSERT OR UPDATE on unit_versionfn_iu_gateway_write_guard
  • iu_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_version has no independent birth trigger.
  • species_collection_map: information_unit_atom | information_unit | is_primary=true.
  • birth_registry for collection_name='information_unit': 12 rows, all species_code='information_unit_atom', all composition_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 distinct section_type value 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_kind and p_publication_type are vocab-validated by fn_iu_create (already confirmed in Pack 22 README). vocab.unit_kind.law_unit and vocab.publication_type.law both seeded live.
  • p_section_type flows through 12 distinct values; all 12 are seeded under vocab.section_type.*.
  • p_parent_ref is NULL at the fn_iu_create boundary. The TAC hierarchy (35 of 36 rows have parent_id) is recorded in identity_profile JSONB 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_address namespace is D38-DIEU35-*. IU current namespace is pilot.* / 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_create returns status='exists_complete' (idempotent) or one of the exists_missing_* health states — R1 will report and abort the transaction. Pattern-matching deletion is PROHIBITED.

5.6 Gateway interaction (Pack 22)

  • fn_iu_create is the canonical writer and already sets app.canonical_writer = 'fn_iu_create' internally; the gateway trigger allows the resulting INSERTs into information_unit/unit_version.
  • The subsequent UPDATE information_unit SET identity_profile = identity_profile || $patch (§5.3) hits the gateway BEFORE-UPDATE trigger. R1 must set SET LOCAL "app.canonical_writer" = 'fn_iu_apply_edit_draft' (an allowed marker per iu_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 exact SET LOCAL statement.
  • Direct INSERT INTO information_unit / INSERT INTO unit_version is forbidden anywhere in R0 or R1.

5.7 Birth integration (Rev3 contract)

  • fn_iu_create causes one birth_registry row per IU via trg_birth_information_unitfn_birth_registry_auto('__birth_synthetic_id__'). unit_version is subordinate → 0 birth rows for UV.
  • Per Rev3, the birth row has canonical_address=NULL, owner=NULL, jsonb_profile='{}'::jsonb at insert (PLACEHOLDER_AT_BIRTH / REQUIRED_AT_BIRTH container). 5C2-R1 will NOT enrich these — that is a separate DEFERRED_ENRICHMENT workstream.
  • species_code defaults to 'information_unit_atom' via species_collection_map (verified live).

5.8 Pack 23 require_review integration

  • The migration uses fn_iu_create (one-shot canonical create with status='created'), not fn_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_savefn_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_review MUST NOT be touched by R0 or R1.

6. Migration options

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 onlybirth_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:

  1. The mapping itself is a non-trivial design surface (10-key identity_profile patch, gateway marker discipline on UPDATE, exact-key rollback strategy). Reviewing it before any write is the cheapest possible safety gate.
  2. 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.
  3. 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>.md AND (b) a VPS log line in LOGGING_DIR/p3d-pack1-phase5c2-dieu35-<utc>.log BEFORE 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_registry requires the allowed marker because birth_registry is 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:

  1. 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 constructed fn_iu_create call payload, the planned identity_profile patch, and the planned content_profile patch.
  2. A read-only gate report showing all G0-1..G0-14 (rev4 §6) checks PASS against the current live PG state.
  3. A vocab coverage report: every distinct src_section_type in the 36 D35 rows has a matching vocab.section_type.* key (live-verified — currently 12/12).
  4. A collision report: zero overlap between the 36 source canonical_address values and the 12 existing IU rows (live-verified — currently 0).
  5. A rev4 revalidation matrix (this design's §4) attached or referenced.
  6. 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, or birth_registry.jsonb_profile for any row (separate DEFERRED_ENRICHMENT workstream).
  • Does not modify iu_edit.policy.default_mode or any iu_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:

  1. GPT review — accept this design or request patches.
  2. Opus review — independent cross-check (no-hardcode / scale / law-boundary).
  3. 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.

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/design/p3d-phase5c2-resume-tac-to-information-unit-migration-plan.md