KB-6EA6

IU Mutation-Safety Foundation — Live Apply Appendix (2026-05-28)

12 min read Revision 1
iumutation-safetybounded-gate-protocolappendix2026-05-28

IU Mutation-Safety Foundation — Live Apply Appendix (2026-05-28)

Verbatim migration / rollback / self-test SQL, channel transcripts, and extracted substrate facts for IU_MUTATION_SAFETY_FOUNDATION_FOR_D_E_AND_F_ENABLE_LIVE_APPLY_150000X. Companion to the main report.


A1 — Channels + artifacts

  • Write/apply: ssh contabo \"docker exec -i postgres psql -U workflow_admin -d directus -v ON_ERROR_STOP=1 -P pager=off\" < /tmp/<file>.sql (stdin pipe; container /tmp ≠ host /tmp).
  • Read-proof: MCP query_pg, role context_pack_readonly, READ ONLY txn, statement_timeout 5s, LIMIT 500.
  • Migration /tmp/mig_gate_protocol.sql — md5 be980c7e3ef77886494e532905811925.
  • Rollback /tmp/rollback_gate_protocol.sql — md5 2d62a732884b238ea49d62e7763eb9da.
  • Self-test /tmp/selftest_gate_protocol.sql — md5 0d7a7845758e65f29e9977c8dc53b2ef.
  • macOS md5 via openssl md5.

A2 — Hard Gate 0 transcripts

Read identity (query_pg): context_pack_readonly | directus | 172.19.0.3/32 | PostgreSQL 16.13.

Container: postgres Up 5 weeks (healthy).

Write identity + reversible probe on the real target table (dot_config):

IDENTITY | workflow_admin | directus |
GATE | iu_core.composer_enabled | false   (… all 9 gates false …)
BEGIN
UPDATE 1
IN_TX | iu_core.composer_enabled | true
ROLLBACK
AFTER_RB | iu_core.composer_enabled | false

⇒ write capability + reversibility proven without persisting.


A3 — Key substrate facts (verified live)

dot_config: (key text NOT NULL, value text NOT NULL, description text, updated_at timestamptz NOT NULL default now()); BEFORE trigger fn_dot_config_touch() sets updated_at. No write-blocking guard (UPDATE probe succeeded).

Catalog/run CHECK constraints (unchanged): category ∈ {collection,piece,lifecycle,read,health}, run_mode ∈ {plan,apply,verify}, run_status ∈ {planned,applied,verified,refused,failed}. Canonical logger fn_dot_iu_command_log(p_command,p_category,p_run_mode,p_run_status,p_mutating,p_params_digest,p_gate_snapshot jsonb,p_evidence jsonb,p_actor) → uuid (fail-closed: command must be in catalog).

cutter_governance.review_decision (read via workflow_admin; RO denied): PK review_decision_id uuid; NOT NULL: governance_event_kind, manifest_id, manifest_version, review_scope, status, verdict, findings(jsonb), reviewer_class, reviewer_identity(jsonb), risk_class_assessment, decision_at, decided_by, cross_signed_by_dot_verifier(bool), version, created_at, updated_at. FKs: manifest_id→manifest_envelope(envelope_id), (manifest_id, manifest_unit_local_id)→manifest_unit_block(envelope_id, unit_local_id), plus self-FKs prior_/superseded_by_review_decision_id. Rowcount = 4.

fn_iu_piece_split (SECURITY DEFINER) review-decision enforcement (verbatim fragments):

IF p_review_decision_id IS NULL THEN
  RETURN jsonb_build_object('status','invalid_input','field','review_decision_id',
    'guidance','Required. Split must reference a cutter_governance.review_decision row.',
    'next_action','record_review_decision_first');
END IF;
...
SELECT count(*) INTO v_rd_found FROM cutter_governance.review_decision
 WHERE review_decision_id = p_review_decision_id;
IF v_rd_found = 0 THEN
  RETURN jsonb_build_object('status','review_decision_not_found', ...);
END IF;

Has a full p_dry_run path. Does NOT reference allow_no_review_decision. fn_iu_piece_merge symmetric. ⇒ id already required + FK-probed in code; no wiring missing.

event_type_registry: (event_domain, event_type, event_stream, delivery_lane, default_severity, description, active, created_at)no json-schema / no compat_mode column. 31 rows: 16 iu, 6 piece, 5 staging, 4 system. iu already includes structure_piece_split, structure_pieces_merged, structure_op_applied, structure_child_added, piece_added_to_collection, piece_removed_from_collection, piece_reordered.

