P3D Pack 1 — IU Canonical Contract + TAC↔IU Reconciliation Design (EVOLVE)
P3D Pack 1 — IU Canonical Contract + TAC↔IU Reconciliation Design
Date: 2026-05-10 Author: Opus 4.7 (Claude) Directive: gpt-directive-opus-p3d-pack1-iu-contract-and-tac-reconciliation-2026-05-10.md Mode: DESIGN ONLY — no DDL, no DB mutation, no migration Status: PRELIMINARY — inventory prompt dispatched but not yet executed. Design grounded in P38-XC + IU-0 + P5 schema draft + Pack 22/23 reports. Live schema evidence pending. SSOT sources: P38-XC final (04-information-unit-profile-schema.md), IU-0 (07-iu0-index-and-core.md), P5 (P5-schema-draft-v0-2.md), Pack 22/23 reports
§A. Executive Decision
Recommended path: EVOLVE
IU native information_unit evolves to subsume TAC tac_logical_unit semantics. TAC tables become views/projections after migration. One schema, one gateway, one set of functions.
Rationale: P38-XC §1.2 already decided "KHÔNG sinh object mới". IU-0 §2.1 decided "information_unit là universal information substrate — Không hệ schema thứ hai." P38-XC §5.7: "TAC = specialized implementation cho unit_kind = law_unit". The design documents already mandate EVOLVE — this Pack 1 formalizes the migration path.
Phase boundaries (EVOLVE):
| Phase | Scope | Gate |
|---|---|---|
| Phase 1 (this pack) | Design-only + read-only inventory | GPT review |
| Phase 2 | Add missing columns to IU native (unit_kind, section_type, sort_order, conformance_status) | DDL prompt, GPT review |
| Phase 3 | Create TAC compatibility views | DDL prompt, GPT review |
| Phase 4 | Migrate 86 TAC rows → IU native (with round-trip verification) | Migration prompt, GPT review |
| Phase 5 | TAC tables → views OR archive; verify render pipeline | GPT + User approve |
§B. Inventory Comparison
Pending live schema evidence from inventory prompt. This section based on P38-XC §10 + P5 §5 + Pack 22/23 23-P2 inspection (33/33 queries).
| Concept | TAC physical | IU native physical | Status |
|---|---|---|---|
| Identity (UUID) | tac_logical_unit.id |
information_unit.id |
Both exist, compatible |
| Canonical address | tac_logical_unit.canonical_address (UNIQUE) |
information_unit.canonical_address (UNIQUE) |
Both exist, compatible |
| Publication membership | tac_publication_member (junction) |
N/A — IU uses version_anchor_ref directly |
GAP: IU has no publication membership model |
| Render order | tac_publication_member.render_order |
N/A | GAP: IU has no render_order |
| Parent/section hierarchy | tac_logical_unit.parent_id (self-ref) |
information_unit.parent_or_container_ref (nullable) |
Compatible — different names |
| Sort order | tac_logical_unit.sort_order |
N/A | GAP: IU has no sort_order |
| Section type | tac_logical_unit.section_type (FK vocab) |
N/A | GAP: IU has no section_type |
| Unit kind | N/A — all TAC = law_unit |
N/A — no column | GAP: neither has unit_kind column |
| Unit version | tac_unit_version |
unit_version |
Compatible schemas |
| Body | tac_unit_version.body |
unit_version.body (NOT NULL) |
Compatible |
| Content hash | tac_unit_version.content_hash |
unit_version.content_hash (NOT NULL) |
Compatible |
| Version seq | tac_unit_version.version_seq |
unit_version.version_seq (NOT NULL) |
Compatible |
| Lifecycle status | Both tables have | Both tables have | Compatible |
| Review state | tac_unit_version.review_state |
N/A in IU UV (check) | CHECK: may exist |
| Identity profile | tac_logical_unit.identity_profile JSONB (GIN) |
information_unit.identity_profile JSONB (GIN) |
Compatible |
| Content profile | tac_unit_version.content_profile JSONB |
unit_version.content_profile JSONB (NO GIN) |
Compatible — GIN diff |
| Version anchor | N/A | information_unit.version_anchor_ref FK→UV |
IU-ONLY: pointer to current/latest UV |
| Content anchor | N/A | information_unit.content_anchor_ref |
IU-ONLY |
| Gateway guard | N/A — TAC has no gateway | Enforced (Pack 22) | IU-ONLY: stronger governance |
| Birth registry | Auto trigger (DOT-119 v2) | Trigger + fn_iu_create | IU has dedicated create path |
| Notification | N/A | P3D1+P3D2 triggers | IU-ONLY |
| Event emission | N/A | event_outbox runtime | IU-ONLY |
| Conformance status | N/A | N/A | GAP: P44-5 creates |
§C. Do-Not-Rebuild Constraints
| # | Runtime | Must preserve |
|---|---|---|
| 1 | TAC 3 pubs / 86 units / 0 drift | Round-trip must remain 0 drift post-migration |
| 2 | fn_iu_create gateway (Pack 22) | Gateway guard must protect reconciled model |
| 3 | fn_iu_apply_edit_draft, fn_iu_edit, fn_iu_save | Edit machinery must work on reconciled schema |
| 4 | require_review policy (P3C4) | Policy unaffected |
| 5 | IU notification runtime (P3D1+P3D2) | Triggers fire on reconciled tables |
| 6 | event_outbox published display | Unaffected (separate table) |
| 7 | DOT-119 v2/no-clobber | birth_registry trigger must fire for migrated rows |
| 8 | TAC reader pipeline (/knowledge/laws) | Nuxt reader must continue working — via compatibility view or adapted query |
§D. UMC / Contract Verification
Source: P38-XC §5.2 (verified from full doc read in this session)
| # | UMC element | P38-XC citation | TAC mapping | IU native mapping | Reconciliation note |
|---|---|---|---|---|---|
| U1 | unit_id | §5.2 | tac_logical_unit.id | information_unit.id | Same type (UUID) |
| U2 | canonical_address | §5.2 | tac_logical_unit.canonical_address | information_unit.canonical_address | Compatible — format per unit_kind (§5.4) |
| U3 | unit_kind | §5.2, §5.3 | MISSING (all = law_unit) | MISSING | Add column to IU native |
| U4 | lifecycle_status | §5.2 | tac_logical_unit.lifecycle_status | information_unit.lifecycle_status | Compatible |
| U5 | content_anchor_ref | §5.2 | Implicit via UV relationship | information_unit.content_anchor_ref | IU has it, TAC doesn't — non-blocking |
| U6 | version_anchor_ref | §5.2 | Implicit via pub_member | information_unit.version_anchor_ref | IU has it, TAC doesn't — add during migration |
| U7 | owner_ref | §5.2 | identity_profile JSONB | identity_profile JSONB (infer) | CHECK: verify both have owner in JSONB |
| U8 | timestamps | §5.2 | created_at, updated_at | created_at, updated_at | Compatible |
| U9 | parent_or_container_ref | §5.2 | tac_logical_unit.parent_id | information_unit.parent_or_container_ref | Compatible — different name |
| U10 | conformance_status | §5.2 | MISSING | MISSING | Add column (P44-5) |
Difference from spec re-authored §G: Spec listed 16 concepts (10 UMC + 6 extensions). P38-XC source confirms 10 UMC as stated. Extensions (version_anchor_ref, content_anchor_ref, parent_or_container_ref, unit_kind, section_type, review_state) are correct per P38-XC + P5.
§E. Canonical IU Contract v1
| # | Field/Concept | In IU native now? | In TAC now? | In both (diff name)? | Missing (add) | Deferred |
|---|---|---|---|---|---|---|
| 1 | unit_id (UUID PK) | ✅ | ✅ | — | — | — |
| 2 | canonical_address (UNIQUE) | ✅ | ✅ | — | — | — |
| 3 | unit_kind | ❌ | ❌ (implicit law_unit) | — | ADD | — |
| 4 | lifecycle_status | ✅ | ✅ | — | — | — |
| 5 | content_anchor_ref | ✅ | ❌ | — | — | — |
| 6 | version_anchor_ref (FK→UV) | ✅ | ❌ | — | — | — |
| 7 | parent_or_container_ref | ✅ | ✅ (parent_id) | ✅ diff name | — | — |
| 8 | identity_profile JSONB | ✅ (GIN) | ✅ (GIN) | — | — | — |
| 9 | timestamps (created/updated) | ✅ | ✅ | — | — | — |
| 10 | conformance_status | ❌ | ❌ | — | ADD (P44-5) | — |
| 11 | section_type (FK vocab) | ❌ | ✅ | — | ADD to IU | — |
| 12 | sort_order | ❌ | ✅ | — | ADD to IU | — |
| 13 | review_state | ❌ (check UV) | ✅ (UV) | — | — | — |
| 14 | body (UV-level) | ✅ | ✅ | — | — | — |
| 15 | content_hash (UV-level) | ✅ | ✅ | — | — | — |
| 16 | version_seq (UV-level) | ✅ | ✅ | — | — | — |
| 17 | content_profile JSONB (UV) | ✅ (no GIN) | ✅ | — | — | — |
| 18 | publication_member junction | ❌ | ✅ | — | BRIDGE | — |
| 19 | render_order (pub_member) | ❌ | ✅ | — | BRIDGE | — |
Columns to ADD to IU native: unit_kind, sort_order, section_type (+ conformance_status later). Publication membership: Keep tac_publication + tac_publication_member as-is (or evolve names). This is a separate family (not IU).
§F. Reconciliation Option Analysis
MERGE
- Benefits: One table, clean, no confusion
- Risks: 86 production rows migration, reader pipeline break if not careful
- Complexity: Medium — data migration + view creation
- ≈ EVOLVE (functionally same)
BRIDGE
- Benefits: Zero migration risk, both systems co-exist
- Risks: Two schemas = maintenance burden, confusion, violates P38-XC §1.2 + IU-0 §2.1
- Complexity: Low initially, HIGH long-term
- Verdict: Violates design mandate
EVOLVE (RECOMMENDED)
- Benefits: Aligns with P38-XC + IU-0 design intent, one schema, gateway protects all, IU functions work for all unit_kinds
- Risks: Migration risk (86 rows, mitigated by small scale + round-trip verify), reader pipeline needs adaptation (CTE query or view)
- Complexity: Medium — staged, each phase independently verifiable
- TAC reader compatibility: Create
tac_logical_unitas VIEW overinformation_unit WHERE unit_kind='law_unit'; reader CTE unchanged if column names preserved - Gateway: Already protects information_unit — migrated TAC rows automatically protected
- Edit/save: fn_iu_save already routes by address — works for migrated rows
- Event emission: IU event triggers fire for migrated rows
- Vector boundary: Unchanged — 1 chunk ⊂ 1 unit + 1 version
- Rollback: Keep TAC tables as backup for grace period; drop only after verify
HYBRID
- Benefits: Safety — bridge now, evolve later
- Risks: Defers inevitable, adds complexity
- Verdict: Unnecessary given 86-row scale
Final recommendation: EVOLVE with 5 phases (see §A).
§G. Recommended Architecture
Phase 1: Design + Inventory (THIS PACK) — no mutation
- Read-only inventory prompt (dispatched)
- Design document (this file)
- GPT/User review
Phase 2: DDL — extend IU native schema
- Add
unit_kindcolumn (default 'draft_unclassified', NOT NULL after migration) - Add
sort_ordercolumn (INTEGER, default 0) - Add
section_typecolumn (TEXT, FK to section_type_vocab if exists, nullable initially) - Verify fn_iu_create, fn_iu_save, gateway still function
- Run existing tests
Phase 3: Compatibility views
- Create
v_tac_logical_unitAS SELECT frominformation_unitWHERE unit_kind='law_unit' (map column names) - Create
v_tac_unit_versionAS SELECT fromunit_version(map if needed) - Verify TAC reader CTE works against views
Phase 4: Migration
- INSERT INTO information_unit SELECT ... FROM tac_logical_unit (with column mapping)
- INSERT INTO unit_version SELECT ... FROM tac_unit_version (with column mapping)
- Update tac_publication_member to point to new IU rows (or keep as-is if using views)
- Set unit_kind='law_unit' for all migrated rows
- Run round-trip 0 drift verification (same method as P10A/P10B)
- Run fn_iu_verify_invariants on each migrated row
Phase 5: Cutover
- TAC reader CTE → point to IU native (or views)
- tac_* tables → archive or rename with _legacy suffix
- Grace period monitoring (2 weeks)
- Drop legacy after verify
§H. Acceptance Criteria for Implementation
| # | Criterion | Verification method |
|---|---|---|
| AC-1 | All 86 TAC units accounted for in IU native | COUNT match + canonical_address match |
| AC-2 | Round-trip 0 drift preserved | Same diff method as P10A/P10B |
| AC-3 | No direct-write into information_unit/unit_version | Gateway guard still enforced |
| AC-4 | fn_iu_create gateway remains canonical create path | Test create via fn_iu_create → PASS |
| AC-5 | Pack 23 edit/save functions work on migrated rows | Test fn_iu_save on migrated address → PASS |
| AC-6 | render_order preserved | publication_member unchanged or adapted |
| AC-7 | Vector boundary: 1 chunk ⊂ 1 unit + 1 version | Rule unchanged, verify architecture |
| AC-8 | Event emission path attachable | IU notification triggers fire on migrated rows |
| AC-9 | TAC reader pipeline functions | /knowledge/laws still renders 3 publications |
| AC-10 | unit_kind = 'law_unit' for all 86 rows | SQL verify |
§I. Open Questions and Decisions
| # | Question | Recommend | Decide at |
|---|---|---|---|
| OQ-1 | Implementation pattern (P38-X-12): single table or per-kind? | Single table (EVOLVE = single information_unit) |
This pack (GPT review) |
| OQ-2 | unit_kind vocab anchor: Đ24 or dot_config? | dot_config initially, Đ24 integration later | Phase 2 |
| OQ-3 | section_type_vocab: reuse existing or create new? | Reuse existing (already seeded for TAC) | Phase 2 (verify via inventory) |
| OQ-4 | publication_member: rename tac_ prefix or keep? | Keep as-is initially — publication is separate family | Phase 5 |
| OQ-5 | Gateway allow-list: expand for fn_iu_save? | Already done (Pack 23 P3C2) — verify | Phase 2 |
| OQ-6 | Pilot or big-bang migration? | Pilot first (1 doc → verify → remaining 2) | Phase 4 |
| OQ-7 | TAC tables post-migration: views, archive, or drop? | Views for grace period → archive after verify | Phase 5 |
| OQ-8 | Birth registry for migrated rows: backfill or skip? | Backfill via fn_birth_registry_auto trigger (auto on INSERT) | Phase 4 |
§J. Next Pack Recommendation
Next step: read-only inventory prompt dispatch + GPT review this design.
After inventory report confirms schema assumptions:
- GPT reviews this design — approve/patch EVOLVE direction
- If EVOLVE approved → Phase 2 DDL prompt (extend IU schema with 3 columns)
- Phase 2 dispatched to Codex/Claude Code with full test suite
Hard Boundary Attestation
This design does NOT:
- ❌ Mutate any database table
- ❌ Create or alter any PG object
- ❌ Execute migration
- ❌ Create bridge or view
- ❌ Write Nuxt code
- ❌ Execute DOT-119
P3D Pack 1 Design | PRELIMINARY | EVOLVE recommended | 2026-05-10 | Opus 4.7 | Pending inventory evidence