KB-6FC4

dot-iu-cutter v0.2 — Phase α Dry-Run Plan (2026-05-15)

19 min read Revision 1
dieu44-trien-khaidot-iu-cutterv0.2phase-alphadry-run-planno-execution2026-05-15

dot-iu-cutter v0.2 — Phase α Dry-Run Plan

document_path: knowledge/dev/laws/dieu44-trien-khai/v0.2-design/dot-iu-cutter-v0.2-phase-alpha-dry-run-plan-2026-05-15.md
revision: r1
date: 2026-05-15
author: Agent (Claude Code CLI, Opus 4.7 1M)
phase: v0.2 — Phase α dry-run PLAN (planning only; no dry-run executed)
no_dry_run_executed: TRUE
no_DDL_written: TRUE
no_mutation: TRUE

§1 — Purpose

Specify the dry-run scenario matrix that the Phase α DDL must satisfy before any production migration is authorized. This is the HB-05-equivalent for Phase α. No scenario is run in this document; this is the plan to be executed in a future explicitly-prompted session.


§2 — Dry-Run Environment Requirement

recommendation: SPIN A FRESH SIBLING dry-run env (do NOT reuse the v0.1 HB-05 env)
proposed_name: pg-dry-run-v0.2-phase-alpha-<date>
proposed_image: postgres:16 (matching production PG 16.13)
proposed_network: docker bridge (no host port published; isolated)
proposed_volume: pg-dry-run-v0.2-phase-alpha-<date>-data (persistent for the dry-run cycle)

reasons_to_NOT_reuse_HB_05_env (pg-dry-run-hb05-2026-05-15):
  - HB-05 env carries v0.1 dry-run-only cutter_governance schema state + 26-scenario harness state
  - mixing v0.1 dry-run with v0.2 scenarios risks cross-contamination of evidence
  - HB-05 env must remain UNCHANGED per v0.1 final-readiness §4 condition (d) until v0.2 success declared
  - rolling Phase α dry-run onto HB-05 env could inadvertently dirty it

baseline_restore_source:
  - a FRESH production-bound backup (taken at dry-run setup time; HB-08 pattern, pg_dump -F c -Z 6)
  - sha256 recorded; restore-test PASS before dry-run begins
  - alternative: REUSE the v0.1 fresh-execution backup at /opt/incomex/backups/dieu44_exec_2026-05-15/directus_full_20260515T141429Z.dump (sha256 7d7e424c…)
    → acceptable IF the production state has not materially changed since 2026-05-15 14:14 UTC
    → recommended check: compare current row counts vs the pre-snapshot psv before restoring; if they differ, take a fresh backup instead
  - dry-run baseline must include: 86 tac_logical_unit rows, 76 sandbox_tac.logical_unit rows, 5 cutter_governance tables (empty), all sister tables (events / drafts / units / etc.)

§3 — Dry-Run DDL Authoring (separate future session)

NOT_authored_here: this plan describes the scenarios; the actual DDL file is authored in a separate session under explicit prompt

planned_dry_run_DDL_outline_(for_clarity_only;_no_DDL_in_this_doc):
  Step 1:  ADD COLUMN public.tac_logical_unit.canonical_address_format_version text NOT NULL DEFAULT 'canonical-address-v1'
  Step 2:  ADD COLUMN public.tac_logical_unit.authority text NULL DEFAULT 'draft'
  Step 3:  UPDATE public.tac_logical_unit SET authority = CASE lifecycle_status WHEN 'draft_only' THEN 'draft' WHEN 'active' THEN 'enacted' WHEN 'retired' THEN 'enacted' END WHERE authority IS NULL
  Step 4:  ADD COLUMN sandbox_tac.logical_unit.canonical_address_format_version text NOT NULL DEFAULT 'canonical-address-v1'
  Step 5:  ADD COLUMN sandbox_tac.logical_unit.authority text NULL DEFAULT 'draft'
  Step 6:  (NO sandbox backfill of authority in Phase α; rows remain NULL)
  Step 7:  CREATE TABLE IF NOT EXISTS cutter_governance.canonical_address_alias (alias_id uuid PK, target_unit_id uuid NOT NULL, alias_text text NOT NULL, alias_kind text NOT NULL, valid_from timestamptz NOT NULL DEFAULT now(), valid_until timestamptz NULL, created_by text NOT NULL, rationale text NULL, scenario_ref text NULL)
  Step 8:  CREATE INDEX on alias_text, target_unit_id, alias_kind, (valid_from, valid_until DESC)

