dot-iu-cutter v0.5 — Lifecycle Enactment Design · Grant + Verification + Rollback Plan (G5 PASS) (doc 5 of 6)
dot-iu-cutter v0.5 — Lifecycle Enactment Design · Grant + Verification + Rollback Plan
doc 5 of 6 · 2026-05-20 · OPERATIONS DESIGN, NO MUTATION
phase : G5 — operations: grants, verification, rollback, fingerprints, sequence, follow-on backlog outcome : PASS — full operational envelope for the OPT-E1 contract production_mutation : NONE
0. Scope
This document is the operational counterpart to the contract in [[dot-iu-cutter-v0-5-04-recommended-lifecycle-enactment-contract-2026-05-20]]. Sections cover:
- Role / GRANT matrix
- Implementation sequence (DDL/PL/pgSQL phasing)
- Fingerprint capture before/after each phase
- Verification procedure (read-only)
- Rollback / compensation runbook
- Risk-bounded execution rules (the same Macro-rules used in CUT / write-VERIFY)
- Follow-on backlog (supersede, retire, identity_profile backfill, detector)
It is intended to be packagable as a single "command-review" approval artifact for the next macro (which will author the SQL bodies, run unit tests, and produce an approval-ready package; execution is gated on a separate sovereign ruling).
1. Role / GRANT matrix
1.1 Object-level grants required
public.fn_iu_enact(...) :
OWNER : directus
SECDEF : YES
GRANT :
- EXECUTE TO cutter_exec
- EXECUTE TO workflow_admin (optional; useful for ops-side enactment
when running as a non-cutter ops user)
REVOKE :
- EXECUTE FROM public (best practice; restrict invocation)
public.iu_lifecycle_vocab :
OWNER : directus
GRANT :
- SELECT TO public (vocab is reference data; everyone reads)
- SELECT TO context_pack_readonly
- SELECT TO cutter_exec
- SELECT TO cutter_verify
NO INSERT/UPDATE/DELETE granted (seed managed by ops)
public.iu_lifecycle_log :
OWNER : directus
GRANT :
- SELECT TO context_pack_readonly
- SELECT TO cutter_exec
- SELECT TO cutter_verify
- SELECT TO workflow_admin
INSERT : only fn_iu_enact (running as directus inside SECDEF)
No explicit GRANT INSERT to any role — writes happen
under SECDEF as directus
UPDATE/DELETE : nobody (audit-log integrity)
ENABLE ROW LEVEL SECURITY : optional; can be added in a later macro
if multi-tenancy emerges
public.fn_iu_enacted_immut() / fn_uv_enacted_immut() :
OWNER : directus
SECDEF : NO (regular trigger function; runs under invoking session,
which is fine because they are RAISE-only)
trg_iu_enacted_immut / trg_uv_enacted_immut :
no GRANT involved
1.2 dot_config row that REQUIRES UPDATE
key : iu_create.gateway.allowed_marker_values
before: 'fn_iu_create,fn_iu_apply_edit_draft'
after : 'fn_iu_create,fn_iu_apply_edit_draft,fn_iu_enact'
authority : directus (owner of dot_config)
new dot_config keys to INSERT (idempotent ON CONFLICT DO UPDATE):
- iu_enact.canonical_function
- iu_enact.mode
- iu_enact.target_default
- iu_enact.vocab_table
- iu_enact.log_table
- iu_enact.allow_no_review_decision
- iu_enact.policy_doc_path
- iu_enact.readme_path
1.3 Privileged role posture summary
directus : owner of everything; SECDEF function runs as directus
cutter_exec : EXECUTE on fn_iu_enact; SELECT on vocab/log; NO direct
write on IU/UV (already revoked since Pack 22-P3)
cutter_verify : SELECT-only across the stack (parity with M2 verify role)
workflow_admin : EXECUTE on fn_iu_enact (optional); SELECT on log
context_pack_readonly : SELECT on vocab/log only
public : SELECT on vocab only (reference data); nothing else
2. Implementation sequence
Designed as a 7-step DDL + seeding script, with verification checkpoints between steps. Each step is small enough that the operator can pause between steps without leaving the database in a half-built state.
PHASE 0 — Pre-flight (read-only)
P0.1 : Confirm gateway state ('enforced', allowed_marker_values value)
P0.2 : Confirm 60 ICX-CONST IUs are still all 'draft'
P0.3 : Confirm cutter_governance row IDs (review_decision, change_set)
to be linked exist (operator obtains the NEW enactment-time
review_decision via leg-B style governed recording BEFORE this
phase; see §7.1)
P0.4 : Capture pre-state fingerprints (see §3)
PHASE 1 — Vocab + log DDL
P1.1 : CREATE TABLE public.iu_lifecycle_vocab (...)
P1.2 : Seed 4 rows via INSERT … ON CONFLICT DO UPDATE
P1.3 : CREATE TABLE public.iu_lifecycle_log (...)
P1.4 : CREATE INDEX (5 indexes per §3.1 of doc 04)
P1.5 : GRANT SELECT (per §1.1)
VERIFY: SELECT count(*) FROM iu_lifecycle_vocab = 4
SELECT count(*) FROM iu_lifecycle_log = 0
information_schema cross-check on column shapes
PHASE 2 — Immutability functions + triggers
P2.1 : CREATE OR REPLACE FUNCTION public.fn_iu_enacted_immut()
P2.2 : CREATE TRIGGER trg_iu_enacted_immut
P2.3 : CREATE OR REPLACE FUNCTION public.fn_uv_enacted_immut()
P2.4 : CREATE TRIGGER trg_uv_enacted_immut
VERIFY: pg_trigger probe shows 2 new rows; live rows untouched
(158 IUs / 165 UVs); functions md5-pinned in docs
PHASE 3 — fn_iu_enact body
P3.1 : CREATE OR REPLACE FUNCTION public.fn_iu_enact(...)
P3.2 : ROLLBACK-only smoke test (call with p_dry_run=true on one
ICX-CONST address; expect status='plan_ok')
VERIFY: pg_proc probe shows new function exists; md5-pinned;
dry_run smoke test passes; zero rows persist
PHASE 4 — Gateway policy + iu_enact.* keys
P4.1 : UPDATE dot_config.iu_create.gateway.allowed_marker_values
→ 'fn_iu_create,fn_iu_apply_edit_draft,fn_iu_enact'
P4.2 : INSERT iu_enact.* keys (7 rows, ON CONFLICT DO UPDATE)
P4.3 : GRANT EXECUTE ON FUNCTION public.fn_iu_enact(...) TO cutter_exec
P4.4 : (optional) GRANT EXECUTE ON FUNCTION public.fn_iu_enact(...) TO workflow_admin
VERIFY: SELECT value FROM dot_config WHERE key IN (...) returns expected
all 8 keys present; pg_proc.proacl shows EXECUTE granted
PHASE 5 — fn_iu_apply_edit_draft in-scope patch (OQ-1)
P5.1 : CREATE OR REPLACE FUNCTION public.fn_iu_apply_edit_draft(...)
with patched body
VERIFY: pg_proc md5 changed; rerun T-I10 / T-I11 if integration env
available
PHASE 6 — Caller integration + README + KB doc rollout
P6.1 : Author cutter_agent/lifecycle_enact_adapter.py + tests
P6.2 : Add 'enact' sub-command to cutprod_canonical.py
P6.3 : Author knowledge/dev/laws/dieu44-trien-khai/readme/iu-lifecycle-enactment-readme.md
P6.4 : Author KB pre-execution approval package
VERIFY: pytest unit tests pass; CI green
PHASE 7 — (NOT IN THIS DESIGN) Authorized enactment of 60 ICX-CONST
Performed only after a SEPARATE sovereign ruling on an approval package
produced by the implementation macro. Out of scope of this design package.
3. Fingerprint capture (pre- and post-each-phase)
Used to prove byte-level non-regression. The fingerprints below are captured by the implementation macro at well-defined moments and stored in the operational reports.
3.1 Schema-level fingerprints (read by implementation macro)
gateway_function_body_md5 : md5(pg_proc.prosrc) for fn_iu_gateway_write_guard
expected pre = post = 6907fa4e5e46b5617d7dfecbd86326d7
(function unchanged across the whole macro)
fn_iu_create_body_md5 : pre = post (unchanged)
current: 3017892a5ac605a6daeaa5348e2a6cdf
fn_iu_apply_edit_draft_body_md5: pre = 22875ce25b2e2d1751cc4f3d1757252e
post = (new md5, recorded during P5)
fn_iu_verify_invariants_body_md5: pre = post (unchanged)
fn_iu_enact_body_md5 : pre = (absent)
post = (new md5; recorded during P3)
iu_lifecycle_vocab_row_count : pre = (absent)
post = 4
iu_lifecycle_log_row_count : pre = (absent)
post = 0 (no enactment writes in this macro)
trigger_inventory_information_unit : pre 5 / post 6
(gateway, birth-L1, birth-L2, updated_at,
birth-auto, +trg_iu_enacted_immut)
trigger_inventory_unit_version : pre 2 / post 3
(gateway, notif-version,
+trg_uv_enacted_immut)
dot_config_allowed_marker_values: pre = 'fn_iu_create,fn_iu_apply_edit_draft'
post = 'fn_iu_create,fn_iu_apply_edit_draft,fn_iu_enact'
3.2 Data-level fingerprints (read by implementation macro)
icx_const_iu_count : pre = 60 / post = 60 (unchanged)
icx_const_iu_lifecycle_status: pre = ['draft'] uniform / post = ['draft'] uniform
(enactment is OUT OF SCOPE of this macro)
unit_version_total : pre = 165 / post = 165
information_unit_total : pre = 158 / post = 158
birth_registry_total : pre = (current) / post = (unchanged)
cutter_governance_writes : pre = (current) / post = (unchanged)
ASSERT post == pre for ALL data-level fingerprints.
3.3 KB-level fingerprints
six_kb_docs_in_v0.5-lifecycle-enactment-design/ :
doc01_sha256 : (captured at upload)
doc02_sha256 : (captured at upload)
doc03_sha256 : (captured at upload)
doc04_sha256 : (captured at upload)
doc05_sha256 : (captured at upload)
doc06_sha256 : (captured at upload)
4. Verification procedure (read-only, post-implementation)
The implementation macro produces a verification report by running the
following probes. All probes use context_pack_readonly (or equivalent
SELECT-only role).
4.1 DDL existence probes
-- 1. iu_lifecycle_vocab table exists with 4 expected rows
SELECT code, name, sort_order
FROM public.iu_lifecycle_vocab
ORDER BY sort_order, code;
-- expect: ('draft','Bản nháp',10), ('enacted','Đã ban hành',20),
-- ('superseded','Bị thay',30), ('retired','Đã rút',40)
-- 2. iu_lifecycle_log table exists with 0 rows + 5 indexes
SELECT count(*) AS n_rows FROM public.iu_lifecycle_log;
SELECT count(*) AS n_idx
FROM pg_index ix
JOIN pg_class c ON ix.indrelid = c.oid
WHERE c.relname = 'iu_lifecycle_log';
-- expect: n_rows = 0, n_idx >= 5
-- 3. fn_iu_enact / fn_iu_enacted_immut / fn_uv_enacted_immut exist
SELECT proname, prosecdef, pg_get_function_identity_arguments(oid) AS args
FROM pg_proc
WHERE pronamespace='public'::regnamespace
AND proname IN ('fn_iu_enact','fn_iu_enacted_immut','fn_uv_enacted_immut');
-- expect 3 rows; fn_iu_enact prosecdef=true; others prosecdef=false
-- 4. Triggers attached
SELECT tgname, c.relname
FROM pg_trigger t JOIN pg_class c ON t.tgrelid = c.oid
WHERE NOT t.tgisinternal
AND tgname IN ('trg_iu_enacted_immut','trg_uv_enacted_immut');
-- expect 2 rows
-- 5. dot_config keys
SELECT key, value FROM public.dot_config
WHERE key IN ('iu_create.gateway.allowed_marker_values',
'iu_enact.canonical_function','iu_enact.mode',
'iu_enact.target_default','iu_enact.vocab_table',
'iu_enact.log_table','iu_enact.allow_no_review_decision',
'iu_enact.policy_doc_path','iu_enact.readme_path')
ORDER BY key;
-- expect 9 rows; allowed_marker_values appended with fn_iu_enact
-- 6. fn_iu_enact EXECUTE granted to cutter_exec
SELECT has_function_privilege('cutter_exec',
'public.fn_iu_enact(text,text,uuid,text,uuid,text,text,boolean)',
'EXECUTE') AS can_execute;
-- expect: true
-- 7. fn_iu_apply_edit_draft body md5 changed
SELECT md5(prosrc) FROM pg_proc
WHERE pronamespace='public'::regnamespace
AND proname='fn_iu_apply_edit_draft';
-- expect: NOT 22875ce25b2e2d1751cc4f3d1757252e
4.2 Behavioral probes (ROLLBACK-only)
B-1 : Dry-run fn_iu_enact on one ICX-CONST address
BEGIN;
SELECT public.fn_iu_enact(
'ICX-CONST/DIEU-0', 'verify_probe',
'<existing review_decision_id>', 'enacted', NULL, NULL, NULL, true
);
-- expect status='plan_ok' ; would_write_rows={iu:1,uv:1,log:1}
ROLLBACK;
ASSERT live row counts unchanged
B-2 : Negative test — bad target lifecycle
BEGIN;
SELECT public.fn_iu_enact(
'ICX-CONST/DIEU-0', 'verify_probe',
'<rd_id>', 'NOT_IN_VOCAB', NULL, NULL, NULL, true
);
-- expect status='invalid_target_lifecycle'
ROLLBACK;
B-3 : Negative test — nonexistent canonical_address
BEGIN;
SELECT public.fn_iu_enact('NOT_AN_ADDRESS','verify_probe',
'<rd_id>','enacted',NULL,NULL,NULL,true);
-- expect status='iu_not_found'
ROLLBACK;
B-4 : Negative test — review_decision_not_found
BEGIN;
SELECT public.fn_iu_enact('ICX-CONST/DIEU-0','verify_probe',
'00000000-0000-0000-0000-000000000000','enacted',NULL,NULL,NULL,true);
-- expect status='review_decision_not_found'
ROLLBACK;
B-5 : Gateway compatibility — direct UPDATE still blocked
BEGIN;
SET LOCAL app.canonical_writer = 'ad_hoc_attempt';
UPDATE public.information_unit
SET lifecycle_status='enacted'
WHERE canonical_address='ICX-CONST/DIEU-0';
-- expect: trg_aa_iu_gateway_write_guard RAISES 'IU Gateway blocked'
ROLLBACK;
4.3 Pass criteria
ALL of:
- DDL existence probes 1-7 return expected shapes
- Behavioral probes B-1..B-5 produce expected status strings
- Data-level fingerprints unchanged (60 ICX-CONST still draft;
iu_lifecycle_log row count = 0)
- Schema-level fingerprints match expected pre→post deltas
- No untouched-table touched (cutter_governance row counts unchanged)
5. Rollback / compensation
5.1 Rollback scope by phase
PHASE 1 (vocab + log DDL) rollback:
DROP TABLE public.iu_lifecycle_log; -- safe; no rows yet
DROP TABLE public.iu_lifecycle_vocab; -- safe; only reference data
PHASE 2 (immutability triggers) rollback:
DROP TRIGGER trg_uv_enacted_immut ON public.unit_version;
DROP TRIGGER trg_iu_enacted_immut ON public.information_unit;
DROP FUNCTION public.fn_uv_enacted_immut();
DROP FUNCTION public.fn_iu_enacted_immut();
⇒ live data untouched (no row has lifecycle_status='enacted' yet)
PHASE 3 (fn_iu_enact) rollback:
DROP FUNCTION public.fn_iu_enact(text,text,uuid,text,uuid,text,text,boolean);
PHASE 4 (dot_config + grants) rollback:
UPDATE public.dot_config
SET value = 'fn_iu_create,fn_iu_apply_edit_draft', updated_at = now()
WHERE key = 'iu_create.gateway.allowed_marker_values';
DELETE FROM public.dot_config WHERE key LIKE 'iu_enact.%';
REVOKE EXECUTE ON FUNCTION public.fn_iu_enact(...) FROM cutter_exec;
PHASE 5 (fn_iu_apply_edit_draft patch) rollback:
CREATE OR REPLACE FUNCTION public.fn_iu_apply_edit_draft(...)
LANGUAGE plpgsql ... AS $$ <ORIGINAL BODY> $$;
-- the implementation macro stores the original body as a sidecar
-- alongside the patch; rollback restores it byte-for-byte
PHASE 6 (cutter_agent integration) rollback:
git revert <commit> -- repo-side only; no DB action
PHASE 7 (authorized enactment) rollback:
Not in scope of THIS design package.
If/when phase 7 runs and needs reversal: see §5.2.
5.2 Compensation procedure for an ACCIDENTAL ENACTMENT (defense-in-depth)
If a row was enacted in error AFTER phase 7 (NOT in scope of this macro, but design must include the runbook for completeness):
preferred (forward-compensation):
- Use the lifecycle FSM to transition the erroneously enacted IU to
'retired' via a FUTURE fn_iu_retire(canonical_address, actor,
new_review_decision_id, reason='accidental_enactment_compensation')
- This is the on-doctrine compensation path: the enactment row remains
in iu_lifecycle_log forever as audit; the IU itself moves to 'retired'.
- DOES NOT use direct UPDATE; goes through canonical function.
emergency (forensic-recovery only; sovereign ruling required):
- Author a one-shot fn_iu_emergency_revert(canonical_address, actor,
rollback_review_decision_id, root_cause_text) function. SECDEF.
Add temporarily to allowed_marker_values. UPDATE row back to 'draft'
+ UPDATE UV.lifecycle_status='draft' + UPDATE UV.enacted_at=NULL +
INSERT iu_lifecycle_log row with transition_type='emergency_revert'.
REMOVE marker after use.
- This is the OPT-E3 anti-pattern dressed in proper canonical clothing;
use ONLY under explicit sovereign approval (incident ticket).
- NOT designed in this package; surfaced as backlog only.
absolute floor:
- The iu_lifecycle_log row PERMANENTLY records the enactment, even after
compensation. No DELETE allowed on log rows. Audit trail is forever.
6. Risk-bounded execution rules (Macro-rules)
The implementation macro will operate under the same rule set used in v0.5 CUT / write-VERIFY / main-FF macros. Reiterated here for the review package:
forbidden in this macro:
- lifecycle UPDATE to 'enacted' on any row (out of scope; PHASE 7 only)
- any UPDATE to any row in production via raw SQL
- any deploy/restart/push/tag
- any merge/push/tag
- any hard delete
- any mutation of source_document / source_version (TAC sandbox or otherwise)
- any DROP without explicit rollback authority
- any function body authoring that grants the function direct mutation
of cutter_governance rows from non-SECDEF context
allowed inside this macro:
- read-only SELECTs (via context_pack_readonly)
- SQL syntax validation of authored CREATE/ALTER statements (psql -c "EXPLAIN" or pg_dump --schema-only diff)
- unit tests with mock psycopg2 (no DB)
- integration tests with ROLLBACK-only (when integration env is approved separately)
- KB document authoring + upload
execution gates (each phase):
G0.x → G7.x checkpoints (see §2 for phase boundary VERIFY criteria)
Phase progression requires the prior phase's VERIFY block to PASS;
on failure: rollback that phase's writes, surface as a blocker,
STOP and route to GPT/User.
session-state:
- All DDL within the implementation macro runs inside a single
psql transaction PER PHASE.
- autocommit must be False at the connection-provider level
(see CUT macro lesson L2: provider autocommit=False).
7. Follow-on backlog (out of THIS design's scope)
These items are SURFACED but not designed in this package. Each becomes a future macro:
B-1 : fn_iu_supersede(canonical_address, prior_uv_id, new_uv_id, actor,
review_decision_id, change_set_id, reason)
Transitions enacted IU's current anchor to 'superseded' and points
to new enacted anchor. Composes with fn_iu_enact for the new version.
B-2 : fn_iu_retire(canonical_address, actor, review_decision_id, reason)
Transitions IU + current anchor to 'retired'. Allowed from
draft/enacted/superseded. Idempotent on retired.
B-3 : iu_identity_profile_backfill macro
Adds 'publication_authority_ref' (and possibly other strict-mode-
required fields) to the 60 ICX-CONST IUs' identity_profile so
that future strict-mode L1 birth-gate doesn't block them.
Cannot use direct UPDATE on identity_profile of enacted IUs (the
new trg_iu_enacted_immut locks it). Must be done BEFORE phase 7
enactment, OR via a future fn_iu_amend_identity_profile that
bypasses the immutability lock for identity_profile-only changes
on a tightly-scoped allow-list (sovereign decision required).
B-4 : L3 Detector — agent that scans for direct INSERT/UPDATE attempts
on IU/UV/lifecycle_log and surfaces them as incidents
(Pack 22-deferred; still deferred here)
B-5 : iu-lifecycle-enactment-readme.md (operator-facing) under
knowledge/dev/laws/dieu44-trien-khai/readme/
Mirror of iu-create-gateway-readme.md but for the enactment path.
Authored alongside implementation, not in this design package.
B-6 : Hard FK enforcement of iu_lifecycle_vocab on information_unit
(OQ-2 deferred from soft to hard). Future DDL macro with
NOT VALID + VALIDATE pattern.
B-7 : Multi-tenancy / RLS on iu_lifecycle_log if/when needed.
B-8 : Phase 7 — sovereign-approved enactment of the 60 ICX-CONST IUs.
Uses the adapter authored in phase 6. Produces an enactment
closeout doc parallel to the CUT and write-VERIFY closeouts.
Requires:
- a NEW cutter_governance.review_decision row for enactment
(DISTINCT from 29c88a7b-… which approved CREATION not ENACTMENT)
- optionally a NEW cut_change_set linking the 60-row enactment
batch
- per-IU iu_lifecycle_log row written by fn_iu_enact
- post-flight verification report
7.1 Pre-Phase-7 governance prerequisite (sovereign action)
Before phase 7 executes, a NEW cutter_governance.review_decision row
must be recorded specifically for "enact ICX-CONST 60 IUs". This is a
leg-B-style governed-recording macro (parallel to the CUT's leg-B that
produced review_decision 29c88a7b-…). The implementation macro that
delivers the design will package phase 7 separately and gate it on this
prerequisite being satisfied.
artifact required : cutter_governance.review_decision row
with decision pertaining to ICX-CONST
enactment (not CUT)
recording authority : the existing leg-B governed recording path
(cutter_agent/ledger_v2_canonical_cut.py-class
recorder; new "enactment" recorder or reuse)
linkage : that row's id becomes p_review_decision_id
passed to every fn_iu_enact call in phase 7
8. G5 disposition
G5_grant_verification_rollback_plan : PASS
production_mutation : NONE
delivered:
- GRANT matrix (function, table, dot_config)
- 7-phase implementation sequence + verification gates
- 3-axis fingerprint capture (schema, data, KB)
- 7 DDL existence probes + 5 behavioral probes
- rollback runbook per-phase + accidental-enactment compensation
- Macro-rules + execution gates
- 8-item follow-on backlog (supersede, retire, identity_profile backfill,
detector, readme, FK, RLS, phase-7 enactment)
next:
- G6 — final lifecycle design report
[[dot-iu-cutter-v0-5-06-final-lifecycle-design-report-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-03-design-options-analysis-2026-05-20]]
- [[dot-iu-cutter-v0-5-04-recommended-lifecycle-enactment-contract-2026-05-20]]
- [[dot-iu-cutter-v0-5-06-final-lifecycle-design-report-2026-05-20]] — next