KB-2807

dot-iu-cutter v0.2 — BR-4 Authority Backfill Rule Design (2026-05-15)

18 min read Revision 1
dieu44-trien-khaidot-iu-cutterv0.2br-4authoritybackfilldesignplanning-onlydieu0-g2026-05-15

dot-iu-cutter v0.2 — BR-4 Authority Backfill Rule Design

document_path: knowledge/dev/laws/dieu44-trien-khai/v0.2-planning/dot-iu-cutter-v0.2-br-4-authority-backfill-rule-design-2026-05-15.md
revision: r1
date: 2026-05-15
author: Agent (Claude Code CLI, Opus 4.7 1M)
sovereign: User / anh Huyên
verifier: GPT (Đ32 HIGH-risk path — anticipated)
secondary: Opus
governance_review_required: Đ0-G (authority semantics) + Đ32 (risk class)
phase: v0.2 planning — BR-4 rule design (planning only)
BR_4_status: pending_gpt_review
mutation_performed: false
ddl_written: false
backfill_executed: false

§1 — Purpose

Choose the authority-backfill rule that will populate public.tac_logical_unit.authority for the 86 existing rows when v0.2 Phase α adds the column. This document proposes the rule and recommends one candidate for Đ0-G review; it does NOT execute any backfill, write any DDL, or modify any row.

Authority semantics (per v0.1 P0-1 §7):

authority enum (Đ24 Step 1 ratified):  enacted | draft | runtime
authority_precedence_in_retrieval:     enacted > draft > runtime
mapping_to_Đ0-G_birth_gate:            enacted = official; draft = controlled provisional; runtime = derived/operational

