KB-6D46

84 — SB-1 Fail-Closed / Trigger / Quorum Analysis (live-verified, no APR created, 2026-06-01)

8 min read Revision 1
one-roof-governanceimplementation-indexsb-1fail-closedauto-approve-bypassfn-auto-approve-addfn-apr-quorum-checkfn-apr-block-unimplementedaction-reviewphase-ano-commit2026-06-01

84 — SB-1 Fail-Closed / Trigger / Quorum Analysis

Branch B (mission §5). Tier: live read-only verification of the approval-path triggers/functions + design analysis. Mutation footprint: ZERO — pg_get_functiondef/pg_get_triggerdef read-only via query_pg; no approval_requests/apr_approvals row was created (mission §14). Trigger/quorum behaviour is proven from authoritative live source, not by inserting an APR. Base: doc 16 §2–3, doc 27 (re-verified verbatim 2026-06-01). This doc freshens that evidence for the final bundle and ties it to the SB-1 rehearsal (doc 83).


84.1 The existing auto-approve path (live, verbatim)

approval_requests has 6 triggers (re-verified 2026-06-01):

Trigger Timing / event When-clause Function
trg_approval_auto_code BEFORE INSERT fn_approval_auto_code
trg_apr_auto_approve BEFORE INSERT fn_auto_approve_add ← the bypass
trg_apr_quorum_check BEFORE UPDATE OF status WHEN new.status='approved' AND old.status='pending' fn_apr_quorum_check
trg_apr_block_unimplemented BEFORE UPDATE WHEN new.status='applied' AND old.status<>'applied' fn_apr_block_unimplemented_handler
trg_apr_lifecycle BEFORE UPDATE OF status fn_enforce_apr_lifecycle
trg_birth_approval_requests AFTER INSERT fn_birth_registry_auto('code')

fn_auto_approve_add (doc 27 §1.1, verbatim): on NEW.action='add' AND NEW.status='pending' it sets NEW.status:='approved', reviewed_by:='system_auto_approve' inside the INSERT. approval_requests.action CHECK ∈ {add, modify, delete, review} with DEFAULT 'add'.

fn_apr_block_unimplemented_handler (re-verified live 2026-06-01, verbatim):

IF NEW.proposed_action_code IS NULL THEN RETURN NEW; END IF;
SELECT handler_ref INTO handler FROM apr_action_types WHERE action_code = NEW.proposed_action_code;
IF handler = 'unimplemented' THEN
  RAISE EXCEPTION 'Action % has handler_ref=unimplemented. Reserve-only, cannot execute.', NEW.proposed_action_code;
END IF;
RETURN NEW;

84.2 Why action='add' is unsafe (the bypass mechanics)

The quorum gate (fn_apr_quorum_check) fires only on the pending → approved UPDATE transition. fn_auto_approve_add sets status:='approved' on INSERT — so an action='add' APR is inserted already approved; it never sits at pending; therefore the pending→approved UPDATE never happens and trg_apr_quorum_check never fires. Result: approved with zero quorum votes, no apr_approvals rows, and no self-approve check. The handler_ref='unimplemented' block fires only on the later →applied UPDATE, so in Phase A it still prevents execution, but it does not prevent approval-without-quorum, and it offers no protection once Phase B flips a handler.

The dangerous value is the default. action defaults to 'add'; an APR inserted without explicitly setting action is auto-approved. The four governance action-types are all risk_level='high' (proven in doc 83: all 4 inserted rows were high) → they require president + ≥2 ai_council quorum + a self-approve prohibition, all of which live only on the pending→approved path. Submitting a governance change as action='add' would grant it with no quorum, no self-approve check, and (Phase B) executable — the precise opposite of the intended high-risk routing.

Risk scenarios (doc 27 §3): R1 default-trap (high), R2 Phase-B unquorumed execution (critical), R3 approval-record pollution (high), R4 self-approve evasion (high).


84.3 Safe action convention (load-bearing Phase-A safety)

