P3D Pack 1 Phase 2 — DDL Extend IU Schema — Implementation Report
P3D Pack 1 Phase 2 — DDL Extend IU Schema — Implementation Report
Date: 2026-05-11 Executor: Claude Opus 4.7 agent (VPS 38.242.240.89, postgres container) Source prompt: knowledge/dev/laws/dieu44-trien-khai/prompts/p3d-pack1-phase2-ddl-extend-implementation-prompt.md (rev3) Approval: knowledge/dev/laws/dieu44-trien-khai/reviews/gpt-final-approval-p3d-pack1-phase2-ddl-prompt-rev3-2026-05-11.md Log: /tmp/p3d-pack1-phase2-20260511-054722.log (on VPS)
Summary
phase_status=PASS
mode=DDL_SCHEMA_EXTENSION_ONLY
gate_results=G1:PASS G2:RECORDED G3:PASS G4:PASS G5:PASS G6:PASS G7:RECORDED
hash_investigation_findings=IU.content_hash matches sha256(body::bytea); TAC.content_hash matches neither md5(body) nor sha256(body::bytea) — different algorithm/encoding. Record-only; does not gate Phase 2 DDL.
iu_cols_before=16
iu_cols_after=19
uv_cols_before=9
uv_cols_after=16
iu_rows_before=12
iu_rows_after=12
uv_rows_before=19
uv_rows_after=19
columns_added=information_unit.doc_code,information_unit.section_type,information_unit.section_code,unit_version.title,unit_version.description,unit_version.review_state,unit_version.provenance,unit_version.editor,unit_version.enacted_at,unit_version.updated_at
all_new_columns_nullable=true
new_column_defaults_present=false
fk_added=false
index_added=false
trigger_added=false
data_migration_performed=false
tac_tables_modified=false
directus_mutation_performed=false
vector_columns_added=false
qdrant_mutation_performed=false
gateway_after=enforced
gateway_triggers_enabled=true
fn_iu_create_plan_reachable=true
rollback_performed=false
log_path=/tmp/p3d-pack1-phase2-20260511-054722.log
Preflight gate results
| Gate | Check | Result |
|---|---|---|
| G1 | public.information_unit + public.unit_version exist | 2/2 — PASS |
| G2 | Record cols/rows before | IU_COLS=16 UV_COLS=9 IU_ROWS=12 UV_ROWS=19 |
| G3 | iu_create.gateway.mode='enforced' | PASS |
| G4 | trg_aa_iu/uv_gateway_write_guard enabled (tgenabled='O') | 2/2 — PASS |
| G5 | All 10 target columns missing (verdict=missing_ok); no bad existing | G5_BAD=0 — PASS |
| G6 | No vector columns present (vector_sync_status/synced_at/chunk_count) | 0 — PASS |
| G7 | Activity snapshot recorded (no blocking long-tx beyond own session) | RECORDED |
Hash investigation (record-only)
- TAC sample (
tac_unit_version):content_hashlength 64; md5(body) mismatch; sha256(body::bytea) mismatch on all 5 sampled rows. Algorithm/encoding diverges from naive interpretations (likely sha256 over a normalized form or non-bytea cast). - IU sample (
unit_version):content_hashlength 64; matchesencode(sha256(body::bytea),'hex')on all 5 sampled rows. - Conclusion: TAC and IU currently use different content-hash schemes. Reconciliation is Phase-deferred (out of scope for Phase 2 DDL).
DDL executed
Single transaction with lock_timeout=5s and statement_timeout=60s:
BEGIN;
SET LOCAL lock_timeout = '5s';
SET LOCAL statement_timeout = '60s';
ALTER TABLE public.information_unit ADD COLUMN IF NOT EXISTS doc_code TEXT;
ALTER TABLE public.information_unit ADD COLUMN IF NOT EXISTS section_type TEXT;
ALTER TABLE public.information_unit ADD COLUMN IF NOT EXISTS section_code TEXT;
COMMENT ON COLUMN public.information_unit.doc_code IS 'Structural document lineage binding. NULL for non-law units. No FK in Phase 2.';
COMMENT ON COLUMN public.information_unit.section_type IS 'Section classification. No FK in Phase 2; vocab reconciliation deferred.';
COMMENT ON COLUMN public.information_unit.section_code IS 'Human-readable section alias. Not identity.';
ALTER TABLE public.unit_version ADD COLUMN IF NOT EXISTS title TEXT;
ALTER TABLE public.unit_version ADD COLUMN IF NOT EXISTS description TEXT;
ALTER TABLE public.unit_version ADD COLUMN IF NOT EXISTS review_state TEXT;
ALTER TABLE public.unit_version ADD COLUMN IF NOT EXISTS provenance TEXT;
ALTER TABLE public.unit_version ADD COLUMN IF NOT EXISTS editor TEXT;
ALTER TABLE public.unit_version ADD COLUMN IF NOT EXISTS enacted_at TIMESTAMPTZ;
ALTER TABLE public.unit_version ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ;
COMMENT ON COLUMN public.unit_version.title IS 'Unit title per version. NULL until populated.';
COMMENT ON COLUMN public.unit_version.description IS 'Unit description per version. NULL until populated.';
COMMENT ON COLUMN public.unit_version.review_state IS 'Review lifecycle. No FK/default in Phase 2. NULL means unset.';
COMMENT ON COLUMN public.unit_version.provenance IS 'Content provenance. No FK/default in Phase 2. NULL means unset.';
COMMENT ON COLUMN public.unit_version.updated_at IS 'Last content update timestamp. NULL means not set by current workflow.';
COMMIT;
Outcome: COMMIT reached without lock-timeout or error. No rollback required.
Post-DDL verification evidence
Column count deltas
| Table | Before | After | Delta | Expected |
|---|---|---|---|---|
| public.information_unit | 16 | 19 | +3 | +3 ✓ |
| public.unit_version | 9 | 16 | +7 | +7 ✓ |
Row count deltas
| Table | Before | After | Δ |
|---|---|---|---|
| public.information_unit | 12 | 12 | 0 ✓ |
| public.unit_version | 19 | 19 | 0 ✓ |
New columns NULL on existing rows
- IU_NON_NULL_NEW_COLS = 0 ✓
- UV_NON_NULL_NEW_COLS = 0 ✓
Defaults / nullability on new columns
information_unit.doc_code text YES (no default)
information_unit.section_code text YES (no default)
information_unit.section_type text YES (no default)
unit_version.description text YES (no default)
unit_version.editor text YES (no default)
unit_version.enacted_at timestamp with time zone YES (no default)
unit_version.provenance text YES (no default)
unit_version.review_state text YES (no default)
unit_version.title text YES (no default)
unit_version.updated_at timestamp with time zone YES (no default)
NEW_COLS_WITH_DEFAULT_OR_NOT_NULL = 0 ✓
No FK / no index / no trigger added on new columns
- FK_ON_NEW_COLS = 0 ✓
- INDEX_ON_NEW_COLS = 0 ✓
- TRIGGER_COUNT_TARGET_TABLES = 7 (unchanged baseline trigger set; DDL was only ALTER ADD COLUMN, no CREATE TRIGGER was issued)
Gateway state after DDL
iu_create.gateway.mode=enforced✓trg_aa_iu_gateway_write_guardon information_unit, tgenabled='O' ✓trg_aa_uv_gateway_write_guardon unit_version, tgenabled='O' ✓
fn_iu_create_plan reachability (dry-run, no row created)
Call:
SELECT public.fn_iu_create_plan('test/phase2-verify', 'law_unit', 'Phase 2 verify', 'Test body', NULL, NULL, NULL, NULL, NULL);
Result (single row):
{
"mode": "plan",
"issues": [],
"status": "plan_ok",
"existing": null,
"preflight": "pass",
"body_length": 14,
"would_create": true,
"resolved_owner_ref": "Test body",
"resolved_unit_kind": "design_doc_section",
"content_hash_preview": "57dde47fe54c81b2",
"fk_initially_deferred": true,
"resolved_section_type": "section"
}
Function reachable; planner returns plan_ok; no row inserted (mode=plan).
Warnings
- TAC content_hash algorithm/encoding remains unresolved (see hash investigation above). This is record-only for Phase 2; reconciliation belongs to a later phase before EVOLVE-time data alignment.
resolved_unit_kindreturneddesign_doc_sectiondespite caller passing'law_unit'— this is the planner's policy resolution and does not affect Phase 2 DDL verdict, but should be reviewed in the law-unit unification phase.
Hard boundaries — confirmed
- No data migration performed.
- No TAC table modified.
- No view created.
- No publication_member modified.
- No vocab rename/FK.
- No Qdrant/vector mutation.
- No Nuxt code changes.
- No DOT-119 execution.
- No Directus mutation/restart/schema-refresh.
- No DEFAULT on new columns.
- No FK on new columns.
- No index on new columns.
- No trigger added.
- No vector columns added.
- No row updates.
- No direct data writes to information_unit / unit_version (planner dry-run only).
Next recommended phase
Pack 1 Phase 3 — read-side compatibility / introspection layer for the new IU+UV columns (still no data writes), preparing for the future EVOLVE backfill from TAC. Hash algorithm reconciliation must precede any TAC→IU body migration.
Rollback
Not performed. DDL and all verification passed.