KB-242C rev 2

dot-iu-cutter v0.2 — Phase α DDL Authoring Report (rev 2, 2026-05-15)

22 min read Revision 2
dieu44-trien-khaidot-iu-cutterv0.2phase-alphaddl-authoring-reportready-for-gpt-re-reviewrevision-22026-05-15

dot-iu-cutter v0.2 — Phase α DDL Authoring Report

document_path: knowledge/dev/laws/dieu44-trien-khai/v0.2-ddl-authoring/dot-iu-cutter-v0.2-phase-alpha-ddl-authoring-report-2026-05-15.md
revision: r2
supersedes: r1 (2026-05-15)
date: 2026-05-15
revised: 2026-05-16
author: Agent (Claude Code CLI, Opus 4.7 1M)
sovereign: User / anh Huyên
verifier: GPT (Đ32 standard path)
secondary: Opus
phase: v0.2 — Phase α DDL authoring CONSOLIDATED REPORT
ddl_authored: TRUE
ddl_executed: FALSE
mutation_performed: FALSE
dry_run_started: FALSE
production_migration_allowed: FALSE

§0 — Revision 2 (response to GPT REVISION_REQUIRED)

gpt_verdict_on_r1: REVISION_REQUIRED — dry-run NOT opened

blocking_issue (as raised by GPT):
  r1 added `authority text DEFAULT 'draft'` at column-add time. In PostgreSQL,
  ADD COLUMN with a constant DEFAULT makes every pre-existing row immediately
  read the default value. Therefore `UPDATE ... WHERE authority IS NULL`
  (Step 3) would match ZERO rows → the BR-4 lifecycle_status → authority
  backfill silently no-ops, and sandbox rows would read 'draft' instead of
  remaining NULL. The end value coincidentally still landed on 'draft' for
  the all-'draft_only' dataset, but the mapping never executed and any
  active/retired row would be mis-stamped.

correction_applied_in_r2 (exactly as GPT specified):
  public.tac_logical_unit:
    Step 2  ALTER TABLE ... ADD COLUMN IF NOT EXISTS authority text;   -- NO DEFAULT
    Step 3  UPDATE ... 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  ALTER TABLE ... ALTER COLUMN authority SET DEFAULT 'draft';
  sandbox_tac.logical_unit:
    Step 6  ALTER TABLE ... ADD COLUMN IF NOT EXISTS authority text;   -- NO DEFAULT
    (no backfill — 76 rows stay NULL)
    Step 7  ALTER TABLE ... ALTER COLUMN authority SET DEFAULT 'draft';

files_revised_to_r2:
  1. dot-iu-cutter-v0.2-phase-alpha-ddl-draft-2026-05-15.sql.md         → r2
  2. dot-iu-cutter-v0.2-phase-alpha-ddl-authoring-report-2026-05-15.md  → r2 (this file)
  3. dot-iu-cutter-v0.2-phase-alpha-ddl-verification-plan-2026-05-15.md → r2
  4. dot-iu-cutter-v0.2-phase-alpha-rollback-draft-2026-05-15.md        → r2 (revision-tracking + step-renumber refs; DROP-COLUMN reversal logic unchanged)

accepted_parts_unchanged (per GPT):
  fn_tac_birth_gate_lu inspection · single transaction · alias placement
  cutter_governance.canonical_address_alias · no cross-schema FK · no CHECK in
  Phase α · no sandbox alias table · no identity_profile cleanup ·
  canonical_address_format_version column (NOT NULL DEFAULT — intentionally
  unchanged) · canonical_address_alias general shape

still_no_execution: authoring only — no DDL run, no dry-run, no production mutation, no migration

