KB-1870

P3D Pack 1 Phase 2 — DDL Extend IU Schema — Implementation Report

9 min read Revision 1
p3dpack1phase2ddlreport2026-05-11pass

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_hash length 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_hash length 64; matches encode(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_guard on information_unit, tgenabled='O' ✓
  • trg_aa_uv_gateway_write_guard on 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_kind returned design_doc_section despite 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).

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.

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/reports/p3d-pack1-phase2-ddl-extend-implementation-report.md