§2 — Source Evidence (verbatim from this session's read-only discovery)

2.1 public.tac_lu_lifecycle_vocab (3 entries)

code         | name                | description                                                      | sort_order
-------------|---------------------|------------------------------------------------------------------|-----------
active       | Hoạt động           | Logical unit có ≥1 version enacted                              | 10
draft_only   | Chỉ bản nháp        | Chưa từng có version enacted                                    | 20
retired      | Đã rút              | Quyết định retire qua change-set + APR                          | 30

2.2 Lifecycle distribution on public.tac_logical_unit

lifecycle_status  | count
------------------|------
draft_only        | 86      ← 100% of rows

2.3 Lifecycle distribution on sandbox_tac.logical_unit

lifecycle_status  | count
------------------|------
draft_only        | 76      ← 100% of rows

2.4 doc_code distribution on public.tac_logical_unit

doc_code  | count
----------|------
DIEU-28   | 27
DIEU-32   | 23
DIEU-35   | 36

2.5 Implication for backfill choice

Because all 86 rows are currently lifecycle_status='draft_only', every rule below produces the same concrete result on existing data: authority='draft' for all 86 rows.

The rules differ only in:

  • semantic clarity (what does the rule say about why a row gets that authority?)
  • generalization (what happens when lifecycle_status transitions to active or retired? what happens for future rows?)
  • governance coupling (how tightly does authority track lifecycle?)

§3 — Candidate Rules

3.1 Candidate A — Uniform DEFAULT 'draft' for all existing rows

rule:
  on_backfill: UPDATE public.tac_logical_unit SET authority='draft' WHERE authority IS NULL
  on_new_writes: column DEFAULT 'draft' (DDL-level) until further governance lands
  for_existing_86_rows: all 86 → 'draft'

semantics:
  - "authority is unknown at column-introduction time; assume the most-conservative non-enacted value"
  - decoupled from lifecycle_status

generalization:
  - future rows with lifecycle_status='active' would still default to 'draft' unless caller explicitly sets authority
  - retired rows behavior: untouched at this rule level

dieu0_g_alignment:
  - explicit "draft" floor — Đ0-G can adjudicate up to 'enacted' once enactment occurs

pros:
  - simplest rule; trivially reviewable; trivially reversible
  - no dependency on lifecycle_status vocabulary stability
  - matches v0.1 P0-1 §4.1 "nullable initially → backfill" pattern exactly

cons:
  - decouples authority from the existing lifecycle_status semantic (which has an `active = ≥1 version enacted` definition that maps perfectly to authority='enacted')
  - if/when a row's lifecycle_status transitions to `active`, the authority column does NOT auto-update — a separate write-side or trigger update is needed
  - introduces a latent inconsistency risk: lifecycle='active' AND authority='draft' for a transitioned row

risks:
  - lifecycle/authority drift across enactment transitions (medium-term)
  - reviewers in Phase β / γ might find rows where lifecycle says one thing and authority says another → forensic effort

required_governance:
  Đ0-G review confirming that uniform 'draft' floor is acceptable
  Đ32 risk class: standard

dry_run_requirement:
  - HB-equivalent dry-run includes a scenario "ADD COLUMN authority text NULL → UPDATE … SET authority='draft' WHERE … → backfill audit": rowcount 86 updated
  - rollback: UPDATE SET authority=NULL OR DROP COLUMN

3.2 Candidate B — Derive from lifecycle_status mapping

rule:
  mapping_table:
    lifecycle_status='draft_only'  → authority='draft'
    lifecycle_status='active'      → authority='enacted'
    lifecycle_status='retired'     → authority='enacted'   (rationale: retired means it WAS enacted; the retirement is tracked separately via lifecycle, not via authority)

  on_backfill:
    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

  on_new_writes: column DEFAULT 'draft' (DDL-level) for new rows; application/trigger updates authority on lifecycle transitions (FUTURE Phase β work)

  for_existing_86_rows: all 86 are 'draft_only' → 'draft' (same concrete result as Candidate A, but with a derived rule rather than a fiat value)

semantics:
  - "authority is derivable from existing governance state"
  - keeps a single conceptual model: lifecycle_status is the operational state; authority is the Đ0-G birth-gate classification, derived from operational state until Đ0-G rules expand

generalization:
  - future rows where lifecycle transitions to 'active' SHOULD have authority updated to 'enacted' (trigger or application-layer write — out of v0.2 scope; documented as Phase β FUTURE item)
  - the rule is forward-compatible if `tac_lu_lifecycle_vocab` adds more entries (each new vocab row needs an authority mapping)

dieu0_g_alignment:
  - explicit mapping from existing operational vocabulary → authority enum
  - Đ0-G can review the mapping table; future vocabulary additions go through Đ0-G

pros:
  - operationally semantically tight; same row content reads consistently across lifecycle_status and authority
  - generalizes when production data accumulates active/retired rows
  - keeps Đ0-G in the loop for any vocabulary expansion
  - matches the discovery finding (F-DISC-3) verbatim ("lifecycle_status serves a different concern" still true at the column level, but Candidate B explicitly bridges them via a documented mapping rather than ignoring the relationship)

cons:
  - slightly more design surface than Candidate A (a 3-entry mapping table to maintain)
  - retired → enacted mapping is a JUDGMENT CALL (could also map to a new authority value like 'retired'); requires Đ0-G ratification
  - couples authority to lifecycle_status vocab stability — if vocab evolves, mapping evolves

risks:
  - mis-classification of retired rows: if 'retired' should actually map to a distinct authority (not 'enacted'), every retired row gets the wrong value. Mitigation: defer 'retired'-row backfill; only backfill draft_only + active in v0.2 Phase α; deal with retired explicitly in Phase β.
  - lifecycle vocab expansion not handled: new vocab row added but mapping not yet updated → CASE returns NULL → column constraint (NOT NULL after v0.2 Phase α complete) violated. Mitigation: keep column NULL-able through v0.2 Phase α; add CHECK or NOT NULL only after full mapping ratified.

required_governance:
  Đ0-G review of the 3-entry mapping table (the retired→enacted mapping in particular)
  Đ24 cross-check that authority enum {enacted, draft, runtime} is still the canonical vocabulary
  Đ32 risk class: standard

dry_run_requirement:
  - HB-equivalent dry-run scenario S-B1: backfill on draft_only rows → all become 'draft'
  - HB-equivalent dry-run scenario S-B2: synthetic active row inserted → backfill produces 'enacted'
  - HB-equivalent dry-run scenario S-B3: synthetic retired row inserted → backfill produces 'enacted' (with Đ0-G confirmation that this is intended)
  - rollback: UPDATE SET authority=NULL OR DROP COLUMN

3.3 Candidate C — Derive from doc_code / canonical_address prefix regex

rule:
  on_backfill:
    rule_text:
      authority = CASE
        WHEN doc_code ~ '^DIEU-[0-9]+$' AND lifecycle_status='active' THEN 'enacted'
        WHEN doc_code ~ '^DIEU-[0-9]+$'                                THEN 'draft'
        WHEN doc_code ~ '^RUNTIME-'                                    THEN 'runtime'
        ELSE 'draft'
      END
    or alternative:
      authority = derived from canonical_address prefix:
        D{doc}-DIEU{N}-…  → law artifact (use lifecycle to discriminate draft vs enacted)
        runtime-…          → 'runtime'
        else               → 'draft'

  for_existing_86_rows:
    DIEU-28 (27) + DIEU-32 (23) + DIEU-35 (36) — all law artifacts, all lifecycle 'draft_only'
    → all 86 → 'draft'   (same concrete result as A and B)

semantics:
  - "authority is determined by what KIND of artifact the row represents"
  - introduces a third axis (artifact-kind) beyond lifecycle

pros:
  - distinguishes law artifacts from runtime artifacts at the rule level
  - useful when runtime IU data (which lives in information_unit, not tac_logical_unit) is later folded into the same authority vocabulary

cons:
  - more complex rule; depends on doc_code naming convention being stable
  - duplicates information already implicit in the table separation (tac_logical_unit = curated; information_unit + runtime tables = derived) — overengineering for v0.2 scope
  - canonical_address-prefix-based regex couples authority to the syntax that BR-5 is about to ratify — circular coupling

risks:
  - regex maintenance burden
  - doc_code convention drift (if new doc_code patterns emerge)
  - over-determines the rule when lifecycle_status alone is sufficient for v0.2 scope

required_governance:
  Đ0-G review (authority semantics)
  Đ24 review (doc_code naming convention stability)
  Đ32 risk class: standard but with elevated review attention due to regex complexity

dry_run_requirement: larger scenario set (mix of doc_code patterns + lifecycle statuses)

3.4 Candidate D — Hybrid fallback rule for future expansion

rule:
  on_backfill: Candidate B's mapping for the 3 currently-defined lifecycle values
  on_unknown_lifecycle_value (future vocab additions):
    - log to decision_backlog_entry (kind='authority_mapping_gap')
    - default to 'draft' (most conservative)
    - flag for Đ0-G review
  on_new_writes: column DEFAULT 'draft'; lifecycle-transition triggers update authority (Phase β FUTURE)

semantics:
  - Candidate B with explicit "we know we don't know everything" forward-compat guard

pros:
  - graceful degradation under future vocabulary expansion
  - explicit backlog signal when mapping needs Đ0-G attention
  - safest long-term

cons:
  - depends on decision_backlog_entry being live (it IS, post-v0.1 production execution)
  - slightly more design surface
  - the trigger / app-write side of "log to backlog" is out of v0.2 Phase α scope (Phase α is DDL + backfill only)

required_governance:
  - same as Candidate B
  - additionally: Đ32 sign-off on the fallback behavior

dry_run_requirement:
  - Candidate B's scenarios
  - + S-D1 synthetic future-vocab row → CASE returns NULL → fallback fires → 'draft' assigned, backlog entry created

recommended_rule: Candidate B — Derive from lifecycle_status mapping

rationale:
  1. For current data (86 rows × draft_only), Candidate B produces the same concrete result as Candidates A, C, and D: authority='draft'. No data risk on Day 1.
  2. Candidate B is the only rule that has a SEMANTICALLY TIGHT relationship between an existing governance column (lifecycle_status) and the new authority column. This is what discovery F-DISC-3 was asking for — a documented bridge between the two concerns, not a flat decoupling.
  3. Candidate A is too loose; ignores the existing lifecycle vocab.
  4. Candidate C is overengineered for v0.2 scope; couples authority to canonical_address syntax (which BR-5 is about to ratify) — circular.
  5. Candidate D is Candidate B + a fallback. The fallback is itself good design but out of Phase α scope (it requires a trigger or write-side hook, which is Phase β). Recommend ADOPTING Candidate D's fallback DOCUMENTATION but not implementing the trigger in v0.2 Phase α.

binding_choice:
  v0_2_phase_α:
    - use Candidate B mapping for backfill of 86 existing rows
    - all 86 become authority='draft' (because all 86 are lifecycle_status='draft_only')
    - column DEFAULT 'draft' for new writes
    - column nullable through Phase α (no NOT NULL constraint yet — added in Phase β after lifecycle-transition write hooks are wired)
  v0_2_phase_β_or_v0_3:
    - implement lifecycle-transition write hook (trigger or app-layer) per Candidate D
    - add NOT NULL constraint after backfill rule generalizes to all observed lifecycle values
    - revisit retired→enacted mapping with Đ0-G concrete sign-off

§5 — Đ0-G Review Notes (anticipated questions)

question_1: Is 'draft' the right floor for rows whose lifecycle is 'draft_only'?
answer_proposal: YES — `draft_only` means "never enacted"; 'draft' authority captures the controlled-provisional state exactly per P0-1 §7.

question_2: Should 'retired' map to 'enacted' (was-enacted) or to a new authority value 'retired'?
answer_proposal: 'enacted' for v0.2 — retirement is an operational lifecycle event, not a change in birth-gate authority. If Đ0-G later decides 'retired' is its own authority class, that is a vocabulary expansion handled via Đ24 + this mapping doc revision. NOTE: there are 0 retired rows in production today, so deferring the answer is safe.

question_3: Where does 'runtime' authority come from in v0.2?
answer_proposal: NOT from tac_logical_unit. Runtime artifacts live in `information_unit` and downstream tables; v0.2 Phase α scope EXCLUDES adding authority to information_unit. Runtime authority is a Phase β / v0.3 concern.

question_4: Is the authority column DEFAULT 'draft' (rather than NULL) safe?
answer_proposal: YES for new writes — 'draft' is the most-conservative non-enacted value; any writer that intends 'enacted' must set it explicitly. This matches the principle that promotion to 'enacted' requires explicit governance action.

question_5: What if doc_code conventions diverge (e.g., new artifact kinds beyond DIEU-{N})?
answer_proposal: Candidate B does not depend on doc_code; only on lifecycle_status. New doc_code conventions are orthogonal to authority and will not break Candidate B.

§6 — Effect on sandbox_tac

sandbox_tac.logical_unit (76 rows, all draft_only):
  Phase α plan: MIRROR the additive `authority` column (Option II from BR-7) — column added with same default 'draft'
  backfill_for_sandbox_in_phase_alpha: DEFER until BR-4 rule is ratified for sandbox data semantically
  recommendation: when ratified, apply Candidate B identically to sandbox (rule generalizes cleanly because sandbox has the same lifecycle_status='draft_only' shape)

risk_of_skipping_sandbox_backfill_in_phase_alpha: LOW
  - sandbox rows would have authority=NULL temporarily (column nullable through Phase α)
  - no production decision depends on sandbox authority value
  - sandbox backfill can be a one-line UPDATE done alongside Phase β

§7 — Hard Boundaries

no_DDL_written_in_this_document: TRUE
no_SQL_mutation: TRUE
no_ALTER_TABLE: TRUE
no_INSERT_UPDATE_DELETE_executed: TRUE  (rule TEXT in §3 is illustrative; nothing is run)
no_migration: TRUE
no_change_to_tac_logical_unit: TRUE
no_change_to_sandbox_tac: TRUE
no_change_to_cutter_governance: TRUE
no_design_advanced_beyond_BR_4: TRUE
no_deploy: TRUE
no_phase_alpha_design_started: TRUE
BR_4_status: pending_gpt_review (Đ0-G review request to be issued by GPT or User after this doc is reviewed)
output_form: br_4_rule_design_planning_only

§8 — Cross-References

br_2_doc: knowledge/dev/laws/dieu44-trien-khai/v0.2-planning/dot-iu-cutter-v0.2-br-2-identity-profile-jsonb-discovery-2026-05-15.md
br_3_doc: knowledge/dev/laws/dieu44-trien-khai/v0.2-planning/dot-iu-cutter-v0.2-br-3-canonical-address-reader-writer-inventory-2026-05-15.md
br_7_doc: knowledge/dev/laws/dieu44-trien-khai/v0.2-planning/dot-iu-cutter-v0.2-br-7-sandbox-tac-role-clarification-2026-05-15.md
br_2_3_7_consolidated: knowledge/dev/laws/dieu44-trien-khai/v0.2-planning/dot-iu-cutter-v0.2-br-2-3-7-discovery-report-2026-05-15.md
br_5_companion: knowledge/dev/laws/dieu44-trien-khai/v0.2-planning/dot-iu-cutter-v0.2-br-5-dieu24-canonical-address-v1-ratification-request-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-readiness-report-2026-05-15.md
v0_1_p0_1_design (baseline for authority enum): knowledge/dev/laws/dieu44-trien-khai/migration-design/dot-iu-cutter-v0.1-p0-1-canonical-address-migration-design-2026-05-15.md
v0_2_scope_backlog: knowledge/dev/laws/dieu44-trien-khai/planning/dot-iu-cutter-v0.2-scope-backlog-2026-05-15.md
reconciliation_report: knowledge/dev/laws/dieu44-trien-khai/v0.2-planning/dot-iu-cutter-v0.2-canonical-address-reconciliation-report-2026-05-15.md

End of BR-4 authority backfill rule design.

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.2-planning/dot-iu-cutter-v0.2-br-4-authority-backfill-rule-design-2026-05-15.md