§1 — Files Created / Revised (this lineage)

  1. …/dot-iu-cutter-v0.2-phase-alpha-fn-tac-birth-gate-lu-inspection-2026-05-15.md — B-DDL-3 read-only inspection of the trigger function (r1; accepted by GPT; unchanged)
  2. …/dot-iu-cutter-v0.2-phase-alpha-ddl-draft-2026-05-15.sql.md — Phase α DDL draft (revised → r2: authority added without default; backfill before SET DEFAULT; 9 steps)
  3. …/dot-iu-cutter-v0.2-phase-alpha-ddl-verification-plan-2026-05-15.md — verification queries + pre-backfill sanity checks (revised → r2: mechanism notes; expectations unchanged because they were already correct)
  4. …/dot-iu-cutter-v0.2-phase-alpha-rollback-draft-2026-05-15.md — rollback SQL draft (revised → r2: revision tracking + step-renumber references; DROP COLUMN reversal logic unaffected)
  5. (this file) — authoring report (revised → r2)

§2 — fn_tac_birth_gate_lu Inspection Result

(Unchanged from r1 — GPT accepted this part. Reproduced for self-containment.)

inspection_done: YES
mutation: NO

function_attributes:
  language:  plpgsql
  security:  SECURITY DEFINER
  trigger:   BEFORE INSERT OR UPDATE FOR EACH ROW

what_function_references_inside_body:
  - NEW.canonical_address       (regex validation against canonical-address-v1 grammar)
  - NEW.doc_code                (non-empty check)
  - NEW.parent_id               (existence + doc_code parity)
  - NEW.section_type            (FK + vocab activity check via tac_section_type_vocab)
  - NEW.owner                   (non-empty check)
  - NEW.sort_order              (>= 0 check)
  - NEW.updated_at              (WRITTEN: NEW.updated_at := now())

what_function_does_NOT_reference:
  - authority                                    (NEW Phase α column — invisible to trigger)
  - canonical_address_format_version             (NEW Phase α column — invisible to trigger)
  - public.tac_logical_unit.lifecycle_status     (NOT used; the trigger reads tac_section_type_vocab.lifecycle_status which is a DIFFERENT field for a DIFFERENT vocabulary)

conflict_assessment: NONE
  - ADD COLUMN / ALTER COLUMN SET DEFAULT operations do not fire the trigger (metadata DDL)
  - backfill UPDATE (Step 3) fires the trigger; re-validates existing canonical_address (passes; value unchanged) and all other gates (pass)
  - documented side effect: NEW.updated_at bumped to now() on all 86 backfilled rows (accepted as normal UPDATE semantics)

r2_note: the r2 add-without-default change does NOT alter the trigger surface — only Step 3 (the UPDATE) fires the row trigger, exactly once per row, exactly as analysed in r1.

informational_findings (NOT blocking Phase α):
  - trigger regex hardcodes "D38-" prefix — future doc ids will not validate at write time; v0.3+ followup
  - regex grammar matches BR-5 canonical-address-v1 — positive corroboration

risk_to_phase_α_from_this_trigger: NONE

§3 — DDL Summary (r2)

3.1 Operations (in execution order; all inside one single transaction)

Step Operation Target Risk
1 ADD COLUMN canonical_address_format_version text NOT NULL DEFAULT 'canonical-address-v1' public.tac_logical_unit LOW (additive; DEFAULT intentionally backfills 86 rows)
2 ADD COLUMN authority text (NO DEFAULT) — existing rows → NULL public.tac_logical_unit LOW (additive; nullable; r2 fix)
3 UPDATE … SET authority = CASE lifecycle_status … END WHERE authority IS NULL public.tac_logical_unit (86 rows) STANDARD (fires trigger; documented updated_at side effect)
4 ALTER COLUMN authority SET DEFAULT 'draft' — future rows only public.tac_logical_unit LOW (metadata; does not rewrite existing rows; r2 fix)
5 ADD COLUMN canonical_address_format_version text NOT NULL DEFAULT 'canonical-address-v1' sandbox_tac.logical_unit LOW (mirror; DEFAULT backfills 76 rows)
6 ADD COLUMN authority text (NO DEFAULT) — 76 rows stay NULL sandbox_tac.logical_unit LOW (mirror; no backfill; r2 fix)
7 ALTER COLUMN authority SET DEFAULT 'draft' — future rows only sandbox_tac.logical_unit LOW (metadata; 76 existing rows stay NULL; r2 fix)
8 CREATE TABLE IF NOT EXISTS cutter_governance.canonical_address_alias cutter_governance LOW (0 rows at create)
9 CREATE INDEX IF NOT EXISTS (4 btree indexes) cutter_governance.canonical_address_alias LOW (empty table)