Emit gating (fn_iu_emit_event / fn_iu_piece_emit_event): master fn_iu_core_routes_enabled() → runtime piece_event_runtime.emit_enabled → registry lookup (unknown/inactive → NULL) → event_outbox trigger trg_event_outbox_type_validate requires (domain,type) present+active AND event_stream/delivery_lane to MATCH registry.

Delivery gating (fn_iu_route_deliver): (1) fn_iu_delivery_enabled(); (2) route registered in iu_outbound_route; (3) per-route allowlist dot_config.iu_core.delivery_live_routes (CSV, EMPTY)route_code <> ALL(string_to_array(value,',')) ⇒ refused; (4) only target_kind='sql_function' AND target_ref='fn_iu_structure_consumer'; all else feature_not_supported. iu_outbound_route columns include payload_contract jsonb, fail_closed bool, dry_run bool, enabled bool.


A4 — Migration SQL (verbatim, structure)

Single TX. Full body in /tmp/mig_gate_protocol.sql. Guard asserts db=directus, catalog=47, table+4 fns+4 catalog rows absent; post-check asserts catalog=51, 4 fns present.

CREATE TABLE public.iu_gate_transition (
  transition_id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  gate_key text NOT NULL,
  action text NOT NULL CHECK (action IN ('open','close','watchdog_close')),
  prev_value text, new_value text, approval_id uuid, ttl_seconds int,
  expires_at timestamptz, actor text NOT NULL, reason text, run_id uuid,
  created_at timestamptz NOT NULL DEFAULT now());
CREATE INDEX ix_iu_gate_transition_gate ON public.iu_gate_transition (gate_key, created_at DESC);

-- fn_iu_gate_open: fail-closed core
c_never_flip := {iu_enact.allow_no_review_decision, iu_core.vector_sync_enabled};
c_governable := {iu_core.composer_enabled, iu_core.structure_ops_enabled, iu_core.delivery_enabled,
                 iu_core.three_axis_auto_refresh_enabled, iu_core.operator_runtime_enabled,
                 queue.job_substrate.enabled, queue.dlq.replay_enabled};
  IF gate = ANY(never_flip)     -> RAISE insufficient_privilege ('NEVER-FLIP … supply a real review_decision_id');
  IF NOT gate = ANY(governable) -> RAISE insufficient_privilege ('not a governable runtime gate (fail-closed)');
  IF approval_id IS NULL        -> RAISE check_violation ('approval_id required (Điều 32)');
  IF ttl NOT IN (0,3600]        -> RAISE check_violation;
  UPDATE dot_config SET value='true' WHERE key=gate;
  INSERT iu_gate_transition(...,'open',prev,'true',approval_id,ttl,expires_at,...);
  run := fn_dot_iu_command_log('dot_iu_gate_open','health','apply','applied',true,...);

-- fn_iu_gate_close: forces value='false' for governable|never_flip; logs 'close'
-- fn_iu_gate_verify_closed (STABLE): VALUES list of 9 gates LEFT JOIN dot_config;
--   returns gates[], all_governed_closed, never_flip_intact, all_safe
-- fn_iu_gate_watchdog: for each governable gate currently 'true' whose latest
--   'open' transition expires_at < now() (or none) -> fn_iu_gate_close + relabel 'watchdog_close'

INSERT INTO dot_iu_command_catalog(command_name,category,mutating,reversible,target_functions) VALUES
 ('dot_iu_gate_open','health',true,true,ARRAY['fn_iu_gate_open']),
 ('dot_iu_gate_close','health',true,true,ARRAY['fn_iu_gate_close']),
 ('dot_iu_gate_watchdog','health',true,true,ARRAY['fn_iu_gate_watchdog']),
 ('dot_iu_gate_verify_closed','read',false,true,ARRAY['fn_iu_gate_verify_closed']);

Apply transcript (real): BEGIN / DO(guard) / CREATE TABLE / CREATE INDEX / COMMENT / CREATE FUNCTION ×4 / INSERT 0 4 / DO(post-check) / COMMIT.

Object diff: catalog 47→51; new rows dot_iu_gate_close|health|mut=true|rev=true, dot_iu_gate_open|health|mut=true|rev=true, dot_iu_gate_verify_closed|read|mut=false|rev=true, dot_iu_gate_watchdog|health|mut=true|rev=true. Volatility: fn_iu_gate_open=v, fn_iu_gate_close=v, fn_iu_gate_watchdog=v, fn_iu_gate_verify_closed=s. dot_iu_command_run=55 (unchanged). Schema 1,617,882 → 1,628,568 bytes (Δ +10,686).