execution_form_in_dry_run:
  single transaction, ON_ERROR_STOP=1, mirroring HB-05 / v0.1 production pattern
  all 8 steps in one transaction; either all land or none

§4 — Scenario Matrix (Phase α HB-equivalent)

26 scenarios, modeled after the HB-05 pattern. NONE executed in this plan.

4.1 Column-add and DEFAULT scenarios (S01–S05)

# Scenario Setup Expected Outcome Pass Condition
S01 Add canonical_address_format_version to tac_logical_unit baseline restored (86 rows) column exists; all 86 rows = 'canonical-address-v1' exact match
S02 Add authority to tac_logical_unit (no backfill yet) post-S01 column exists; all 86 rows = NULL initially exact match
S03 Backfill authority via Candidate B CASE post-S02 all 86 rows = 'draft' (since 100% lifecycle='draft_only') exact match; count(authority IS NULL) = 0
S04 Add canonical_address_format_version to sandbox_tac.logical_unit baseline includes sandbox column exists; all 76 sandbox rows = 'canonical-address-v1' exact match
S05 Add authority to sandbox_tac.logical_unit (no backfill) post-S04 column exists; all 76 rows = NULL exact match; sandbox backfill intentionally skipped

4.2 Alias-table creation scenarios (S06–S09)

# Scenario Setup Expected Outcome Pass Condition
S06 Create cutter_governance.canonical_address_alias post-S05 table exists; 0 rows exact match
S07 Indexes on canonical_address_alias exist post-S06 PK + 4 btree indexes per §3 outline indexes verified by pg_indexes query
S08 Insert one synthetic alias row (then delete) post-S06 row inserted; row deleted; table back to 0 rows round-trip; no errors
S09 Alias table rollback (DROP TABLE) post-S08 (then re-create for S10) table dropped; no orphan; no FK violation clean drop

4.3 Default-on-insert scenarios (S10–S12)

# Scenario Setup Expected Outcome Pass Condition
S10 INSERT a new tac_logical_unit row without specifying authority post-S03 + table reset to a known synthetic baseline new row has authority = 'draft' (via DEFAULT); format_version = 'canonical-address-v1' both columns auto-populated
S11 INSERT a new tac_logical_unit row with explicit authority='enacted' post-S10 reset new row has authority = 'enacted'; format_version = 'canonical-address-v1' explicit value honored
S12 INSERT a new tac_logical_unit row with explicit format_version='canonical-address-v1.1' (forward-compat test) post-S11 reset row accepts the value (Phase α has no FK/CHECK on format_version) column accepts the value; no constraint violation

4.4 Backfill correctness scenarios (S13–S16)

# Scenario Setup Expected Outcome Pass Condition
S13 Synthetic row with lifecycle_status='active' before backfill inject synthetic active row before Step 3 backfill backfill assigns authority='enacted' mapping correct
S14 Synthetic row with lifecycle_status='retired' before backfill inject synthetic retired row before Step 3 backfill backfill assigns authority='enacted' mapping correct (matches BR-4 ratified interpretation)
S15 Backfill is idempotent (re-running the UPDATE produces no further change) post-S03 second UPDATE affects 0 rows (WHERE authority IS NULL matches nothing) rowcount=0 on re-run
S16 Synthetic row with NULL lifecycle_status (would fail FK but test backfill robustness) inject only if PG FK allows; otherwise skip backfill leaves authority=NULL (CASE returns NULL); WHERE authority IS NULL filter spares the row clear behavior under unexpected vocab

