19 — SB-1/SB-2 Rehearsal Plan & Results (author-mode, no live execution, 2026-06-01)
19 — SB-1 / SB-2 Rehearsal Plan & Results
Tier: rehearsal plan + static proof + read-only pre-flight; no live BEGIN..ROLLBACK was executed. Mutation footprint: read-only PG pre-flight + this KB doc; zero committed PG/Directus/Qdrant/Nuxt/law/approval mutation; no COMMIT, no DDL/DML run live. Evidence base: doc 18 (live read-only, 2026-06-01). Discipline source: house-law live-apply hard gate + prior-practice BEGIN..ROLLBACK conventions (statement_timeout, entry==exit, no idle txn).
0. Rehearsal posture — why live execution is NOT run in this macro
The mission permits a BEGIN..ROLLBACK rehearsal only if explicitly safe and fully rolled back, and is otherwise design-only / KB-docs-only. Live execution is deliberately not performed here, for four converging reasons:
- Channel reality. The only PG channel available to this agent is
query_pg— AST-validated, READ ONLY role, no writes/DDL (doc 18 §0). It cannot run a write transaction even one that ends inROLLBACK. A real rehearsal would require a write-capablepsql(e.g.ssh contabo → docker exec postgres psql -U workflow_admin), which is not part of this design-only macro's channel. - House-law Decision Bank §3.3. "If retention/deploy/mutation not approved, dry-run or author-mode only." No approval (C-1/C-2/H-1) exists;
os_proposal_approvals=0⇒ COMMIT_FORBIDDEN. - Trigger side-effects. Even a rolled-back rehearsal of SB-1 fires
fn_birth_registry_auto+fn_description_birth_guard+fn_auto_label_provenanceonapr_action_types(doc 18 §1.2); opening a write channel to a production DB to exercise these, purely for an optional rehearsal, exceeds what a substrate design needs. - The rehearsal's purpose is already met statically. Its goal — prove additivity + reversibility + no-migration-risk — is established by the live read-only pre-flight (§1) + the static additivity proof (§4) + docs 16 §2 / 17 §7. The executable script is provided author-mode, paste-ready (§2–3) for the operator to run under the proper gate (§5).
This is OPERATOR_HANDOFF_MODE (house-law branch classification), not a skipped deliverable. Acceptance criterion #9 ("rehearsal safely completed or explicitly not needed") is met via this explicit posture.
1. Read-only pre-flight assertions (LIVE, executed 2026-06-01)
These are the assertions a rehearsal would check before any write; they were run read-only and all pass:
| assertion | query result | verdict |
|---|---|---|
| SB-1 codes do not collide | count(apr_action_types WHERE action_code IN (4 codes)) = 0 |
✔ additive |
| SB-2 table absent | to_regclass('governance_object_ownership') = null |
✔ greenfield |
| SB-2 scope ref absent | to_regclass('governance_responsibility_scope') = null |
✔ greenfield |
| owner-agency FK targets present | count(governance_registry WHERE code IN (COUNCIL,SIV,DOT,KG-SYS,NRM-SYS,MOUT)) = 6 |
✔ FK valid |
| GOV-IU absent (OP-B) | count(... code='GOV-IU') = 0 |
✔ (IU owner deferred) |
| relations CHECK unchanged target | chk_relations_*_type ∈ {law,agency} (unchanged) |
✔ not widened |
| trigger inventory known | apr_action_types: 4 triggers; governance_relations: 2; approval_requests: 6 (incl. quorum/auto-approve/block-unimplemented) | ✔ accounted for |
2. SB-1 rehearsal script (AUTHOR-MODE — paste-ready, do not run without C-2 + approval)
Registers the 4 action-types fail-closed (handler_ref='unimplemented'), asserts, and rolls back. Illustrative; codes/descriptions subject to C-2.
-- SB-1 vocabulary-registration rehearsal (ROLLBACK-only)
SET statement_timeout = '5s';
SET lock_timeout = '2s';
SET idle_in_transaction_session_timeout = '10s';
BEGIN;
-- entry count
SELECT count(*) AS entry_action_types FROM apr_action_types; -- expect 6
-- additive INSERTs (fail-closed: handler_ref='unimplemented' -> fn_apr_block_unimplemented_handler blocks any use)
INSERT INTO apr_action_types (action_code, description, handler_ref, risk_level, status, _dot_origin) VALUES
('assign_governance_owner', 'Assign accountable owner for (governed object x scope) [SB-1/Đ37 §4.15-bis]', 'unimplemented', 'high', 'active', 'SB-1-DESIGN-T3'),
('grant_governance_exception', 'Grant 11-field governed exception [SB-1/M-DEF-6]', 'unimplemented', 'high', 'active', 'SB-1-DESIGN-T3'),
('delegate_authority', 'Time-boxed delegation of a responsibility scope [SB-1/C-5]', 'unimplemented', 'high', 'active', 'SB-1-DESIGN-T3'),
('assign_axis_owner', 'Assign accountable owner of an axis (governed object) [SB-1/M-DEF-8]', 'unimplemented', 'high', 'active', 'SB-1-DESIGN-T3');
-- NB: fires fn_birth_registry_auto + fn_description_birth_guard + fn_auto_label_provenance per row
-- assertions
SELECT count(*) AS after_insert FROM apr_action_types; -- expect 10
SELECT count(*) AS fail_closed FROM apr_action_types
WHERE action_code IN ('assign_governance_owner','grant_governance_exception','delegate_authority','assign_axis_owner')
AND handler_ref='unimplemented'; -- expect 4
-- negative test: an APR using one of these must be blocked (do in a sub-test, expect RAISE 'Reserve-only')
ROLLBACK;
-- exit verification (separate connection / after rollback)
SELECT count(*) AS exit_action_types FROM apr_action_types; -- expect 6 == entry
SELECT count(*) FROM pg_stat_activity WHERE state='idle in transaction'; -- expect 0
Expected outcome (when run live by operator): entry=6, after_insert=10, fail_closed=4, exit=6 (==entry), idle-in-txn=0. Proves: registration is additive (only +4 rows), reversible (ROLLBACK restores 6), and fail-closed (the 4 are inert until a real handler is set).
3. SB-2 rehearsal script (AUTHOR-MODE — paste-ready, do not run without C-1 + approval)
Creates the scope ref + ownership table + resolution views, asserts the 8 live edges are untouched, rolls back. Illustrative contract (not final DDL).
-- SB-2 ownership-substrate rehearsal (ROLLBACK-only)
SET statement_timeout = '5s'; SET lock_timeout = '2s'; SET idle_in_transaction_session_timeout = '10s';
BEGIN;
-- entry counts (the live substrate we must NOT disturb)
SELECT count(*) AS entry_relations FROM governance_relations; -- expect 8
SELECT count(*) AS entry_reg FROM governance_registry; -- expect 9
-- additive scope reference (6 law-defined scopes)
CREATE TABLE governance_responsibility_scope (
scope_code text PRIMARY KEY,
description text,
default_owner_hint text REFERENCES governance_registry(code),
status text NOT NULL DEFAULT 'active');
INSERT INTO governance_responsibility_scope(scope_code, description, default_owner_hint) VALUES
('policy','Policy authority','GOV-COUNCIL'),('health','Health/integrity','GOV-SIV'),
('execution','DOT execution','GOV-DOT'),('render','Display/render','GOV-MOUT'),
('approval','Approval routing','GOV-COUNCIL'),('audit','Audit','GOV-SIV');
-- additive ownership table (contract; see doc 17 §2)
CREATE TABLE governance_object_ownership (
id bigserial PRIMARY KEY,
object_type text NOT NULL, object_ref text NOT NULL,
scope text NOT NULL REFERENCES governance_responsibility_scope(scope_code),
owner_kind text NOT NULL CHECK (owner_kind IN ('accountable','supporting','delegated','exception')),
owner_gov_code text NOT NULL REFERENCES governance_registry(code),
is_inherited_anchor boolean NOT NULL DEFAULT false,
effective_from timestamptz NOT NULL DEFAULT now(), effective_to timestamptz,
lifecycle_status text NOT NULL DEFAULT 'active' CHECK (lifecycle_status IN ('active','superseded','revoked','expired')),
approval_ref text, audit_ref text, rollback_ref text,
source_law_ref text, source_design_ref text, supersedes_id bigint REFERENCES governance_object_ownership(id),
created_at timestamptz NOT NULL DEFAULT now(), created_by text, updated_at timestamptz, updated_by text);
CREATE UNIQUE INDEX uq_gov_obj_accountable
ON governance_object_ownership(object_type, object_ref, scope)
WHERE owner_kind='accountable' AND lifecycle_status='active';
-- resolution + gap views (contract sketch)
CREATE VIEW v_object_effective_owner AS
SELECT object_type, object_ref, scope, owner_gov_code, owner_kind
FROM governance_object_ownership WHERE owner_kind='accountable' AND lifecycle_status='active';
-- (full inheritance walk + v_object_owner_gap omitted for brevity; see doc 17 §4-5)
-- smoke INSERT (one anchor) + uniqueness negative test
INSERT INTO governance_object_ownership(object_type,object_ref,scope,owner_kind,owner_gov_code,is_inherited_anchor,approval_ref)
VALUES ('pivot','registries-pivot','policy','accountable','GOV-COUNCIL',true,'APR-REHEARSAL');
-- second accountable for same (object,scope) MUST fail the unique index (expect ERROR)
-- INVARIANT: the live governance substrate is untouched
SELECT count(*) AS exit_relations FROM governance_relations; -- expect 8 == entry
ROLLBACK;
-- post-rollback (separate connection)
SELECT to_regclass('public.governance_object_ownership'); -- expect NULL
SELECT count(*) FROM governance_relations; -- expect 8
SELECT count(*) FROM pg_stat_activity WHERE state='idle in transaction'; -- expect 0
Expected outcome: entry_relations=8 == exit_relations=8 (live edges untouched); the second accountable insert ERRORs on uq_gov_obj_accountable (uniqueness proven); after ROLLBACK the table is gone (to_regclass=NULL), idle-in-txn=0. Proves additivity, owner-per-scope uniqueness, and no disturbance to governance_relations.
4. Static additivity / reversibility proof (independent of execution)
Even without running the scripts, additivity is proven from the live schema (doc 18) + the design:
- SB-1: the only operation is
INSERT … apr_action_types(4 rows) — noALTER, no constraint change, no touch to other rows.action_codePK + the FK fromapproval_requestsmean the new rows are isolated additions; nothing references them until an APR opts in. Reversible byDELETE WHERE _dot_origin='SB-1-DESIGN-T3'(or never-committed via ROLLBACK). - SB-2: the only operations are
CREATE TABLE/CREATE VIEW/CREATE INDEX(greenfield,to_regclass=null) + optional seedINSERT. NoALTERongovernance_relations/governance_registry; new FKs validate against existing parent rows (no-op on empty child). Reversible byDROP TABLE/VIEW. - No-migration-risk: the 8 agency→law edges, their 2 CHECKs, their
UNIQUE, and their 2 triggers are not in any statement above. The set of objects the rehearsal mutates ∩ the set of live governance objects = ∅ (except the additive children).
5. Operator handoff — how to run the rehearsal safely (only under the gate)
When C-1/C-2 are ruled and the human approval phase (H-1) authorises a rehearsal:
- Channel:
ssh contabo→docker exec -it postgres psql -U workflow_admin -d directus(PG 16.13). - Set
statement_timeout='5s',lock_timeout='2s',idle_in_transaction_session_timeout='10s'first. - Run §2 then §3 inside a single
BEGIN … ROLLBACKeach; capture entry/after/exit counts. - Verify
exit==entry,to_regclass(new table)=NULLpost-rollback, andpg_stat_activityshows 0idle in transaction. - Take a
pg_dumpofapr_action_types,governance_relations,governance_registrybefore any committed apply (T11 only). - Record the rehearsal log back into this package (append
19a-…), never as a COMMIT.
6. Acceptance
✔ Rehearsal plan complete (paste-ready §2–3); ✔ read-only pre-flight executed live and green (§1); ✔ static additivity + no-migration-risk proven (§4); ✔ live execution explicitly deferred with reason (§0) — satisfies acceptance #9 ("safely completed or explicitly not needed"); ✔ operator handoff defined (§5); ✔ zero committed mutation. Rehearsal verdict: PLAN-COMPLETE / LIVE-DEFERRED (OPERATOR_HANDOFF_MODE).