Dry-run transcript (COMMIT→ROLLBACK before real apply): BEGIN/DO/CREATE TABLE/CREATE INDEX/COMMENT/CREATE FUNCTION ×4/INSERT 0 4/DO/ROLLBACK; post: catalog=47, my 4 gate fns=0, iu_gate_transition absent (the residual gatefns count of 1 is the unrelated pre-existing fn_iu_gateway_write_guard).


A5 — Self-test transcript (BEGIN/ROLLBACK, verbatim extract)

T0 baseline:                  all_safe = true
T1 open composer (approved):  status = opened
T2 after open:                all_governed_closed = false | never_flip_intact = true
T3 never-flip (allow_no_review_decision): NOTICE refused_as_expected | still = false
T3b never-flip (vector_sync_enabled):     NOTICE refused_as_expected | still = false
T4 non-governable (routes_master_enabled): NOTICE refused_as_expected
T5 null approval_id (structure_ops):       NOTICE refused_as_expected
T6 close composer:            status = closed
T7 open delivery + back-date expiry; watchdog:
      T7-before delivery = true | T7-watchdog closed_count = 1 | T7-after delivery = false
T8 final verify:              all_safe = true
T9 ledger during test:
   iu_core.composer_enabled | open           | false -> true
   iu_core.composer_enabled | close          | true  -> false
   iu_core.delivery_enabled | open           | false -> true
   iu_core.delivery_enabled | watchdog_close | true  -> false
ROLLBACK
POST: ledger_rows = 0 | composer = false | delivery = false | run_count = 55

Note: T1/T6 composer_now columns read the pre-statement snapshot (sibling subquery in the same SELECT as the volatile function) — a cosmetic psql artifact; flips proven authoritatively by T2 (separate statement), T7, and the T9 ledger. Every DO-block assertion would have RAISEd on failure; none did.

Committed end-state fn_iu_gate_verify_closed():

all_safe = true | all_governed_closed = true | never_flip_intact = true
7 governable gates = false ; iu_enact.allow_no_review_decision = false ; iu_core.vector_sync_enabled = false
catalog = 51 | run = 55 | iu_gate_transition rows = 0

A6 — Rollback SQL (verbatim, structure) + proofs

BEGIN;
-- fail-closed guard: refuse if any governable gate is open
DO $safe$ ... IF (count of 7 governable gates WHERE value='true') > 0
            THEN RAISE 'rollback refused: N governable gate(s) currently OPEN'; ... $safe$;
DELETE FROM dot_iu_command_run     WHERE command_name IN ('dot_iu_gate_open','dot_iu_gate_close','dot_iu_gate_watchdog','dot_iu_gate_verify_closed');
DELETE FROM dot_iu_command_catalog WHERE command_name IN (… same 4 …);
DROP FUNCTION IF EXISTS public.fn_iu_gate_watchdog(text);
DROP FUNCTION IF EXISTS public.fn_iu_gate_verify_closed(text);
DROP FUNCTION IF EXISTS public.fn_iu_gate_close(text,text,text);
DROP FUNCTION IF EXISTS public.fn_iu_gate_open(text,uuid,text,text,integer);
DROP TABLE IF EXISTS public.iu_gate_transition;
-- assert catalog=47, 0 gate fns, no ledger table
COMMIT;

Rollback dry-run (COMMIT→ROLLBACK): BEGIN/DO/DELETE 0/DELETE 4/DROP FUNCTION ×4/DROP TABLE/DO(assert ok)/ROLLBACK; protocol confirmed still installed (catalog=51, gatefns=4). Reverts to exact baseline (47 / 0 fns / no table).

Fail-closed guard proof: opened iu_core.structure_ops_enabled in-TX → guard NOTICE: ROLLBACK-GUARD would REFUSE: 1 gate(s) open → ROLLBACK → structure_ops = false. The rollback cannot remove the watchdog while a gate is open.


A7 — Forbidden-action ledger (what was NOT done)

No INSERT/UPDATE/DELETE on any IU-data table (all Δ=0). No cutter_governance.review_decision write (stayed 4). No event_type_registry write (stayed 31). No event_outbox / iu_route_attempt / iu_route_dead_letter write. No iu_sql_link.enabled flip. No split/merge/compose. No persistent gate flip (self-test rolled back; committed all_safe=true). No allow_no_review_decision / vector_sync_enabled flip (coded refusal + stayed false). No Qdrant/Directus/Nuxt/4-Mothers action. Persisted writes = gate-protocol DDL + 4 catalog rows + these KB docs only.

Back to Knowledge Hub knowledge/dev/reports/architecture/iu-mutation-safety-foundation-for-d-e-f-enable-live-apply-appendix-2026-05-28.md