r1 → r2 step delta: r1 step 2 split into r2 steps 2+4; r1 step 5 split into r2 steps 6+7; the intentional "no sandbox backfill" NO-OP is now expressed as the absence of an UPDATE between steps 6 and 7. r1 step 7 (alias table) → r2 step 8; r1 step 8 (indexes) → r2 step 9. Step 3 = backfill is preserved (fn-inspection + verification pre-backfill checks still reference Step 3).

3.2 Posture statements

single_transaction: YES (BEGIN/COMMIT)
on_error_stop_required_at_dispatch: TRUE (caller dispatches with --single-transaction --set ON_ERROR_STOP=1)
idempotency_guards: ADD COLUMN IF NOT EXISTS, CREATE TABLE IF NOT EXISTS, CREATE INDEX IF NOT EXISTS, UPDATE WHERE authority IS NULL, ALTER COLUMN SET DEFAULT (re-set = same value)
r2_idempotency_correctness: because authority is added WITHOUT a default, a re-run finds all rows already non-NULL → Step 3 matches 0 rows (safe); SET DEFAULT re-applies the same value (safe). Under r1 a first run would have skipped the backfill entirely — the defect GPT blocked.
re-applicability_after_a_clean_first_run: 0 schema changes, 0 row updates (full no-op)
re-applicability_after_a_mid_transaction_failure: ROLLBACK restores pre-state; column never half-added; next run starts clean
COMMENT_ON_statements_present: YES (column/table docstrings; benignly overwrite on re-run)