4.5 Reader/writer smoke tests (S17–S20)

# Scenario Setup Expected Outcome Pass Condition
S17 Existing trigger trg_tac_birth_gate_lu still fires on INSERT post-Phase-α trigger fires; new columns visible inside trigger but unused trigger does not error
S18 fn_event_unread query still returns canonical_address correctly post-Phase-α function output unchanged query result matches baseline
S19 fn_iu_verify_invariants still passes on synthetic IU post-Phase-α function returns its existing verdict result unchanged
S20 Existing UNIQUE constraint on canonical_address still enforced post-Phase-α attempt to insert duplicate canonical_address fails rejection observed

4.6 sandbox_tac mirror verification (S21–S22)

# Scenario Setup Expected Outcome Pass Condition
S21 sandbox_tac.fn_sbx_lu_parent_same_doc still functions post-Phase-α sandbox helper unaffected by new columns function passes synthetic test
S22 sandbox_tac.logical_unit row inserts get authority=NULL by default behavior choice (no backfill) and format_version='canonical-address-v1' post-S05 new sandbox row matches policy column values match policy

4.7 Rollback scenarios (S23–S26)

# Scenario Setup Expected Outcome Pass Condition
S23 Drop authority column from tac_logical_unit post-S03 column gone; rows preserved column gone; rowcount=86 unchanged
S24 Drop format_version column from tac_logical_unit post-S01 column gone; rows preserved column gone; rowcount=86 unchanged
S25 Drop canonical_address_alias table post-S06 table gone; no orphan; no error clean drop
S26 Full rollback inside the same dry-run transaction: BEGIN; (all 8 steps); ROLLBACK; fresh restored baseline NO trace of any new column or table after ROLLBACK schema diff against baseline = empty

§5 — Expected Pass / Fail Matrix Summary

expected_PASS_count: 26
expected_FAIL_count: 0
expected_safety_critical_failures: 0
expected_stops: 0

per_category_summary:
  column_add_default:  5 scenarios   (S01–S05) — all PASS
  alias_table:         4 scenarios   (S06–S09) — all PASS
  default_on_insert:   3 scenarios   (S10–S12) — all PASS
  backfill_correctness:4 scenarios   (S13–S16) — all PASS
  reader_writer_smoke: 4 scenarios   (S17–S20) — all PASS (existing behavior unchanged)
  sandbox_mirror:      2 scenarios   (S21–S22) — all PASS
  rollback:            4 scenarios   (S23–S26) — all PASS

acceptance_threshold: 100% (26/26)  — same discipline as v0.1 HB-05
failure_at_any_scenario_BLOCKS_production_authorization: TRUE

§6 — Backfill Verification Procedure

pre_backfill_state:                          authority = NULL on all 86 rows (post-S02)
post_backfill_state_expected:                authority = 'draft' on all 86 rows
verification_queries (dry-run only):
  Q-1: SELECT count(*) FROM public.tac_logical_unit WHERE authority IS NULL;
       expected: 0
  Q-2: SELECT authority, count(*) FROM public.tac_logical_unit GROUP BY authority;
       expected: 'draft' | 86
  Q-3: SELECT count(*) FROM public.tac_logical_unit WHERE authority='draft' AND lifecycle_status='draft_only';
       expected: 86
  Q-4: SELECT count(*) FROM public.tac_logical_unit WHERE authority NOT IN ('enacted','draft','runtime');
       expected: 0  (defensive: no unexpected values)

verification_passes_iff: all 4 queries return their expected values
on_failure: rollback dry-run; record evidence; escalate to GPT review before retry

§7 — Reader/Writer Smoke Tests

purpose: confirm that NO existing reader/writer breaks after Phase α additive columns land