Every governance APR is submitted with action='review' (explicitly; modify/delete also acceptable — anything except add). This is a fixed protocol constant of the Điều-32 contract (like an HTTP method), not a discovered datum → not a no-hardcode violation. The dot_governance_gap_propose DOT (doc 25) sets action='review' by contract and is paired-tested to refuse action='add'. Two independent Phase-A safeties: (a) action≠'add' (forces the quorum path — protects the approval; load-bearing for R1/R3/R4) and (b) handler_ref='unimplemented' (blocks execution at →applied — protects the apply).


84.4 Phase A cannot execute ownership changes (fail-closed proof)

In Phase A all four governance action-types carry handler_ref='unimplemented' (proven additive in doc 83). Any APR that reaches the →applied transition with one of these proposed_action_codes is RAISEd by fn_apr_block_unimplemented_handler (Reserve-only, cannot execute.). This is identical to how amend_law and enact_nrm sit live today (high/unimplemented). Therefore registering the SB-1 vocabulary (Phase A) creates ZERO apply capability — no owner row, no exception, no delegation, no axis-owner can be written by these action-types until Phase B builds a real handler and flips handler_ref (itself an APR).


84.5 Phase B remains NO-GO

Flipping any governance handler_ref from 'unimplemented' to a real ref requires ALL of: (a) C-2 council ratification (recorded, not GPT-delegated); (b) the handler exists as a governed DOT writing the canonical SB-2 target (governance_object_ownership), not a private table; (c) SB-2 built (owner-write target exists); (d) sovereign sign-off path — os_proposal_approvals currently 0 ⇒ COMMIT_FORBIDDEN (M-1); (e) dot_coverage_required rows for the new propose/apply ops. No gate may be self-approved. The mutating apply DOT (dot_governance_assignment_apply) is G-APPLY = do-not-build until A-9 (H-1/H-2/SB-6) sovereign sign-off exists.


84.6 No self-approval / no event emit / no hidden handler activation

  • No self-approval: the prohibition lives in fn_apr_quorum_check (proposer==approver blocked). Because governance APRs use action≠'add' they traverse pending→approved, so the check fires. (The action='add' evasion R4 is closed by the convention + future H-OPT-1/4.)
  • No event emit: this mission registered no event_type_registry governance row and emitted nothing — event_outbox governance = 0 pre/post (doc 91). SB-1 emits (governance.owner.assigned, …) are register-before-emit, deferred to T7 build.
  • No hidden handler activation: the rehearsal's in-txn trigger DDL (the F-83-1 fix) was fully reversed by ROLLBACK (doc 83 §83.3); no handler_ref was flipped; apr_action_types is byte-for-byte at entry state (doc 91).

84.7 Hardening recommendations (T11-gated, design-only — carried from doc 27)

Unchanged and reaffirmed: H-OPT-4 (data-driven apr_action_types.auto_approvable boolean DEFAULT false allowlist gating fn_auto_approve_add) as the cleanest no-hardcode mechanism, paired with H-OPT-2 (change approval_requests.action default from 'add' to 'review', or drop the default). H-OPT-1 (risk-aware guard: never auto-approve risk∈{high,medium}) is the direct risk-aware fix. Per-action quorum config table = not warranted (quorum already data-driven off risk_level). All are DDL/function changes → forbidden in this macro; each needs its own council awareness + BEGIN..ROLLBACK rehearsal + H-1 enact path before any commit.


84.8 Verdict

Branch B = COMPLETE. The auto-approve path, the action='add' bypass mechanics (BEFORE-INSERT approve vs BEFORE-UPDATE quorum), the safe action='review' convention, Phase-A non-executability (handler_ref='unimplemented'fn_apr_block_unimplemented_handler RAISE), Phase-B NO-GO gating, and the no-self-approval / no-emit / no-hidden-handler guarantees are all verified against live source (2026-06-01) and tied to the doc-83 rehearsal. No APR row was created; no mutation occurred.

Back to Knowledge Hub knowledge/dev/reports/architecture/one-roof-governance-technical-addendum-and-implementation-index-2026-06-01/84-sb1-fail-closed-trigger-quorum-rehearsal-results.md