GPT_decided_options_(applied):
  Q1 alias placement → cutter_governance.canonical_address_alias
  Q2 FK posture → soft uuid reference; no cross-schema FK
  Q3 authority CHECK → none in Phase α
  Q4 sandbox alias → omit
  Q5 dry-run env → fresh sibling env (caller's responsibility at dry-run time)
  Q6 jsonb cleanup → defer

NOT_in_the_DDL_(consistent_with_design + GPT_Q1-Q6 + r2 fix):
  - no DEFAULT at authority column-add time (r2 — set in a later step after backfill)
  - no CHECK constraint on authority
  - no FK from alias.target_unit_id to tac_logical_unit.id
  - no sandbox alias table
  - no sandbox authority backfill (76 rows stay NULL)
  - no identity_profile jsonb cleanup
  - no NOT NULL promotion on authority
  - no change to existing canonical_address column or v0.1 cutter_governance tables

§4 — Verification Plan Summary (r2)

total_post_DDL_verification_queries: 16  (V-α-1 .. V-α-16)
total_pre_backfill_sanity_checks:     3  (Q-CHK-1, Q-CHK-2, Q-CHK-3)
overall_pass_threshold: ALL queries PASS

r2_verification_expectation (as confirmed by GPT — these were already the r1 expectations; r2 makes them ACTUALLY ACHIEVED by the corrected mechanism):
  public.tac_logical_unit.authority:   draft=86, enacted=0, runtime=0, null=0
  sandbox_tac.logical_unit.authority:  null=76
  (r1 would have produced authority='draft' on ALL 86 *without running the mapping*,
   and sandbox authority='draft'×76 instead of null×76 — the mechanism is what r2 fixes.)

what_the_queries_assert:
  V-α-1 .. V-α-5   structural: new columns + alias table + alias indexes
  V-α-6, V-α-7     authority + format_version distribution on production tac_logical_unit
  V-α-8, V-α-9     sandbox mirror distribution (V-α-8: authority NULL × 76)
  V-α-10           alias table empty
  V-α-11, V-α-12   existing canonical_address column + UNIQUE constraint unchanged
  V-α-13           v0.1 cutter_governance tables unchanged
  V-α-14, V-α-15   row counts of tac_logical_unit + sandbox_tac.logical_unit unchanged
  V-α-16           schema diff against pre-snapshot: ADDITIONS ONLY

failure_response: STOP + capture evidence + rollback per the rollback draft + escalate to GPT
no_verification_executed_in_this_authoring_session: TRUE

§5 — Rollback Summary (r2)

rollback_scope: matches Phase α DDL exactly (LIFO order)
  R-1: DROP TABLE cutter_governance.canonical_address_alias
  R-2: DROP COLUMN sandbox_tac.logical_unit.authority + canonical_address_format_version
  R-3: DROP COLUMN public.tac_logical_unit.authority + canonical_address_format_version

r2_note: DROP COLUMN removes the column AND its attached DEFAULT in one operation,
  so the r2 "SET DEFAULT" steps need no separate reversal. Rollback logic is
  unchanged from r1; only revision tracking + step-number references updated.

data_loss_considerations:
  alias_table: 0 rows in Phase α → no loss
  sandbox_tac: 76 NULL authority values (r2: genuinely NULL, not 'draft') + 76 DEFAULT format_version values → trivially reproducible
  public.tac_logical_unit:
    - authority='draft' for 86 rows → deterministically reproducible from BR-4 mapping (now actually executed by Step 3)
    - format_version='canonical-address-v1' for 86 rows → reproducible via DEFAULT on re-apply
    - updated_at bump from backfill → ORIGINAL TIMESTAMPS LOST after rollback; recoverable from pre-Phase-α backup (.dump)

rollback_dispatch_authorization:
  - Đ32 standard path + G-4 Custodian co-sign
  - sovereign User explicit acknowledgement
  - separate explicit prompt naming: (a) operation = rollback, (b) environment, (c) triggering verification failure
  - agent_self_authorize_rollback: PROHIBITED

cheaper_alternative_for_in_transaction_failure:
  - dispatch Phase α DDL with --single-transaction --set ON_ERROR_STOP=1
  - any failure aborts atomically; no separate rollback script needed
  - the §3 rollback applies only to POST-COMMIT problems detected by V-α-1..16

§6 — Deviations from Design

deviations_from_phase_α_design_master / schema_design / alias_design: NONE
  (the r2 add-without-default → backfill → set-default ordering is the
   PostgreSQL-correct *implementation* of the design's stated intent —
   "existing rows backfilled from lifecycle_status; default for future rows" —
   not a deviation. r1 was an implementation defect, not a design change.)

confirmations_of_design_decisions_in_DDL:
  - alias table placement: cutter_governance.canonical_address_alias                ✓ (design: master §5.3, alias §7.2; GPT Q1)
  - soft uuid reference (no PG FK): alias.target_unit_id uuid NOT NULL              ✓ (design: alias §7.1; GPT Q2)
  - no CHECK constraint on authority                                                 ✓ (design: schema §3.1; GPT Q3)
  - no sandbox alias table                                                           ✓ (design: master §5.3; GPT Q4)
  - no jsonb cleanup                                                                  ✓ (design: master §6 Q4; GPT Q6)
  - authority nullable; NOT NULL deferred to Phase β                                  ✓ (BR-4 closure)
  - authority backfilled from lifecycle_status BEFORE default is set                 ✓ (r2 — PG-correct realisation of BR-4 backfill rule)
  - canonical_address_format_version NOT NULL DEFAULT 'canonical-address-v1'         ✓ (BR-5 closure — intentionally column-add backfilled; unchanged)
  - sandbox authority no backfill in Phase α (76 rows genuinely NULL)                ✓ (BR-7 Option II; design alias §6.2; r2 makes this actually true)
  - existing canonical_address SSOT preserved unchanged                               ✓ (Option D)
  - single-transaction wrap                                                            ✓ (design master §7)

additions_beyond_design_(minor; not deviations):
  - COMMENT ON column/table statements added for self-documentation (benign metadata)
  - SET LOCAL statement_timeout / lock_timeout / idle_in_transaction_session_timeout added for safety
  - trailing in-transaction SELECT added for live verification (informational, not pass/fail logic; r2 expanded to break out draft/enacted/runtime/null)
  - 4 explicit btree indexes named per dry-run plan §10 recommendation (PK is implicit)

§7 — Blockers Before Dry-Run

B-DR-1: GPT RE-review of this r2 DDL authoring package (4 revised docs + 1 accepted inspection)
  status: PENDING (this r2 report is the trigger)

B-DR-2: Phase α DDL draft frozen + sha256 captured at GPT-review-time
  status: PENDING — sha256 MUST be captured on the r2 §2 body (r1 superseded)

B-DR-3: explicit GPT or User prompt authorizing dry-run authoring lane
  status: NOT YET issued (GPT verdict on r1 was REVISION_REQUIRED — dry-run remains closed until r2 PASS)

B-DR-4: fresh dry-run env provisioned (pg-dry-run-v0.2-phase-alpha-<date>)
  status: NOT YET (separate session)

B-DR-5: baseline backup taken / verified
  status: NOT YET (separate session)

B-DR-6: dry-run scenario harness authored (Phase α HB-equivalent harness; mirroring HB-05 hb05_harness.py pattern)
  status: NOT YET (separate session)

§8 — Blockers Before Production

(See risk review plan §6 for full enumeration. Unchanged by r2.)

B-PROD-1: dry-run 26/26 PASS
B-PROD-2: Phase α HB-equivalent closure document
B-PROD-3: Phase α final readiness review
B-PROD-4: Phase α command-review package
B-PROD-5: GPT review of (closure + readiness + command-review)
B-PROD-6: fresh production-bound backup < 60 min
B-PROD-7: explicit User production execution prompt
B-PROD-8: separate session for production execution
B-PROD-9: pg-dry-run-hb05-2026-05-15 v0.1 env still UP
B-PROD-10: production SSOT canonical_address column unchanged
B-PROD-11: dot_tools 991/992 still active and unchanged

§9 — Readiness for GPT Re-Review

phase_α_ddl_authoring_status: revised_r2_ready_for_gpt_re_review

all_authoring_docs_present: TRUE (4 revised to r2 + 1 accepted inspection at r1)
r1_blocking_issue_addressed: TRUE (PG fast-default pitfall fixed exactly per GPT's required pattern)
internally_consistent_with_design_package: TRUE
internally_consistent_with_BR_closures: TRUE
internally_consistent_with_GPT_Q1_Q6_decisions: TRUE
deviations_from_design: NONE (r2 is the PG-correct implementation of the unchanged design intent)

ddl_authored: TRUE
ddl_executed: FALSE
mutation_performed: FALSE
dry_run_started: FALSE
production_migration_allowed: FALSE
agent_self_advance_to_execution: PROHIBITED

can_dry_run_be_re_reviewed_now:
  the DRY-RUN LANE remains CLOSED. r2 is submitted for GPT RE-review of the
  authoring package only. Dry-run authoring may be opened ONLY after GPT
  issues a PASS verdict on r2 AND an explicit prompt (B-DR-3) authorizes the
  dry-run lane. The agent does NOT self-open it.

what_gpt_re_review_should_decide:
  - confirm the r2 add-without-default → backfill → set-default ordering clears REVISION_REQUIRED
  - PASS / further-revise the r2 authoring docs
  - capture sha256 of the frozen r2 DDL draft (caller extracts §2 of the .sql.md into a real .sql file and sha256s it at review-time)
  - if PASS: issue the explicit prompt to enter the Phase α dry-run authoring lane

§10 — Negative Confirmations

ddl_executed_anywhere: FALSE
psql_run: FALSE  (the only DB access in the lineage was the read-only fn_tac_birth_gate_lu inspection via pg_get_functiondef + pg_get_triggerdef; no DDL or DML; r2 revision was pure document authoring)
mutation_performed: FALSE
dry_run_started: FALSE
dry_run_env_provisioned: FALSE
production_migration_allowed: FALSE
sister_tables_touched: FALSE
v0_1_cutter_governance_tables_touched: FALSE
sandbox_tac_data_modified: FALSE
trigger_modified: FALSE
function_modified: FALSE
identity_profile_jsonb_modified: FALSE
deploy_performed: FALSE
CUT_VERIFY_run: FALSE
output_form: phase_alpha_ddl_authoring_consolidated_report_r2

§11 — Sign-Off Block

agent (author):                  Claude Code CLI / Opus 4.7 1M    action: r2 revision of 4 Phase α DDL authoring docs (DDL draft + verification plan + rollback draft + this report) per GPT REVISION_REQUIRED
verifier (Đ32 standard path):    GPT                              action: Phase α DDL authoring RE-review (r2) — PENDING
secondary (record keeping):      Opus                              action: r2 docs stored in Agent Data — DONE
sovereign (authorizing party):   User / anh Huyên                  action: explicit prompt for Phase α dry-run authoring — PENDING (gated on r2 PASS)
council_status:
  Đ0-G:  authority semantics — ratified at BR-4 closure
  Đ24:   canonical-address-v1 grammar — ratified at BR-5 closure
  Đ32:   standard path — r1 REVISION_REQUIRED; r2 pending re-review

§12 — Cross-References

fn_inspection:        knowledge/dev/laws/dieu44-trien-khai/v0.2-ddl-authoring/dot-iu-cutter-v0.2-phase-alpha-fn-tac-birth-gate-lu-inspection-2026-05-15.md  (r1; accepted)
ddl_draft:            knowledge/dev/laws/dieu44-trien-khai/v0.2-ddl-authoring/dot-iu-cutter-v0.2-phase-alpha-ddl-draft-2026-05-15.sql.md  (r2)
verification_plan:    knowledge/dev/laws/dieu44-trien-khai/v0.2-ddl-authoring/dot-iu-cutter-v0.2-phase-alpha-ddl-verification-plan-2026-05-15.md  (r2)
rollback_draft:       knowledge/dev/laws/dieu44-trien-khai/v0.2-ddl-authoring/dot-iu-cutter-v0.2-phase-alpha-rollback-draft-2026-05-15.md  (r2)

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
dry_run_plan:          knowledge/dev/laws/dieu44-trien-khai/v0.2-design/dot-iu-cutter-v0.2-phase-alpha-dry-run-plan-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

br_4_closure:          knowledge/dev/laws/dieu44-trien-khai/v0.2-planning/dot-iu-cutter-v0.2-br-4-authority-backfill-rule-closure-2026-05-15.md
br_5_closure:          knowledge/dev/laws/dieu44-trien-khai/v0.2-planning/dot-iu-cutter-v0.2-br-5-canonical-address-v1-ratification-closure-2026-05-15.md
br_4_5_closure_report: knowledge/dev/laws/dieu44-trien-khai/v0.2-planning/dot-iu-cutter-v0.2-br-4-5-closure-report-2026-05-15.md
br_2_3_7_report:       knowledge/dev/laws/dieu44-trien-khai/v0.2-planning/dot-iu-cutter-v0.2-br-2-3-7-discovery-report-2026-05-15.md

v0_1_production_handoff: knowledge/dev/laws/dieu44-trien-khai/execution/dot-iu-cutter-v0.1-production-handoff-status-2026-05-15.md
v0_1_HB_05_dry_run_DDL_(pattern_reference): /opt/incomex/backups/dieu44_hb05_2026-05-15/schema/cutter_governance_dryrun_v0.1.sql  (sha256 9eb032e2…)

End of Phase α DDL authoring report (revision 2).

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.2-ddl-authoring/dot-iu-cutter-v0.2-phase-alpha-ddl-authoring-report-2026-05-15.md