tests_to_run (in dry-run env):
  T-1: fn_event_unread('synthetic_actor', NULL) — call the function; confirm it returns rows; confirm the canonical_address column in the result matches baseline values
  T-2: fn_iu_verify_invariants('D38-DIEU28-S2-P1') OR equivalent synthetic call — confirm same verdict as baseline
  T-3: existing Nuxt-style SELECT query: SELECT logical_unit_id, render_order, …, canonical_address, … FROM <appropriate join> LIMIT 5 — confirm rows return with expected fields; new columns NOT in this select
  T-4: trigger trg_tac_birth_gate_lu — INSERT a synthetic tac_logical_unit row; confirm trigger fires; no error
  T-5: UNIQUE constraint on canonical_address — attempt duplicate INSERT; confirm rejection

expected_outcome_for_all_smoke_tests: PASS, behavior identical to baseline before Phase α
on_any_smoke_test_failure: BLOCKS dry-run acceptance; rollback and escalate

§8 — sandbox_tac Mirror Verification

tests_in_dry_run:
  M-1: sandbox_tac.logical_unit has the two new columns      (S04 + S05 confirm structurally)
  M-2: sandbox_tac.logical_unit rows have authority=NULL (76 rows; intentional non-backfill)
  M-3: sandbox_tac.logical_unit rows have format_version='canonical-address-v1' (76 rows via DEFAULT)
  M-4: sandbox_tac.fn_sbx_lu_parent_same_doc still passes existing synthetic test (S21)
  M-5: sandbox_tac.canonical_address_alias does NOT exist (intentional omission per design master §5.3)

expected_outcome: all 5 mirror checks PASS

§9 — Rollback Test Cases (detailed)

test_case_R-1: drop authority column on tac_logical_unit
  preconditions: S03 complete (backfilled state)
  command_outline: ALTER TABLE public.tac_logical_unit DROP COLUMN authority;
  expected: column removed; 86 rows preserved; canonical_address SSOT untouched
  recovery: re-apply S02 + S03 to restore

test_case_R-2: drop canonical_address_format_version column on tac_logical_unit
  preconditions: S01 complete
  command_outline: ALTER TABLE public.tac_logical_unit DROP COLUMN canonical_address_format_version;
  expected: column removed; 86 rows preserved
  recovery: re-apply S01

test_case_R-3: drop canonical_address_alias table
  preconditions: S06+ complete; table possibly empty or with synthetic rows (test both)
  command_outline: DROP TABLE cutter_governance.canonical_address_alias;
  expected: table removed; no orphan; no FK violation (no FK to it exists)
  recovery: re-apply S06–S08

test_case_R-4: in-transaction ROLLBACK of full Phase α DDL
  preconditions: fresh baseline; BEGIN; (Steps 1–8); ROLLBACK;
  expected: post-ROLLBACK schema diff against baseline = empty
  this is the strongest rollback evidence; PASSING this scenario alone covers most failure modes

test_case_R-5: drop sandbox_tac mirror columns
  preconditions: S04 + S05 complete
  command_outline: ALTER TABLE sandbox_tac.logical_unit DROP COLUMN authority, DROP COLUMN canonical_address_format_version;
  expected: columns removed; 76 sandbox rows preserved
  recovery: re-apply S04 + S05

cascade_concerns: NONE — no FKs target the new columns or the alias table
rollback_authorization_at_production_time: separate Đ32 + G-4 sign-off; not pre-authorized

§10 — Dry-Run Pass/Fail Acceptance Procedure

pre-dry-run_gates:
  - Phase α DDL authored, GPT-reviewed
  - dry-run env up and reachable
  - baseline restored and verified (row counts, schema diff vs production)
  - scenario matrix (§4) frozen

during_dry_run:
  - execute scenarios in numbered order
  - record per-scenario: status, evidence (queries + outputs), timestamp
  - any FAIL: stop, record evidence, do NOT continue automatically

