12 — Red-Team Scenarios (Branch L, 24 scenarios) (2026-06-01)
12 — Red-Team Scenarios (Branch L)
24 adversarial scenarios. For each: expected detection · severity · accountable owner (Branch C) · issue/event · blocking rule · and whether the CURRENT pack catches it or a hardening fix is required. A scenario marked ESCAPES (pre-fix) is one the un-hardened pack would miss — those are the teeth of this review.
Legend: ✅ caught by pack as-is · ⚠️ caught only after the named hardening fix · ❌ ESCAPES even after fixes (residual risk for council).
Block 1 — Ownership / island
1. New API route added without owner.
- Detect: route inventory (G2) reconciles nginx+Nuxt → route not mapped to an owner. Sev: high (
route_orphan). Owner: GOV-MOUT (surface). Issue/event:owner_gap/route_orphan→governance.orphan_detected. Block: G-ROUTE fails (route unverifiable/unowned). - Status: ⚠️ requires G2 (no route registry today → ESCAPES as-is; this is the biggest live blind spot).
2. New pivot added without approval.
- Detect: pivot inherits source-collection owner (J8); if source covered, owner ok but counting-contract change needs APR →
approval_path_gap. Sev: high. Owner: GOV-SIV (health) + COUNCIL (approve). Event:governance.approval_path_gap. Block: G-IMPL fails (no APR). - Status: ⚠️ requires I1 (
assign/contract action-type) — else PROPOSE can't even file the APR.
3. Personal pin becomes global. (mission #6)
- Detect: a user-scoped pin (Class 0, non-governed) is re-scoped to global → crosses into
PROFILE-POLICY(B2) →pin_policy_unowned. Sev: high. Owner: COUNCIL. Event:governance.policy_unowned. Block: G-IMPL/G-PROD. - Status: ⚠️ requires A2/B1 — without the non-governed class + the shared-truth test, the scanner either floods on every personal pin (noise) or misses the global escalation. The fix is what makes #3 detectable without drowning in #personal-pins.
4. Local policy table created under a module. (mission #7)
- Detect: PG scan finds a table with policy shape and no
owner_gov_code/relation → F-ISLAND-3. Sev: high. Owner: COUNCIL. Event:governance.policy_unowned/island_detected. Block: G-IMPL + CI (P8). - Status: ✅ (PG-detectable island) — but island roll-up needs D3.
5. Direct-PG adapter added as "temporary" but never expires. (mission #5)
- Detect: exception with no
replacement_plan(E1) → auto-critical; EXCEPTION-REVIEW flips on TTL/review-cadence miss. Sev: critical. Owner: COUNCIL (policy) / SIV (risk) / MOUT (surface) — C4. Event:governance.unratified_exception. Block: G-ROUTE + president. - Status: ⚠️ requires E1 (replacement_plan mandatory) — without it, the exception renews forever (the exact red-team failure). With E1 + max-renewals it's caught.
6. A grouping policy invents its own classification outside Đ24 facets.
- Detect:
classification_policy_unowned(doc 07 #15) + Đ24 §5.4-note (grouping derived-from classification). Sev: high. Owner: COUNCIL (policy) + KG/taxonomy (substrate, J7). Block: G-IMPL. - Status: ⚠️ requires J7 (don't dump on COUNCIL; substrate owner is KG).
7. Two agencies both claim a policy (double owner).
- Detect: coverage view finds 2 accountable owners in the same responsibility scope (C1/C2) → conflict/island. Sev: high. Owner: COUNCIL (tie-break §4.12d / owner-of-last-resort A5). Event:
island_detected. Block: G-IMPL. - Status: ⚠️ requires C1/C2 — without responsibility-scope, the pack can't tell a legitimate multi-scope split from an illegitimate same-scope double-owner.
8. An object falls in a seam nobody owns.
- Detect: owner-of-last-resort (A5) → defaults to COUNCIL → never a "no owner because ambiguous" gap. Sev: warning (defaulted). Owner: COUNCIL. Block: none (covered-by-default, tracked).
- Status: ⚠️ requires A5.
Block 2 — Execution / DOT
9. DOT apply writes labels without approval. (mission #3)
- Detect: mutating DOT not approval-gated →
approval_path_gap+ Đ32 §6 lifecycle gate blocks apply without quorum (doc 06). Sev: critical (anarchic — mutates classification). Owner: GOV-DOT + COUNCIL. Event:governance.dot_authority_gap. Block: Đ32 quorum (PG-enforced) + G-IMPL. - Status: ✅ partly (Đ35 §3 paired + Đ32 lifecycle) — ⚠️ but the SoD rule (I4) must be stated so a future refactor can't merge propose+apply.
10. A mutating DOT outside dot_tools (no paired_dot).
- Detect:
dot_authority_gap; Đ35 §3 trigger already rejects tier-B withoutpaired_dot. Sev: critical. Owner: GOV-DOT. Block: PG trigger (live) + CI F-ISLAND-4. - Status: ✅ (already PG-enforced).
11. The coverage scanner itself is unowned (bootstrap orphan).
- Detect: anti-bootstrap (§6.5) — scanner is a governed object; COVERAGE-AUDIT watches SCAN; seed attestation (I3). Sev: critical. Owner: SIV/DOT. Event:
watchdog_fault. Block: seed APR + attestation before first scan. - Status: ⚠️ requires I3 (bootstrap sequence) — else the chicken-egg is hand-waved.
12. The applier DOT also approves its own change (self-grant).
- Detect: SoD rule (I4/C6) — approval is always Đ32 quorum, never the DOT. Sev: critical (island under the roof). Block: CI F-ISLAND + Đ32 lifecycle.
- Status: ⚠️ requires I4 — pack implies SoD via pairing but never forbids self-approval explicitly.
13. PROPOSE files a malformed approval (no valid action_code).
- Detect: I1 — no
assign_governance_owner/grant_exceptionaction-type exists → PROPOSE returnsproposal_blocked. Sev: high (system can't remediate). Block: G-IMPL prerequisite. - Status: ❌ ESCAPES as-is — the pack assumes the action-type exists; without I1 the whole remediation path silently can't run. Caught only after I1 (register action-types).
14. APPLY tries to assign an owner to a law-orphan route (object edge).
- Detect: I2 —
chk_relations_target_typeblocks object edges; APPLY returnsapply_blocked: object_edge_unexpressible. Sev: high (OWNER_GAPpersists). Block: needs §5.4-EXT. - Status: ❌ ESCAPES / inoperable as-is — APPLY cannot do object-grain assignment until §5.4-EXT. This is the deepest structural limit (I2/B7).
Block 3 — Display / truth
15. UI hardcodes classification. (mission #4)
- Detect: CI source scan (G8/K6) of
web/**incl.server/api/**→ F-ISLAND-6. Sev: high. Owner: GOV-MOUT. Event:hardcode_violation/island. Block: G-ROUTE + CI; Test-4 (100% Nuxt=PG) fails. - Status: ⚠️ requires P8 CI (UNVERIFIABLE-pre-CI). The live
health.get.ts totalGap=reduce()is the canonical instance.
16. UI computes a count not backed by a pivot (PIVOT_MISSING).
- Detect:
pivot_coverage_unowned(#14) — grand-total must be a constant-bucket VIEW (Đ26 §MTx). Sev: high. Owner: GOV-SIV. Event:governance.pivot_coverage_gap. Block: G-ROUTE. - Status: ✅ (concept) — ⚠️ requires the pivot to be a governed object (J8).
17. Render shell decides governance state (covered/orphan) in Nuxt.
- Detect: NT-D1-ext — Nuxt must not compute governance state; CI catches. Sev: high. Owner: MOUT. Block: G-ROUTE + Test-4.
- Status: ⚠️ requires P8 CI + the render owner resolved (J6).
Block 4 — Issue / event / scale
18. Event type emitted before registration. (mission #8)
- Detect: Đ45 §3.2 register-before-emit;
event_outboxregistry CHECK fail-closed (verify, I5). Sev: high (queueanti-pattern). Owner: GOV-SIV. Block: producer fails (PG CHECK). - Status: ✅ for events (CHECK) — ⚠️ but issue_type has NO such gate (H2/H4): a DOT writing an unregistered
issue_typeis NOT caught. Caught only after the H2 vocabulary + register-before-write.
19. Scanner reports 1 million duplicate issues. (mission #9)
- Detect:
coalesce_keydedup + aggregate-to-summary + hard per-scan emit ceiling (G7). Sev: warning (summary). Owner: SIV. Event: onegovernance_coverage_degraded+scan_anomaly. Block: emit ceiling caps it. - Status: ⚠️ G7 backstop needed for the unknown high-cardinality case; pack handles known classes.
20. Parent registry coverage hides a child policy gap. (mission #10)
- Detect: B4 anti-hiding — inheritance covers owner-link ONLY; child's own approval/audit/rollback never inherited. Sev: high (child
APPROVAL_PATH_GAP). Owner: child's profile owner. Block: G-IMPL. - Status: ⚠️ requires B4 — without it, the child is masked as covered (the exact red-team escape). This is the highest-value scale fix.
21. A source is missing from the L1 inventory (detector blind).
- Detect: G1 inventory-completeness vs
information_schema/directus_collections/meta_catalog→inventory_gap(critical). Sev: critical. Owner: COUNCIL. Block: G-IMPL (detector provably blind). - Status: ⚠️ requires G1 — without the ground-truth reconciliation, "remembering to add the L1 row" is the new memory-dependence.
22. An owner agency flips draft and dependents silently rot.
- Detect: G3 governance-context trigger re-scans MOUT-owned objects on status flip. Sev: warning→high. Owner: COUNCIL. Event:
owner_gap. Block: G-PROD (touched objects no longer covered). - Status: ⚠️ requires G3 —
changed_since(object)alone misses this (object didn't change, context did).
23. An exempted read-only adapter quietly gains a write endpoint (scope creep).
- Detect: E5 state-fingerprint — exception auto-invalidates on signature change → re-enters orphan +
exception_scope_drift(critical). Sev: critical. Owner: SIV (risk). Block: G-ROUTE + president. - Status: ⚠️ requires E5 — without fingerprinting, the old waiver silently covers the new write path.
24. An object is parked in ignored to make the gate go green.
- Detect: F2 —
ignoredis a gated permanent exception (COUNCIL + law-ref + replacement_plan); ungatedignored=UNRATIFIED_EXCEPTION(critical). Sev: critical. Owner: COUNCIL. Block: G-PROD. - Status: ⚠️ requires F2 — without gating
ignored, the identity closes while the gap hides (accounting fraud analog).
Red-team verdict
| Outcome | Count | Scenarios |
|---|---|---|
| ✅ caught by pack as-is | 3 | 4, 10, 16(concept) |
| ⚠️ caught only after a hardening fix | 19 | 1,2,3,5,6,7,8,9,11,12,15,17,18,19,20,21,22,23,24 |
| ❌ ESCAPES / inoperable even with wording fixes (need substrate change) | 2 | 13 (no APR action-type), 14 (no object edge) |
Conclusion: the un-hardened pack catches only the cases the existing Đ35/Đ45 machinery already enforces (DOT pairing, event registration, PG-visible no-owner tables). The majority of governance-specific escapes require the hardening fixes — most importantly A2/B1 (non-governed class, or #3 floods/misses), B4 (inheritance anti-hiding, or #20 escapes), G1/G2/G3 (detection completeness/freshness, or #1/#21/#22 escape), E1/E5/F2 (exception integrity, or #5/#23/#24 escape). The two hard ❌ (#13, #14) are not wording problems — they need new APR action-types (I1/E2) and the §5.4-EXT object edge (I2/B7) in the substrate. This is the empirical case for NO-GO-until-hardened: the pack as written does not yet stop the attacks it was built to stop.