KB-7DC3

dot-iu-cutter v0.5 — Lifecycle Enactment Design · Design Options Analysis (G3 PASS · OPT-E1 RECOMMENDED) (doc 3 of 6)

22 min read Revision 1
dot-iu-cutterv0.5lifecycle-enactment-designdesign-options-analysisopt-e1-recommendedopt-e2-not-recommendedopt-e3-rejected-anti-doctrinefn-iu-enact-new-canonical-secdefopen-questions-oq1-to-oq7dieu442026-05-20

dot-iu-cutter v0.5 — Lifecycle Enactment Design · Design Options Analysis

doc 3 of 6 · 2026-05-20 · ARCHITECTURE OPTIONS, NO IMPLEMENTATION

phase             : G3 — design options analysis
outcome           : PASS — OPT-E1 RECOMMENDED ; OPT-E2 NOT RECOMMENDED ;
                    OPT-E3 REJECTED (anti-doctrine) ;
                    7 open questions enumerated (OQ-1..OQ-7)
production_mutation : NONE

0. Scope of this document

Given the live-lifecycle constraints established in [[dot-iu-cutter-v0-5-01-live-lifecycle-survey-2026-05-20]] §8 and the prior-art landscape in [[dot-iu-cutter-v0-5-02-existing-lifecycle-docs-code-review-2026-05-20]], this document compares architectural options for moving the 60 ICX-CONST IUs from 'draft' to a canonical enacted state.

Three options are evaluated. Direct UPDATE under any role is dismissed at the outset: forbidden by C-1 (gateway fail-closed), F-1, F-3, F-4, and rejected by Pack 22-P3 doctrine.

For each option this doc records: scope, gateway compatibility, audit posture, risk profile, blast radius, reversibility, time-to-deliver, and a verdict. Then OQ-1..OQ-7 enumerate the design decisions that MUST be ruled on before implementation can proceed.

1.1 Scope

A new SECURITY DEFINER function public.fn_iu_enact(...) owned by directus. The function:

  1. Sets app.canonical_writer='fn_iu_enact' (txn-local).
  2. Validates input, FSM transition, vocab, and structural invariants.
  3. Performs UPDATE on public.information_unit (lifecycle_status) and on the IU's current version-anchor row in public.unit_version (lifecycle_status + enacted_at).
  4. Writes an audit row into a new public.iu_lifecycle_log (UUID-keyed).
  5. Returns a JSONB result describing the transition.

Configuration:

  • ADD 'fn_iu_enact' to dot_config.iu_create.gateway.allowed_marker_values (single-row UPDATE).
  • SEED dot_config.iu_enact.* namespace (mirrors iu_create.gateway.* self-description pattern).
  • GRANT EXECUTE on the function to cutter_exec (and optionally workflow_admin).

Companion DDL:

  • New tables: public.iu_lifecycle_vocab (4 rows seeded from TAC), public.iu_lifecycle_log.
  • New triggers: trg_iu_enacted_immut (mirrors fn_law_enacted_immutable), trg_uv_enacted_immut (mirrors fn_tac_enacted_immut).
  • One in-scope patch to public.fn_iu_apply_edit_draft to remove the global "uniform lifecycle_status" coupling (see OQ-1).

Caller:

  • New Python adapter cutter_agent/lifecycle_enact_adapter.py loops fn_iu_enact(...) over the 60 ICX-CONST canonical_addresses; one call per IU, each its own atomic transaction.

1.2 Gateway compatibility

gateway_change_required : 1 dot_config row UPDATE (allowed_marker_values
                          append 'fn_iu_enact')
gateway_function_change : NONE (guard function is unchanged; reads
                          allowed_marker_values from dot_config)
guard_disable_required  : NONE
exempt_marker_required  : NONE
on_doctrine             : YES (specific named canonical writer; not
                          generic exemption; matches Pack 22-P3 §C.3
                          "Exempt list phải hẹp")

1.3 Audit posture

primary_audit:
  table  : public.iu_lifecycle_log (NEW, UUID-keyed)
  per_call_rows : 1
  retention : permanent
  fields : id, unit_id, canonical_address, from_status, to_status,
           transition_type, reason, performed_by, performed_at,
           review_decision_id (soft-FK to cutter_governance.review_decision),
           change_set_id (soft-FK to cutter_governance.cut_change_set),
           tool_revision, metadata jsonb