post_dry_run_gates_for_acceptance:
  - 26/26 PASS
  - smoke tests PASS
  - rollback tests PASS
  - results recorded in a Phase α HB-equivalent closure document
  - GPT review of closure document

dry_run_artefacts_to_produce_(future session):
  - schema/cutter_governance_alias_phase_alpha_v0.1.sql           (DDL artefact; sha256 recorded)
  - schema/tac_logical_unit_phase_alpha_additive.sql              (DDL artefact; sha256 recorded)
  - helpers/phase_alpha_harness.py                                (scenario harness; sha256 recorded)
  - logs/phase_alpha_results.json                                 (per-scenario PASS/FAIL)
  - logs/phase_alpha_run.log
  - SHA256SUMS
  - location: /opt/incomex/backups/dieu44_phase_alpha_<date>/

§11 — Pre-Production Readiness After Dry-Run

sequence_after_26_of_26_PASS:
  1. Phase α HB-equivalent closure document (rollback dry-run closure)
  2. GPT review of the closure
  3. Phase α final readiness review (analog of v0.1 final-readiness/*)
  4. GPT review of final readiness
  5. Phase α production command-review package
  6. GPT review of command-review package
  7. explicit User production execution prompt
  8. production execution (separate session)
  9. production execution report
  10. GPT ratification

each_step_terminates_at_GPT_review_or_explicit_user_prompt: TRUE
agent_self_advance_at_any_step: PROHIBITED

§12 — Hard Boundaries

no_dry_run_executed_in_this_plan: TRUE
no_DDL_written: TRUE
no_SQL_executed: TRUE
no_baseline_restored_yet: TRUE
no_dry_run_env_provisioned_yet: TRUE
no_mutation: TRUE
no_change_to_production: TRUE
no_change_to_dry_run_HB_05_env: TRUE
no_phase_alpha_DDL_authoring_started: TRUE
no_production_migration_allowed: TRUE
output_form: phase_alpha_dry_run_planning_only

§13 — Cross-References

phase_α_design_master:    knowledge/dev/laws/dieu44-trien-khai/v0.2-design/dot-iu-cutter-v0.2-phase-alpha-design-master-2026-05-15.md
schema_design:            knowledge/dev/laws/dieu44-trien-khai/v0.2-design/dot-iu-cutter-v0.2-phase-alpha-canonical-address-schema-design-2026-05-15.md
alias_design:             knowledge/dev/laws/dieu44-trien-khai/v0.2-design/dot-iu-cutter-v0.2-phase-alpha-canonical-address-alias-design-2026-05-15.md
risk_review_plan:         knowledge/dev/laws/dieu44-trien-khai/v0.2-design/dot-iu-cutter-v0.2-phase-alpha-risk-review-plan-2026-05-15.md
design_report:            knowledge/dev/laws/dieu44-trien-khai/v0.2-design/dot-iu-cutter-v0.2-phase-alpha-design-report-2026-05-15.md
v0_1_rollback_test_plan_(pattern_baseline): knowledge/dev/laws/dieu44-trien-khai/implementation-planning/dot-iu-cutter-v0.1-p0-rollback-test-plan-2026-05-15.md
v0_1_HB_05_closure:        knowledge/dev/laws/dieu44-trien-khai/blocker-closure/dot-iu-cutter-v0.1-hb-05-rollback-dry-run-closure-2026-05-15.md
v0_1_HB_05_dry_run_env:    pg-dry-run-hb05-2026-05-15 (on VPS; RETAIN, do not reuse for Phase α dry-run)
fresh_backup_pattern_reference: /opt/incomex/backups/dieu44_exec_2026-05-15/   (v0.1 fresh backup; eligible as Phase α baseline if production state unchanged at dry-run setup time)

End of Phase α dry-run plan.

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.2-design/dot-iu-cutter-v0.2-phase-alpha-dry-run-plan-2026-05-15.md