KB-2682

05 — Governed Exception Model (Branch E) (2026-06-01)

10 min read Revision 1
one-roof-governanceclause-hardeningbranch-egoverned-exceptiondirect-pg-exceptionreplacement-plangrant-exception-action-typettlquarantine2026-06-01

05 — Governed Exception Model (Branch E)

Reviews the exception handling scattered across decision-pack docs 02 §2.3 (last row), 03 §3.4, 06 (DOT-GOV-EXCEPTION-REVIEW), 07 #12, 08 §4.17/§8.6 §VIII-ext. Adversarial. The Direct-PG API exception must become a fully-governed exception pattern.

E0. What the pack says (summary)

The pack treats an exception as an approval_requests row with an exception action-type + TTL + an overdue path that reuses the live admin_fallback_logfn_admin_fallback_overdue_scan()fallback_audit_overdue mechanism (Đ35 §6.5). The Direct-PG read-only adapter is the worked case: it must be ratified or recorded as an approved exception with a law-ref (Đ33 §13), read-only enforcement, TTL, and a vps_deploy_log ledger entry (today un-ledgered). Exceptions are excluded from orphan emission while unexpired and re-raised on TTL expiry (DOT-GOV-EXCEPTION-REVIEW).

Overall: the reuse instinct (admin_fallback pattern) and TTL discipline are right. Three real defects: the required field set is incomplete (no replacement plan), the action-type it relies on does not exist, and severity rests on a fact the scanner can't verify.


