dot-iu-cutter v0.5 — Lifecycle Enactment Design · Design Options Analysis (G3 PASS · OPT-E1 RECOMMENDED) (doc 3 of 6)
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. OPT-E1 — New canonical SECDEF function fn_iu_enact (RECOMMENDED)
1.1 Scope
A new SECURITY DEFINER function public.fn_iu_enact(...) owned by directus. The function:
- Sets
app.canonical_writer='fn_iu_enact'(txn-local). - Validates input, FSM transition, vocab, and structural invariants.
- Performs UPDATE on
public.information_unit(lifecycle_status) and on the IU's current version-anchor row inpublic.unit_version(lifecycle_status + enacted_at). - Writes an audit row into a new
public.iu_lifecycle_log(UUID-keyed). - Returns a JSONB result describing the transition.
Configuration:
- ADD
'fn_iu_enact'todot_config.iu_create.gateway.allowed_marker_values(single-row UPDATE). - SEED
dot_config.iu_enact.*namespace (mirrorsiu_create.gateway.*self-description pattern). - GRANT EXECUTE on the function to
cutter_exec(and optionallyworkflow_admin).
Companion DDL:
- New tables:
public.iu_lifecycle_vocab(4 rows seeded from TAC),public.iu_lifecycle_log. - New triggers:
trg_iu_enacted_immut(mirrorsfn_law_enacted_immutable),trg_uv_enacted_immut(mirrorsfn_tac_enacted_immut). - One in-scope patch to
public.fn_iu_apply_edit_draftto remove the global "uniform lifecycle_status" coupling (see OQ-1).
Caller:
- New Python adapter
cutter_agent/lifecycle_enact_adapter.pyloopsfn_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. OPT-E2 — Drafts ARE the constitution (NOT RECOMMENDED)
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:
- UPDATEs
dot_config.iu_create.gateway.allowed_marker_valuesto append a value (e.g.'constitution_first_enact') ordot_config.iu_create.gateway.exempt_policyto enable an exemption flag. - Sets
SET app.canonical_writer = 'constitution_first_enact'(or sets the exempt flag). - Issues
UPDATE public.information_unit SET lifecycle_status='enacted' WHERE canonical_address LIKE 'ICX-CONST%'. - 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]]