governance_link:
  - p_review_decision_id  REQUIRED (a NEW review_decision for enactment,
                           distinct from CUT's review_decision)
  - p_change_set_id       OPTIONAL (if a NEW change_set is recorded
                           for enactment; OQ-3)
detection:
  - any UPDATE on enacted rows is blocked by trg_iu_enacted_immut /
    trg_uv_enacted_immut (mirroring tac/law patterns)

1.4 Risk profile

breaking_changes:
  - fn_iu_apply_edit_draft GLOBAL coupling MUST be patched (OQ-1).
    Without the patch, every edit-draft application across the database
    fails the moment the first UV transitions to 'enacted'.
  - trg_uv_enacted_immut is NEW on public.unit_version. If any existing
    code path UPDATEs a UV body after it is enacted, it will start to fail.
    Current callers (fn_iu_apply_edit_draft) create NEW UV rows; they do
    NOT UPDATE existing UVs. So no live breakage expected. Still: design
    must list the trigger as a behavioral change in the gateway README.
  - L1 birth-gate P-pub1/P-pub2 PILOT warnings: enactment may want to
    enforce these strictly. If yes, the 60 ICX-CONST IUs need their
    identity_profile patched to include 'publication_authority_ref'
    before enactment. See OQ-7.

reversibility:
  - Pre-enact: trivial (no rows touched).
  - Post-enact: enactment is INTENDED to be immutable.
                Designed reversal: 'enacted' → 'retired' (via a future
                fn_iu_retire) or 'enacted' → 'superseded' (via a future
                fn_iu_supersede when a new version is enacted).
                Direct 'enacted' → 'draft' rollback is NOT supported.
  - Emergency rollback procedure: see
    [[dot-iu-cutter-v0-5-05-grant-verification-rollback-plan-2026-05-20]] §6

blast_radius:
  - Schema: +2 tables (iu_lifecycle_vocab, iu_lifecycle_log)
  - Functions: +3 (fn_iu_enact, fn_iu_enacted_immut, fn_uv_enacted_immut)
  - Triggers: +2 (trg_iu_enacted_immut, trg_uv_enacted_immut)
  - dot_config: +1 UPDATE + N INSERTs in iu_enact.* namespace
  - Patches: 1 (fn_iu_apply_edit_draft, scoped function body change)
  - cutter_agent: +1 module
  - Rows touched on initial enactment: 60 IU + 60 UV + 60 log = 180 rows

1.5 Time-to-deliver

authoring_effort : moderate (function bodies + tests; ≈3-4 hours of macro work
                   for one experienced agent OR one full xhigh macro)
review_burden    : single command-review package over this design + the
                   DDL/PL/pgSQL bodies
execution_risk   : LOW given the design composes well-tested patterns
                   (tac_enacted_immut, law_enacted_immutable, gateway
                   marker mechanism); the only NOVEL element is the
                   fn_iu_apply_edit_draft patch

1.6 Verdict

RECOMMENDED. OPT-E1 is the only option that:

  • Preserves Pack 22-P3 doctrine (specific named canonical writer, no generic exemption).
  • Adopts the canonical TAC 4-state vocab uniformly.
  • Provides a UUID-compatible audit log usable by the 60-IU CUT and by future IU enactments.
  • Builds enacted-immutability protection symmetric with TAC/NRM/LAW.
  • Composes onto the existing fn_iu_create / fn_iu_apply_edit_draft pattern (same SECDEF, same marker, same return shape).
  • Surfaces (and fixes) the fn_iu_apply_edit_draft global-coupling latent defect that would otherwise self-detonate the moment any other IU lifecycle infrastructure is introduced.

2.1 Scope

Accept the 60 ICX-CONST IUs in lifecycle_status='draft' as their permanent canonical state. No lifecycle transition is performed. Downstream queries filter by canonical_address LIKE 'ICX-CONST%' (or by content-anchor lineage), not by lifecycle_status.

2.2 Gateway compatibility

gateway_change_required : NONE
guard_disable_required  : NONE
on_doctrine             : YES (no canonical-writer changes)

2.3 Audit posture

primary_audit : the leg-B change_set + write-VERIFY rows ALREADY cover
                the act of CREATION. No new audit needed.
detection     : N/A (no transition)

2.4 Risk profile

semantic_drift:
  - Anyone (current or future tool) filtering IUs by
    `lifecycle_status='enacted'` will silently miss the constitution.
    This includes any future analytics, governance check, or reporting
    layer that uses the canonical 4-state vocab.
  - Coupling between law/NRM/TAC subsystems (which DO use 'enacted')
    and the IU subsystem (which would NOT) becomes inconsistent. The
    constitution would be the only "enacted-but-not-enacted" entity
    class in the system.

future_cost:
  - Eventually some workflow will require lifecycle_status='enacted'
    for IU filtering (rule liveness, search ranking, retired-detection).
    At that point an emergency enactment macro must be authored under
    pressure; it's better to do it deliberately now.

downstream_burden:
  - Every consumer needs to know "for ICX-CONST IUs, draft IS enacted."
    This is a stable but non-discoverable convention; in 6 months it
    will be hard to remember why.

2.5 Verdict

NOT RECOMMENDED. Defensible only if (a) downstream consumers are universally aware of the convention and (b) the cost of OPT-E1 is prohibitive. Neither is the case here.

3. OPT-E3 — One-shot exempt marker for direct UPDATE (REJECTED — anti-doctrine)

3.1 Scope

Operator with workflow_admin privilege:

  1. UPDATEs dot_config.iu_create.gateway.allowed_marker_values to append a value (e.g. 'constitution_first_enact') or dot_config.iu_create.gateway.exempt_policy to enable an exemption flag.
  2. Sets SET app.canonical_writer = 'constitution_first_enact' (or sets the exempt flag).
  3. Issues UPDATE public.information_unit SET lifecycle_status='enacted' WHERE canonical_address LIKE 'ICX-CONST%'.
  4. Removes the marker / disables the exempt flag.

3.2 Gateway compatibility

gateway_change_required : YES (allow_marker_values widening OR exempt_policy
                           change)
guard_disable_required  : implicit (the widened list is the disable)
on_doctrine             : NO (violates Pack 22-P3 §C.3 "Exempt list phải hẹp
                          và mỗi exempt phải có lý do + expiry. Không tạo
                          'cửa hậu vĩnh viễn'.")

3.3 Audit posture

primary_audit : NONE inside the UPDATE itself; depends on operator
                discipline to record the action in KB
detection     : the dot_config UPDATE would be visible in the
                postgres audit log IF one were installed; currently
                NO L3 Detector exists (Pack 22 deferred it)

3.4 Risk profile

precedent_risk:
  - Adds a one-shot exempt mechanism that creates pressure to reuse
    next time. Pack 22-P3 explicitly warned against this.
  - Future-readers will see 'constitution_first_enact' in dot_config
    history and may attempt to recreate it ad hoc.
  - Sets the policy precedent that "if it's important enough, bypass."

bypasses:
  - Skips the vocab check (no iu_lifecycle_vocab consulted)
  - Skips the FSM transition validation
  - Skips the immutability trigger setup (no iu_enacted_immut)
  - Skips the audit log (no iu_lifecycle_log row written)
  - Skips the enacted_at population on unit_version
  - Skips the review_decision linkage
  - Leaves the fn_iu_apply_edit_draft global-coupling defect unaddressed

irreversibility_of_the_decision:
  - Removing an exempt marker doesn't undo the precedent. The dot_config
    change history is forever; future operators will discover it and
    treat it as license.

3.5 Verdict

REJECTED. This option is forbidden by F-3 (G1 §8) and by the Pack 22-P3 design doctrine. It is enumerated here only for completeness; no implementation work should be done against OPT-E3 under any circumstance. If OPT-E1 cannot ship and OPT-E2 is unacceptable, the correct response is to keep the constitution in 'draft' and re-prioritize OPT-E1.

4. Comparison matrix

dimension OPT-E1 OPT-E2 OPT-E3
gateway doctrine preserved preserved violated
vocab consistency with TAC/NRM/LAW full partial full (but achieved via bypass)
audit completeness iu_lifecycle_log + governance soft-FK leg-B suffices none
fixes fn_iu_apply_edit_draft coupling yes (in scope) irrelevant (no transition) no
enacted-immutability protection new triggers N/A none
time-to-deliver one macro (≈3-4h xhigh) zero minimal but reckless
reversibility by retire/supersede trivial direct UPDATE possible
future maintainability high low (silent convention) very low (precedent)
detection of accidents trigger-enforced N/A none (no L3 Detector)
status_for_downstream_consumers discoverable via vocab + log non-discoverable misleading (exemption hidden)
disposition RECOMMENDED NOT RECOMMENDED REJECTED

5. Open questions (OQ-1..OQ-7) — to be resolved before implementation

OQ-1..OQ-7 are the design decisions that the OPT-E1 implementation needs ruled on. Each has a recommended answer; sovereign acceptance is required for any deviation.

OQ-1 — fn_iu_apply_edit_draft global-coupling patch

problem    : fn_iu_apply_edit_draft does SELECT count(DISTINCT lifecycle_status)
             ... FROM public.unit_version (GLOBAL count). Breaks the moment
             any UV becomes non-'draft'.
options    :
  (a) Patch: change the count to a per-unit count
      (SELECT lifecycle_status FROM unit_version WHERE id=v_iu.version_anchor_ref)
      and inherit from the current anchor.
  (b) Defer enactment until the function is hardened later (not a real option
      if we want to ship enactment now).
  (c) Add a bypass conditional inside fn_iu_apply_edit_draft that ignores
      'enacted' rows when checking the global count (subtle, fragile).
recommended: (a) — minimal, correct, scoped to one function body.
risk_if_wrong: full halt of all in-place edit flows globally.
priority   : MUST-RULE — implementation cannot proceed without this.

OQ-2 — iu_lifecycle_vocab as FK-CHECK or soft-check

problem    : information_unit.lifecycle_status has no CHECK constraint today.
             Should the new iu_lifecycle_vocab be enforced via FK
             (information_unit.lifecycle_status → iu_lifecycle_vocab.code)
             or via soft validation inside fn_iu_enact only?
options    :
  (a) Hard FK: ALTER TABLE information_unit ADD CONSTRAINT
      fk_iu_lifecycle FK (lifecycle_status) REFERENCES iu_lifecycle_vocab(code)
      NOT VALID; then VALIDATE.
  (b) Soft check inside fn_iu_enact: IF p_target NOT IN (SELECT code FROM
      iu_lifecycle_vocab) THEN RAISE.
recommended: (b) for THIS scope — adding a hard FK on an existing 158-row
             column at production scale is a DDL change with re-validation
             cost and lock risk. Soft-check inside the function covers the
             write-path and is easily extended to a hard FK in a later macro.
risk_if_wrong: (a) without NOT VALID could deadlock on busy tables; (b)
             leaves the door open to ad-hoc UPDATEs from other future writers.
             Since the gateway already blocks ad-hoc UPDATEs, (b) is safe.
priority   : MUST-RULE.

OQ-3 — Bulk enactment via fn_iu_enact_batch or python adapter loop

problem    : 60 IUs need enactment. Should fn_iu_enact accept an array
             (text[] OR uuid[]) and process all atomically, or should
             python loop one-call-per-IU?
options    :
  (a) fn_iu_enact_batch(p_addresses text[], p_actor, p_review_decision_id)
      — all-or-nothing inside a single SECDEF transaction.
  (b) Python adapter loops fn_iu_enact, one transaction per IU.
  (c) Python adapter wraps N calls inside a single BEGIN…COMMIT.
recommended: (b) — independent atomic transactions. Per-IU log row,
             per-IU rollback granularity. If one IU fails (e.g. invariant
             violation), the rest still enact. Matches the pattern of
             prod_iu_adapter_canonical.py (one fn_iu_create per IU during CUT).
risk_if_wrong: (a) increases blast radius (one bad row aborts all 60);
             (c) hides per-IU rollback.
priority   : MUST-RULE.

OQ-4 — Audit log table: new iu_lifecycle_log vs reuse public.lifecycle_log

problem    : public.lifecycle_log has INTEGER entity_id; IU uses UUID.
options    :
  (a) NEW table public.iu_lifecycle_log with UUID unit_id.
  (b) ALTER public.lifecycle_log to add an "entity_uuid" sidecar column
      (high blast radius; touches every existing caller).
  (c) Stuff the UUID as text into entity_code; entity_id always 0
      (semantic abuse).
recommended: (a) — cleanest, lowest blast radius. ANALOGY: cutter_governance
             schema is already a parallel governance tree distinct from
             public-domain registries.
risk_if_wrong: (b) might lock the existing log table during migration;
             (c) yields ungrepable / brittle data.
priority   : MUST-RULE.

OQ-5 — review_decision_id requirement: hard require, soft, or optional

problem    : Should fn_iu_enact require p_review_decision_id, or accept
             NULL with a "later-link" pattern?
options    :
  (a) HARD require: NULL → RAISE.
  (b) Soft default: NULL → write 'pending' marker; design a backfill macro.
  (c) Fully optional.
recommended: (a) — the CUT pipeline established that every mutation has
             a review_decision (the constitution CUT was approved via
             review_decision 29c88a7b-…). Enactment is a DISTINCT
             sovereign act and needs its OWN review_decision row.
             The function should refuse to enact without one.
             Operator must obtain a new review_decision via leg-B style
             governed recording BEFORE calling fn_iu_enact.
risk_if_wrong: (a) blocks the macro if review_decision row isn't pre-recorded;
             but that's the correct posture.
priority   : MUST-RULE.

OQ-6 — Re-run fn_iu_verify_invariants pre-transition

problem    : The IU passed fn_iu_verify_invariants at CUT time. Should
             fn_iu_enact re-run it before transitioning?
options    :
  (a) YES — re-run; if any of i1..i5 fail, RAISE before UPDATE.
  (b) NO — trust CUT-time invariants; minimize per-call cost.
recommended: (a) — invariants are cheap (single-row probe) and the check
             prevents enactment of a structurally damaged IU
             (e.g. if a UV was deleted out-of-band). Defense-in-depth.
risk_if_wrong: (b) is a thinkable optimization but has no observed
             measurable benefit at 60 calls.
priority   : SHOULD-RULE (default YES).

OQ-7 — L1 P-pub1/P-pub2 strict-enforcement inside fn_iu_enact

problem    : The L1 birth-gate emits PILOT-ONLY warnings about missing
             identity_profile.publication_authority_ref / publication_type_ref.
             Production-design intent is to BLOCK on these in strict mode.
             Should fn_iu_enact enforce them strictly at enactment time?
options    :
  (a) Strict-enforce inside fn_iu_enact: if publication_authority_ref OR
      publication_type_ref missing, RAISE.
  (b) Mirror L1: WARN, don't block (matches current pilot).
  (c) Skip: not in scope of enactment.
status_of_60_ICX_CONST :
  - publication_type_ref = 'law' is PRESENT on all 60 (from A-4 patch).
  - publication_authority_ref is ABSENT on all 60 (was not seeded at CUT).
recommended: (b) for THIS scope. Strict-enforcing publication_authority_ref
             would block all 60 IUs unless their identity_profile is
             patched first. That patch is itself an enactment-class
             policy decision and should be a separate macro
             ("iu_identity_profile_backfill"). For now: warn, do not
             block. Capture as backlog item.
risk_if_wrong: (a) the 60-IU enactment fails immediately on the first call;
             (c) drops a chance to surface the gap.
priority   : SHOULD-RULE (default (b)).

6. Decision matrix snapshot (proposed default rulings)

OQ-1 fn_iu_apply_edit_draft patch        : APPLY (option a)
OQ-2 vocab enforcement                    : SOFT-CHECK inside function (option b)
OQ-3 bulk enactment shape                 : PYTHON LOOP one-tx-per-IU (option b)
OQ-4 audit log location                   : NEW public.iu_lifecycle_log (option a)
OQ-5 review_decision_id requirement       : HARD REQUIRE (option a)
OQ-6 verify_invariants pre-transition     : YES (option a)
OQ-7 P-pub1/P-pub2 strict at enact        : WARN-ONLY for this scope (option b)

Any deviation from this matrix must be explicit in the sovereign ruling that approves the next implementation macro.

7. G3 disposition

G3_design_options_analysis : PASS
recommended_path           : OPT-E1
rejected_paths             : OPT-E2 (NOT RECOMMENDED), OPT-E3 (REJECTED)
open_questions             : 7 (OQ-1..OQ-7); 5 MUST-RULE + 2 SHOULD-RULE
production_mutation        : NONE
next                       : G4 — recommended contract authoring
                             (see [[dot-iu-cutter-v0-5-04-recommended-lifecycle-enactment-contract-2026-05-20]])

Related KB documents:

  • [[dot-iu-cutter-v0-5-01-live-lifecycle-survey-2026-05-20]]
  • [[dot-iu-cutter-v0-5-02-existing-lifecycle-docs-code-review-2026-05-20]]
  • [[dot-iu-cutter-v0-5-04-recommended-lifecycle-enactment-contract-2026-05-20]] — full function contract
  • [[dot-iu-cutter-v0-5-05-grant-verification-rollback-plan-2026-05-20]]
  • [[dot-iu-cutter-v0-5-06-final-lifecycle-design-report-2026-05-20]]
Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.5-lifecycle-enactment-design/dot-iu-cutter-v0.5-03-design-options-analysis-2026-05-20.md