E1 — Incomplete field set: no replacement_plan, no review_cadence distinct from expiry

  • Original: the pack's exception carries owner / approval / TTL / overdue path / law-ref / read-only enforcement.
  • Gap (mission §8): the mission requires eleven fields: exception_type, scope, owner, reason, risk, approval, expiry, review_cadence, rollback, replacement_plan, issue/event-if-expired. The pack is missing replacement_plan (the exit strategy) and treats review_cadence as identical to expiry. An exception with no replacement plan is a permanent island in disguise — it renews at each TTL forever (red-team #5: "temporary but never expires").
  • Hardened wording — the full exception record contract:
    Field Required Meaning
    exception_type e.g. direct_pg_readonly_adapter, emergency_hotfix, legacy_grandfather
    scope exactly what is exempted (this route / this adapter / this object) — never "the module"
    accountable_owner who is answerable (per Branch C)
    reason why the bypass is justified now
    risk computed risk if abused (drives severity)
    approval_ref the Đ32 approval that granted it (quorum per risk)
    expiry (TTL) hard end date; after which → orphan/critical
    review_cadence distinct from expiry — periodic re-justification interval (e.g. monthly) even before expiry
    rollback_ref how the exempted state is reverted if pulled
    replacement_plan the path to NOT needing the exception (e.g. "ratify Directus PK-less view support" / "build route registry") — mandatory; absence ⇒ auto-critical
    issue_on_expiry the system_issues/event raised when expired or review missed
  • Acceptance test: an exception record missing replacement_plan cannot be granted (validation fails); an exception past review_cadence (even if before expiry) raises a warning; past expiry raises critical.
  • Open question: OQ-E1 — default review_cadence and max renewal count before a replacement plan must execute? (Recommend: max 2 renewals, then the replacement plan is itself gated.)

E2 — No grant_exception APR action-type exists (live-verified trap)

  • Original: doc 02 §2.4 / doc 04 §4.3 / doc 08 §4.17: an exception is "an approval_requests row, action-type for exception, with TTL."
  • Live fact: apr_action_types has exactly 6 codes — add_field, amend_law, create_item, enact_nrm, patch_ops_code, update_item (re-verified this session). There is no exception action-type. So "an approval_requests row of an exception action-type" is not currently expressible; the pack assumes a row type that doesn't exist.
  • Misimplementation risk: an implementer either (a) shoehorns an exception into update_item/create_item (losing TTL/replacement-plan semantics → an ungoverned exception, i.e. an island), or (b) can't create the exception at all (so the Direct-PG adapter stays a raw orphan).
  • Hardened wording: state as a prerequisite that a new apr_action_type grant_exception (and likely assign_governance_owner, delegate_authority — see C7, I1) must be registered first (a governed add to apr_action_types, COUNCIL-owned, with risk_level per scope and a handler). Until it exists, exceptions are recorded in the live admin_fallback_log (which already has the overdue machinery) as the interim governed home — not in approval_requests. Be explicit about the interim vs target home.
  • Acceptance test: the exception flow references an existing action-type (after registration) or the interim admin_fallback_log; it never writes a free-shaped row.
  • Open question: OQ-E2 — does grant_exception need a dedicated handler (TTL enforcement, replacement-plan check), or can the overdue scan + a CHECK suffice? (Likely a small handler.)

E3 — Severity split (read-only vs DDL) rests on a fact the scanner can't see

  • Original: doc 03 §3.4 grades Direct-PG critical (if it can reach DDL) / high (read-only).
  • Trap: "can it reach DDL?" is a privilege fact of the pg role behind the adapter, not a registry fact. If the scanner assumes read-only, a role that was silently granted write becomes an undetected critical. Memory-dependence again.
  • Hardened wording: the exception record must declare the enforced privilege as a verifiable claim: the read-only role name + the assertion "this role has no INSERT/UPDATE/DELETE/DDL grant," and the scanner verifies it against information_schema.role_table_grants (or equivalent) every scan. Severity is then computed from the verified grant set, not the declared one. A mismatch (declared read-only, actually has write) is itself critical.
  • Acceptance test: flipping the adapter role's grants to include INSERT (in a rehearsal) flips the computed severity to critical automatically — severity tracks reality, not the label.
  • Open question: none — this is the right reuse of the read-only-role discipline the pack already trusts for query_pg.

E4 — Allowed vs forbidden exceptions are not enumerated

  • Original: the pack says some bypasses "must be ratified or recorded" but never lists what may be exempted temporarily vs what may never be exempted (mission §8: "what exceptions are allowed temporarily; what exceptions are forbidden").
  • Hardened wording — the exception allow/deny list (COUNCIL-owned):
    May be a temporary exception May NEVER be exempted
    read-only Direct-PG adapter (Directus PK-less view limit) a write/DDL path outside DOT governance
    emergency hotfix (Đ35 §6.5 fallback, ≤24h) a local approval mechanism (rule 2 — never)
    legacy surface pending regularization (QUARANTINED, A3) a standalone policy table with no owner (rule 4 — never)
    a pre-registration read-only utility UI computing count/grouping/classification truth (Đ28 NT-D1 — never)
    emitting an unregistered event_type (Đ45 §3.2 — never)
  • Rule: "An exception may waive coverage timing (when the link is resolved) but may never waive a safety invariant (no write without DOT governance, no local approval, no UI truth-math, no unregistered emit). Safety invariants are non-exemptable."
  • Acceptance test: an attempt to grant an exception for a write-path-outside-DOT or a local-approval-flag is rejected at grant time (non-exemptable class).
  • Open question: OQ-E4 — council to ratify the non-exemptable list (it is itself a governed policy).

E5 — Exception suppression of orphan emission can hide a changed object

  • Original: doc 07 §7.5: "an object with an unexpired approved exception is excluded from orphan emission."
  • Loophole: the exception was granted for a specific scope/state. If the exempted object changes (the adapter gains a new endpoint; the route starts mutating), the exclusion still suppresses the orphan — the exception now covers something it was never granted for (scope creep under an old waiver).
  • Hardened wording: the exception suppresses orphan emission only while the object's signature matches the exempted scope. Bind the exception to a state fingerprint (e.g. the adapter's endpoint set / privilege set hash); if the fingerprint changes, the exception is auto-invalidated → the object re-enters orphan emission + a exception_scope_drift issue fires. (Reuses the E3 verified-fact discipline.)
  • Acceptance test: mutating an exempted read-only adapter into a write adapter auto-invalidates the exception and raises critical, rather than staying silently suppressed.
  • Open question: none.

E-summary — Branch E verdict

ID Severity Type Disposition
E1 high gap full 11-field record; replacement_plan mandatory
E2 high trap (live) grant_exception action-type is a prerequisite; interim = admin_fallback_log
E3 high memory-dependence severity from verified grants, not declared
E4 medium gap enumerate allowed/forbidden (non-exemptable safety invariants)
E5 medium loophole bind exception to a state fingerprint (anti scope-creep)

The exception model is the right idea but is under-built; E1+E2 must land before any apply DOT (doc 09) can record a real exception. Note the strong dependency on E2 ⟷ I1 ⟷ C7 (all need new APR action-types).

Back to Knowledge Hub knowledge/dev/reports/architecture/one-roof-governance-clause-review-hardening-2026-06-01/05-governed-exception-model.md