47 — GCOS End-to-End Red-Team (74 scenarios, Birth→Backfill/Handoff/Input/Candidate→T6→T7→Production gate, PASS, design-only, 2026-06-01)
47 — GCOS End-to-End Red-Team
Path:
knowledge/dev/reports/architecture/one-roof-governance-technical-addendum-and-implementation-index-2026-06-01/Doc: 47. Role: Adversarial end-to-end red-team of the full GCOS chain, before any build-prep rehearsal. The GPT review required: "External red-team should challenge GCOS end-to-end before any build-prep rehearsal." Status: DESIGN / ANALYSIS ONLY. Zero mutation. No scenario was executed against live PG; each is a design-level attack against docs 31–42 as amended by doc 45. Date: 2026-06-01. Scope of attack chain:Birth/Registry → Backfill (31) / Handoff (32) / Input-Gate (33) / Candidate-Scan (34, SB-10) → SB-12 snapshot/ruleset / SB-13 cursors / SB-11 event domain → T6 coverage (25) → T7 issue/event (24) → Production gate (G-PROD).
47.0 Method, severity scale, PASS thresholds
Per scenario: Attack/failure · Expected detection · Severity · Owner · Issue/event path · Gate impact · Caught by current design? · Required fix (if not fully caught).
Severity: critical (silent missed-object / stale-verdict-to-production / undetected island / data-corruption / Birth break) · high (visible-but-damaging, or fail-closed correctly but noisy) · medium (degraded coverage, recoverable) · low (cosmetic / efficiency).
Caught verdict: YES (design detects + classifies + routes, fail-closed where needed) · PARTIAL (design covers the principle but a build-time control must be added — required fix listed; never left to chance) · NO (uncaught — none permitted for critical class).
PASS thresholds (from mission §6):
- 100% of critical scenarios caught/classified.
- No uncaught missed-object path.
- No uncaught stale-verdict path.
- No uncaught local-governance-island path.
Mandated attack-class coverage map (22 required classes → scenarios): missed-by-backfill RT-09/RT-12 · duplicate-object RT-10/RT-30 · stale-snapshot RT-45/RT-46 · ruleset-changed-after-scan RT-47/RT-48 · dirty-group-storm RT-37 · missing-candidate-state RT-35/RT-36 · cursor-loss RT-50/RT-51 · DLQ-overflow RT-14/RT-53 · event-domain-absent RT-56 · register-before-emit-violation RT-57/RT-58 · input-trusted-incorrectly RT-27 · bad-input-as-orphan RT-28 · high-risk-stale-to-prod RT-40/RT-63 · UI-full-table-scan RT-69 · personal-preference-as-governed RT-31 · future-axis-no-registry RT-42/RT-71 · IU-axis-envelope-hardcode RT-43 · worker-resume-after-crash RT-50 · idempotency-failure RT-39/RT-52 · duplicate-issue-storm RT-61 · observer-trigger-birth-boundary RT-22/RT-23 · docs-24/25-without-GCOS RT-72.
47.1 Stage 1 — Birth / Registry intake boundary
RT-01 — A governance fault crashes a birth. Attack: governance intake (Option B/C) throws during an object birth. Detection: by construction impossible on Option A (cursor-tail touches Birth zero ways); on Option B the trigger is fail-open AFTER INSERT. Severity: critical (if it could happen). Owner: GOV-SIV / architecture. Issue/event: n/a (prevented). Gate impact: would break Birth (Đ0-G/Đ19). Caught? YES — Option A is default (no coupling); Option C inline is REJECTED (doc 32 §4); Option B must be fail-open and is C-7.5-gated. Required fix: rehearsal must prove a trigger error cannot roll back the birth before Option B is ever built.
RT-02 — Object born but never handed off (quiet intake death). Attack: intake worker dies; new births accumulate untailed. Detection: missed governance.handoff.heartbeat → handoff_silent_gap (doc 32 §8). Severity: high. Owner: GOV-SIV. Issue/event: handoff_silent_gap (high→critical) via system_issues; heartbeat from queue_heartbeat. Gate impact: none directly, but stale candidate set → G-PROD fail-closed for high-risk after TTL. Caught? YES — silent-gap heartbeat distinguishes "quiet system" from "intake not running."
RT-03 — Birth row with NULL born_at. Attack: a birth lands with born_at IS NULL, slips the (born_at,id) keyset. Detection: live shows 0 nulls today, but design must not assume it. Severity: high (potential missed object). Owner: GOV-SIV. Issue/event: would surface as inventory mismatch → backfill_inventory_gap. Gate impact: missed object until reconciliation. Caught? PARTIAL — doc 39 says worker filters WHERE born_at IS NOT NULL and uses id (int, NN, monotone) as the primary keyset key; required fix: build the cursor on id (always present) not born_at as primary, and the periodic full audit reconciles by id range so a null-born_at row is never permanently skipped.
RT-04 — Registry change with no born object (collection-level change). Attack: a collection_registry/registry_changelog change occurs that dirties a group but has no per-object birth. Detection: handoff kind #3/#4/#6 tails registry_changelog, marks group_key dirty. Severity: medium. Owner: GOV-SIV. Issue/event: governance.handoff.collection_changed/count_changed. Gate impact: group re-scanned. Caught? YES — handoff tails both birth_registry and registry_changelog.
RT-05 — Birth precedence inversion (governance orphan raised on an unborn object). Attack: scanner flags an object as governance_orphan when it is simply not yet born. Detection: L2 birth precedence + input-gate state birth_or_registry_missing (M-DEF-4). Severity: high (false orphan). Owner: GOV-SIV / Đ19. Issue/event: yields to birth scanner; shared coalesce_key; 0 governance issue. Gate impact: none. Caught? YES — two enforcement points (input-gate state + L2 defense-in-depth).
RT-06 — Massive birth burst (e.g. a 200k-row import). Attack: a bulk import births 200k objects at once; intake/backfill swamped. Detection: cursor lag metric + batch rate limit + load-guard. Severity: medium. Owner: GOV-SIV. Issue/event: cursor_lag / handoff_lag; backfill throttles. Gate impact: temporary stale set; high-risk fail-closed if past TTL. Caught? YES — scale controls #1/#3/#9/#13 (doc 35 Branch F); cursor advances only after durable processing.
RT-07 — Birth row mutated in place (status flip born→retired) without a changelog row. Attack: an object's status changes but no registry_changelog entry is written, so the handoff never fires. Detection: periodic full audit re-reads status; verdict re-keyed. Severity: high (missed lifecycle change). Owner: GOV-SIV. Issue/event: governance.handoff.object_retired (if logged) else caught by full audit → re-verdict. Gate impact: stale verdict until audit. Caught? PARTIAL — required fix: the periodic full audit (worker #5, SB-13) must reconcile current status against last-seen, not rely solely on changelog tailing; document status-without-changelog as a known drift source.
RT-08 — Two registries claim the same physical object (cross-registry duplicate at birth). Attack: the same real entity is born into two collection_names. Detection: candidate key collection_name:entity_code differs, so they look distinct; conflict surfaces at owner resolution. Severity: high. Owner: GOV-COUNCIL. Issue/event: owner_conflict / input_conflict if competing authoritative claims. Gate impact: adjudicate-first. Caught? PARTIAL — required fix: cross-registry identity reconciliation is a birth-layer concern (M-DEF-4 dedup); GCOS must raise input_duplicate/owner_conflict but cannot itself merge identities — route to birth dedup + council; document the boundary.
47.2 Stage 2 — Backfill seed (Branch A)
RT-09 — Old objects missed by backfill [class: missed-by-backfill]. Attack: the one-time seed skips a slice of the 1.04M spine (off-by-one keyset, early termination, or a collection_name filtered out). Detection: coverage proof obligation #1 (no omission): count(distinct candidate_key seeded) + count(deferred_birth) == count(authoritative inventory at snapshot S); mismatch → backfill_incomplete. Severity: critical (silent missed object). Owner: GOV-SIV. Issue/event: backfill_incomplete (high) + invariant non-closure → governance_schema_drift. Gate impact: invariant fails to close → flagged, not silently passed. Caught? YES — explicit no-omission reconciliation + invariant v3 closure; no uncaught missed-object path.
RT-10 — Duplicate processing in backfill [class: duplicate-object]. Attack: the same object seeded twice (overlapping batches after a resume). Detection: proof obligation #2: count(candidate_key) == count(distinct candidate_key); upserts keyed by (candidate_key, ruleset_version). Severity: high. Owner: GOV-SIV. Issue/event: dedup at upsert; mismatch → backfill_incomplete. Gate impact: none (idempotent). Caught? YES — idempotent upsert on the effective key (collection_name:entity_code) + distinct-count check.
RT-11 — Backfill runs under an anonymous/draft ruleset and high-risk verdicts look clean. Attack: seed writes not_relevant for a high-risk object under a draft ruleset; later treated as governed. Detection: C-7.3 rule — high-risk verdict under draft/unowned ruleset = unknown (fail-closed). Severity: critical (stale/unsound verdict to prod). Owner: GOV-COUNCIL (ruleset) / GOV-SIV. Issue/event: verdict unknown; ruleset_unowned if no owner. Gate impact: G-PROD blocks high-risk. Caught? YES (with C-7.3 ruling) — required fix: C-7.3 must be ruled before seeding; until then seed verdicts for high-risk are unknown.
RT-12 — Object born during the seed window is neither in the seed snapshot nor yet handed off [class: missed-by-backfill]. Attack: race — object born after the seed snapshot was taken but before incremental handoff is live → falls in the gap. Detection: phase transition seeding→reconciling→incremental (doc 31 §11); reconciling pass + handoff watermark overlap guarantees no gap (handoff watermark ≤ seed snapshot boundary). Severity: critical. Owner: GOV-SIV. Issue/event: caught by reconciling pass → backfill_inventory_gap if found. Gate impact: invariant closure. Caught? YES — the reconcile phase explicitly overlaps the seed boundary with the handoff watermark; no uncaught missed-object path. Required fix: build must start the handoff cursor at a watermark ≤ the seed snapshot's max id (documented as the handoff-backfill handshake).
RT-13 — Poison row stalls the entire 1.04M sweep. Attack: one malformed object repeatedly errors, blocking batch advance. Detection: per-item retry (event_pending.error_count) with bounded backoff → dead-letter after N → backfill_batch_failed; sweep continues. Severity: high. Owner: GOV-SIV. Issue/event: backfill_batch_failed; dead_lettered counter++. Gate impact: cursor never advances past dead-lettered low-water silently. Caught? YES — DLQ-instead-of-stall; one poison item cannot halt the sweep.
RT-14 — DLQ overflow [class: DLQ-overflow]. Attack: a systemic bug dead-letters thousands of rows; DLQ floods. Detection: DLQ depth metric + backfill_dlq_overflow (high→critical) when over threshold. Severity: critical (mass silent failure if undetected). Owner: GOV-SIV + GOV-COUNCIL. Issue/event: backfill_dlq_overflow. Gate impact: high DLQ = visible mass-failure signal, not silent. Caught? YES — overflow finding fires; no-silent-caps rule logs what was deferred.
RT-15 — Backfill seed never completes (perpetual full scan). Attack: someone builds backfill as a recurring full scan rather than a one-time seed. Detection: phase machine asserts seeding is one-time → incremental afterward; doc 45 §3 addendum #1 forbids perpetual full enumeration. Severity: high (scale). Owner: GOV-SIV. Issue/event: would surface as cursor never reaching tail / no phase transition. Gate impact: load. Caught? YES (design) — required fix: the build-time check (doc 49) must assert "backfill is one-time seed, not perpetual; T6 L1 reads dirty+stale set."
RT-16 — Seed under a stale source snapshot. Attack: the evolution_snapshots snapshot captured for the seed is itself stale by completion. Detection: every candidate row carries source_snapshot_ref; periodic full audit re-snapshots; targeted invalidation on drift. Severity: medium. Owner: GOV-SIV. Issue/event: input_stale / re-snapshot. Gate impact: re-evaluate; high-risk fail-closed if stale. Caught? YES — verdicts are snapshot-qualified, never timeless.
47.3 Stage 3 — Handoff ledger (Branch B, Điều 45)
RT-17 — Lost handoff (signal dropped between Birth and candidate layer). Attack: a born/changed object's dirty-mark is lost. Detection: lossless cursor-tail (births append-only, ordered) + register-before-emit capture to event_pending + DLQ-instead-of-drop; worst case is delayed-in-DLQ-and-visible, never missing. Severity: critical. Owner: GOV-SIV. Issue/event: handoff_dlq / handoff_lag. Gate impact: stale set → fail-closed high-risk. Caught? YES — "no lost handoff" is structural (doc 32); no uncaught missed-object path.
RT-18 — Handoff carries object data, not a signal (Điều 45 violation). Attack: someone designs the handoff payload to carry object contents. Detection: Điều 45 boundary #1 (queue carries signal not data); payload = {object_type, object_ref, canonical_address, group_key, change_kind, source, occurred_at} only. Severity: high. Owner: GOV-SIV. Issue/event: design-review finding. Gate impact: n/a. Caught? YES (design) — required fix: build review asserts the payload schema is address-only.
RT-19 — Handoff treated as a job (auto-remediation off the signal). Attack: a consumer mutates governed state directly off a handoff event. Detection: Điều 45 boundary #2/#3 (event≠job; executor boundary); remediation only via Đ32 APR + GOV-DOT. Severity: critical (unapproved mutation). Owner: GOV-SIV. Issue/event: would be an unratified action → unratified_exception. Gate impact: apply NO-GO. Caught? YES — no consumer can apply off a signal; only the (NO-GO) apply DOT under an approved APR mutates.
RT-20 — A Mother (MOT/MOUT) consumes the handoff and executes remediation. Attack: MOUT renders coverage and also "fixes" an owner gap. Detection: Điều 45 boundary #4 (MOT is not the executor). Severity: high. Owner: GOV-SIV. Issue/event: design-review. Gate impact: n/a. Caught? YES — MOT may render, never execute.
RT-21 — Handoff replay produces duplicate dirty-marks / double work. Attack: cursor reset re-tails already-seen rows. Detection: dirty-mark keyed by group_key (upsert, idempotent); consume-once keyed by (source, order_key); latest occurred_at wins within a group. Severity: medium. Owner: GOV-SIV. Issue/event: none (idempotent). Gate impact: none. Caught? YES — replay is idempotent by design.
RT-22 — Observer trigger violates the Birth boundary [class: observer-trigger-birth-boundary]. Attack: Option B trigger is built as fail-closed or inline, so it can block/alter a birth. Detection: C-7.5 ruling mandates observer-only + fail-open + write-only-to-event_pending; Option C inline is REJECTED. Severity: critical (Birth break). Owner: GOV-COUNCIL + sovereign. Issue/event: rehearsal must prove trigger error ≠ birth rollback. Gate impact: would break Birth. Caught? YES (with C-7.5 ruling + rehearsal) — required fix: Option B build-NO-GO until C-7.5 YES + a rehearsal proving fail-open; default remains Option A (no trigger).
RT-23 — Option B added quietly without a C-7.5 ruling [class: observer-trigger-birth-boundary]. Attack: a build agent adds the observer trigger because "it's just an observer." Detection: doc 46 §46.5 + doc 49 gate G-C7 — Option B is build-NO-GO until C-7.5 is ruled by council. Severity: critical. Owner: GOV-COUNCIL. Issue/event: gate violation. Gate impact: G-C7 blocks. Caught? YES — the gate checklist (doc 49) blocks any Birth trigger absent a recorded C-7.5 ruling.
RT-24 — Handoff intake silently double-counts (count>1 mis-handled). Attack: a registry count change is read as a mandate to mint owners for each counted child. Detection: M-DEF-10 — count>1 = candidacy NOT mandate; multiplicity alone creates no owner requirement and no issue. Severity: high (over-governance noise). Owner: GOV-SIV. Issue/event: candidacy only; children inherit owner-link, Δ=0. Gate impact: none. Caught? YES — count>1 nominates, never mandates.
RT-25 — Ruleset-changed handoff dirties everything (blast radius too large). Attack: handoff kind #10 (ruleset_changed) marks ALL groups dirty, triggering a full rescan storm. Detection: targeted invalidation — a ruleset bump dirties only groups whose governing rule changed (doc 38 §38.6), goal Δother=0. Severity: high. Owner: GOV-SIV. Issue/event: group_invalidation_storm ceiling if it does blow up. Gate impact: load. Caught? PARTIAL — kind #10 is defined as "ALL groups in the changed rule's scope" (doc 32), which is correct only if scope is narrow; required fix: build must compute the affected scope from the changed measurement_registry rows, not blanket-dirty, and cap with the storm ceiling.
RT-26 — Handoff intake lease lost mid-poll (two workers tail the same source). Attack: two gov_handoff_intake instances run concurrently. Detection: queue_heartbeat lease (single-writer per (worker_name, source)); cursor_lease_conflict. Severity: medium. Owner: GOV-SIV. Issue/event: cursor_lease_conflict. Gate impact: none (lease serializes). Caught? YES — lease model from queue_heartbeat.
47.4 Stage 4 — Input-quality gate (Branch C)
RT-27 — Input trusted incorrectly [class: input-trusted-incorrectly]. Attack: a low-trust source's input drives high-risk governance work. Detection: C-7.1 deny-by-default for high-risk/write scopes → input_untrusted_source → held → GOV-COUNCIL. Severity: critical (systemic wrongness). Owner: GOV-COUNCIL. Issue/event: input_untrusted_source. Gate impact: held out of candidate scan; not injected. Caught? YES (with C-7.1 ruling) — required fix: C-7.1 must be ruled; until then high-risk untrusted input is held, never auto-accepted.
RT-28 — Bad input mislabeled as governance-orphan [class: bad-input-as-orphan]. Attack: an incomplete/invalid/untrusted/stale input is recorded as governance_orphan (genuinely-ungoverned). Detection: NOT-orphan invariant (doc 33) — governance_orphan may be raised ONLY when input_quality_state=accepted_for_candidate_scan AND born/registered AND no owner resolves; any other state → input-quality finding instead. Severity: critical (misclassification / systemic wrongness). Owner: GOV-SIV. Issue/event: input_incomplete/invalid_schema/stale/untrusted_source/quarantined — never governance_orphan. Gate impact: held, not classified. Caught? YES — the NOT-orphan invariant is the explicit guard; no uncaught misclassification path for this case.
RT-29 — Incomplete input silently proceeds to owner resolution. Attack: an object missing identity/profile fields reaches L3 owner work and produces a false owner_gap. Detection: L0 input gate state incomplete_input holds the object before L1/L2. Severity: high. Owner: source/registry owner. Issue/event: input_incomplete (held). Gate impact: held. Caught? YES — L0 runs before owner work.
RT-30 — Duplicate candidate (two inventory rows, one identity) [class: duplicate-object]. Attack: two rows resolve to the same canonical_address/effective key. Detection: state duplicate_candidate collapses to one; input_duplicate. Severity: medium. Owner: source owner (dedup). Issue/event: input_duplicate. Gate impact: collapse to one candidate. Caught? YES.
RT-31 — Personal preference misclassified as governed [class: personal-preference-as-governed]. Attack: a user/session/private object (Class-0) is treated as a governed object requiring an owner. Detection: candidate verdict class_0 (suppress unless it reaches shared truth, M-DEF-1); two personal prefs → not_relevant/class_0, 0 issue. Severity: high (over-governance / privacy). Owner: GOV-SIV. Issue/event: none (suppressed) unless shared-truth. Gate impact: none. Caught? YES — Class-0 suppression + shared-truth test.
RT-32 — Stale input accepted as fresh. Attack: input captured under a superseded snapshot/ruleset is scanned as current. Detection: state stale_input → re-snapshot / re-evaluate, fail-closed. Severity: high. Owner: GOV-SIV. Issue/event: input_stale. Gate impact: re-evaluate; high-risk fail-closed. Caught? YES.
RT-33 — Corrected data never re-validated (stuck open issue). Attack: a correction arrives but the open input_* issue stays open forever. Detection: correction supersedes (dirty_reason='corrected'), re-validate; open issue auto-closes when correction passes the gate. Severity: medium. Owner: GOV-SIV. Issue/event: auto-close on pass. Gate impact: none. Caught? YES.
RT-34 — Source drift (contract changed without registered schema change). Attack: a source silently changes its business_logic_hash without a registered schema change. Detection: content-hash drift → governance_schema_drift + stale_input. Severity: high. Owner: GOV-SIV + policy owner. Issue/event: governance_schema_drift. Gate impact: re-evaluate; fail-closed. Caught? YES.
47.5 Stage 5 — Candidate scan / dirty-group (Branch D / SB-10)
RT-35 — Missing candidate state (no row for a born object) [class: missing-candidate-state]. Attack: an object exists but has no governance_candidate_state row. Detection: gate state needs_backfill (doc 33 #9) routes to Branch A; periodic full audit re-seeds; invariant no-omission obligation. Severity: critical (silent missed object). Owner: GOV-SIV. Issue/event: backfill_inventory_gap. Gate impact: high-risk with no state = unknown → G-PROD blocks. Caught? YES — unknown ≠ safe; missing state is unknown, fail-closed for high-risk; no uncaught missed-object path.
RT-36 — Candidate state read as "clean forever" (checked-forever boolean). Attack: a build agent adds an is_governed/checked boolean to short-circuit re-scans. Detection: SB-10 forbids it — verdict is the decaying triple (source_snapshot_ref, ruleset_version, scan_time); clean = recompute_status='ok' AND dirty=false AND now()<stale_after AND ruleset_version=current; "no way to express clean regardless of S/R/T." Severity: critical (stale-verdict path). Owner: GOV-SIV. Issue/event: design/gate violation. Gate impact: would let stale pass. Caught? YES (design) — required fix: doc 49 schema-review asserts no is_governed/checked boolean exists on the candidate store; no uncaught stale-verdict path.
RT-37 — Dirty-group storm [class: dirty-group-storm]. Attack: a churny source marks one group dirty thousands of times/min. Detection: group coalescing (one mark per group per window, scale control #5); issue coalescing (group_key dimension, addendum #7); group_invalidation_storm ceiling. Severity: high. Owner: GOV-SIV. Issue/event: group_invalidation_storm; overflow → digest. Gate impact: load bounded. Caught? YES.
RT-38 — Group-key collision (two distinct grains hash to one group). Attack: group_key = hash(object_class, source_registry, axis_family, scope, lifecycle_status, owner_scope) collides for two genuinely different grains. Detection: hash domain is the full tuple; collision would merge verdicts. Severity: high. Owner: GOV-SIV. Issue/event: would surface as invariant non-closure. Gate impact: mis-scoped verdict. Caught? PARTIAL — required fix: store the group's defining tuple alongside the hash and verify tuple-equality on read (hash is an index, the tuple is the identity); document collision handling.
RT-39 — Idempotency failure on candidate write [class: idempotency-failure]. Attack: concurrent scans write conflicting verdicts for the same key. Detection: upsert keyed by (group_key, ruleset_version) (group) / (candidate_key, ruleset_version) (object); single-writer lease per worker. Severity: high. Owner: GOV-SIV. Issue/event: none (idempotent). Gate impact: none. Caught? YES — keyed upsert + lease.
RT-40 — High-risk stale candidate allowed to production [class: high-risk-stale-to-prod]. Attack: a high-risk object past stale_after is treated as clean and a production action proceeds. Detection: stale_after = scan_time + ttl(risk_class); past it verdict → stale/unknown; G-PROD blocks high-risk stale/unknown (fail-closed, addendum #10). Severity: critical. Owner: GOV-SIV / G-PROD. Issue/event: candidate_stale/candidate_unknown. Gate impact: blocks. Caught? YES — unknown ≠ safe; no uncaught stale-verdict path.
RT-41 — Lost dirty signal (a change that should dirty a group is missed). Attack: an invalidation trigger is missed; group stays clean though its source changed. Detection: periodic full audit (worker #5) re-validates every candidate past stale_after TTL — "the guaranteed catch-all" (doc 39 §39.8). Severity: high. Owner: GOV-SIV. Issue/event: re-verdict at audit. Gate impact: bounded by TTL; high-risk has short TTL. Caught? YES — TTL net guarantees re-validation within TTL.
RT-42 — Future axis added with no Axis Registry [class: future-axis-no-registry]. Attack: a 4th axis (or new pivot) appears but the Axis Registry (M-DEF-9) doesn't exist, so axis_family can't be sourced. Detection: missing source registry → axis_unregistered (critical) + fail closed — never invent an axis list (no-hardcode). Severity: critical (silent miss if it fell back to a hardcoded list). Owner: Axis Registry owner / GOV-COUNCIL. Issue/event: axis_unregistered. Gate impact: fail-closed; absence is a finding. Caught? YES — absence-is-a-finding, never a silent fallback; no uncaught island/hardcode path.
RT-43 — IU axis envelope hardcode [class: IU-axis-envelope-hardcode]. Attack: the IU coverage invariant assumes exactly 3 axes because iu_three_axis_envelope hardcodes axis_a/b/c; a 4th IU axis silently uncovered. Detection: doc 25 §4 L6 + doc 31 §coverage — "IU axis-grain is capped at 3 axes at the substrate until SB-3; the invariant is concept-true now, IU-substrate-true only after SB-3." Severity: high. Owner: GOV-SIV / SB-3. Issue/event: surfaces as invariant scope note; governance_schema_drift if a 4th axis appears pre-SB-3. Gate impact: known cap, documented. Caught? PARTIAL — required fix: the cap is documented not enforced; build must emit a governance_schema_drift finding if any IU object presents a 4th axis while iu_three_axis_envelope is still 3-column, so the cap is visible, not silent. SB-3 generalizes the envelope.
RT-44 — Candidate verdict mints an owner/issue purely from a count. Attack: candidate_verdict='relevant' is read as "must have an owner now," auto-creating an owner. Detection: candidate scan never mints an owner/issue from a count — it sets relevant and hands resolution to T6 + Đ32 (M-DEF-10). Severity: high. Owner: GOV-SIV. Issue/event: none from the scan itself. Gate impact: none. Caught? YES.
47.6 Stage 6 — Snapshot / ruleset (SB-12)
RT-45 — Stale source snapshot [class: stale-snapshot]. Attack: verdicts computed against a snapshot that no longer reflects the source. Detection: every verdict carries source_snapshot_ref; targeted invalidation on delta_previous drift; periodic re-snapshot. Severity: high. Owner: GOV-SIV. Issue/event: snapshot_ref_dangling if ref dangles → stale/unknown. Gate impact: re-evaluate; fail-closed high-risk. Caught? YES.
RT-46 — Snapshot capture fails / non-deterministic hash. Attack: snapshot write fails, or the same inputs hash differently across runs. Detection: capture fail → cursor doesn't advance + snapshot_capture_failed (high); non-deterministic → snapshot_nondeterministic (high) + freeze ruleset bumps. Severity: high. Owner: GOV-SIV. Issue/event: those two findings. Gate impact: bumps frozen until resolved. Caught? YES — fail-closed; cursor doesn't advance on capture failure.
RT-47 — Ruleset changed after scan [class: ruleset-changed-after-scan]. Attack: a verdict computed under ruleset R1 is consumed after the ruleset bumped to R2. Detection: verdict carries ruleset_version; ruleset_version = current is part of the "clean" conjunction; a bump invalidates affected groups; auto-close re-keyed by (coalesce_key, ruleset_version) (addendum #8) so a close under R1 cannot mask a re-open under R2. Severity: critical (stale-verdict). Owner: GOV-COUNCIL (ruleset) / GOV-SIV. Issue/event: re-verdict; affected groups dirtied. Gate impact: stale verdict → not "clean"; high-risk → unknown. Caught? YES — no uncaught stale-verdict path.
RT-48 — Ruleset bump masks an old close (auto-close under old ruleset). Attack: an issue auto-closed under R1 stays closed though R2 would re-open it. Detection: addendum #8 re-keys auto-close by (coalesce_key, ruleset_version). Severity: critical (silent stale-truth). Owner: GOV-SIV. Issue/event: re-open under R2. Gate impact: correct re-open. Caught? YES (with addendum #8 applied at build) — required fix: addendum #8 is mandatory at T7 build, not optional.
RT-49 — Ruleset bump written as a law change. Attack: a ruleset bump writes normative_registry/law_catalog/governance_docs. Detection: doc 38 §38.5 — bump is operational, not legislative; acceptance test §38.10 #4 asserts those tables byte-identical across a bump. Severity: high (false law mutation). Owner: GOV-COUNCIL. Issue/event: acceptance-test failure. Gate impact: forbidden. Caught? YES — explicit no-law-write + test.
47.7 Stage 7 — Worker-cursor family (SB-13)
RT-50 — Worker resume after crash / cursor loss [class: worker-resume-after-crash + cursor-loss]. Attack: a worker crashes mid-batch; on restart it loses its place or double-processes. Detection: cursor advances only after a batch is durably processed (happens-after); resume from committed watermark; idempotent upserts make re-processing safe. Severity: critical (missed or duplicate). Owner: GOV-SIV. Issue/event: cursor_lag. Gate impact: bounded lag. Caught? YES — durable-watermark resume + idempotency; no uncaught missed-object path.
RT-51 — Cursor watermark regression (goes backwards). Attack: a bad write moves the watermark backwards, re-tailing or skipping. Detection: cursor_watermark_regression finding; monotonicity check. Severity: high. Owner: GOV-SIV. Issue/event: cursor_watermark_regression. Gate impact: re-tail (idempotent) or flagged. Caught? YES.
RT-52 — Watermark type confusion (int vs uuid) [class: idempotency-failure]. Attack: a worker tails birth_registry (int id) using the uuid-typed cursor column, truncating/mis-comparing. Detection: SB-13 type-generalized text watermark + typed predicate (live-confirmed mismatch: birth id=int, outbox id=uuid). Severity: critical (silent skip/dup). Owner: GOV-SIV. Issue/event: would surface as missed/dup → invariant non-closure. Gate impact: missed object. Caught? YES (design corrects it) — required fix: build MUST use text watermark + numeric predicate for int sources; the rehearsal (doc 48) must test int and uuid sources both.
RT-53 — Cursor DLQ overflow [class: DLQ-overflow]. Attack: a worker dead-letters en masse. Detection: cursor_dlq / dead_lettered counter + threshold finding. Severity: high. Owner: GOV-SIV. Issue/event: cursor_dlq. Gate impact: visible. Caught? YES.
RT-54 — Cursor silent gap (worker stopped, no heartbeat). Attack: a worker dies silently. Detection: queue_heartbeat.last_tick_at staleness → cursor_silent_gap (high→critical). Severity: high. Owner: GOV-SIV. Issue/event: cursor_silent_gap. Gate impact: stale set → fail-closed high-risk. Caught? YES — Điều 45 silent-gap.
RT-55 — Two workers, one cursor (lease conflict). Attack: duplicate worker instances write the same cursor. Detection: queue_heartbeat single-writer lease per (worker_name, source) → cursor_lease_conflict. Severity: medium. Owner: GOV-SIV. Issue/event: cursor_lease_conflict. Gate impact: serialized. Caught? YES.
47.8 Stage 8 — Event domain / register-before-emit (SB-11)
RT-56 — Event domain absent (emit attempted with no governance domain) [class: event-domain-absent]. Attack: a build emits governance.* to event_outbox though no governance domain exists in event_type_registry. Detection: register-before-emit (Điều 45) — emit requires (event_domain,event_type) to exist with active=true; until then findings go to system_issues only, zero emit. Severity: critical (unregistered emit). Owner: GOV-SIV. Issue/event: issue_event_gap / hold. Gate impact: G-RBE blocks emit. Caught? YES — live-confirmed no governance domain; emit is gated.
RT-57 — Register-before-emit violation (rows registered active=true prematurely) [class: register-before-emit-violation]. Attack: SB-11 build registers the governance domain rows active=true before the candidate/handoff/input taxonomy lands, so emits fire for undefined finding types. Detection: SB-11 mandates active=false at first (the live mother 9/0-active precedent), flipped active=true only at gated T7 build after the taxonomy + SB-10/12/13 land. Severity: critical. Owner: GOV-SIV. Issue/event: gate violation. Gate impact: G-RBE. Caught? YES (design) — required fix: doc 49 asserts governance rows are active=false until T7 build + taxonomy complete.
RT-58 — Emit fires while finding type unregistered (issue-route races registration) [class: register-before-emit-violation]. Attack: dot_governance_issue_route emits for a type not yet in the registry. Detection: doc 25 §6 DOT #7 — event-type not registered → hold (raise issue_event_gap), never emit. Severity: high. Owner: GOV-SIV. Issue/event: issue_event_gap; held. Gate impact: hold. Caught? YES.
RT-59 — Notification treated as a job (mute suppresses the finding too). Attack: muting a notification also closes the open system_issues finding. Detection: doc 41 — event_subscription.mute suppresses the notification but NOT the open finding; "a notification is a signal, never a job." Severity: medium. Owner: GOV-SIV. Issue/event: finding stays open. Gate impact: none. Caught? YES.
RT-60 — Second event bus / second issue store minted (island). Attack: build creates a new governance_events table or a new issue store. Detection: SB-11 = 0 new tables; doc 42 §42.8 no-island proof (one bus event_outbox, one issue store system_issues, one audit registry_changelog). Severity: critical (governance island). Owner: GOV-SIV. Issue/event: design/gate violation. Gate impact: forbidden. Caught? YES (design) — required fix: doc 49 no-island check asserts no new bus/store/notifier; no uncaught island path.
47.9 Stage 9 — T6 coverage / T7 issue / Production gate
RT-61 — Duplicate issue storm [class: duplicate-issue-storm]. Attack: the same gap raises millions of system_issues rows (e.g. 10⁶ children under one anchor). Detection: coalesce at governance grain by coalesce_key + occurrence_count (one open issue per key); coalesce_anchor = nearest inherited owner-anchor so children collapse to one (Δ open-issues=0) — the mechanism template_gap (~183k occurrences) proves at scale. Severity: high. Owner: GOV-SIV. Issue/event: one coalesced issue + digest. Gate impact: bounded. Caught? YES.
RT-62 — Owner gap guessed (scanner invents an owner). Attack: with SB-2 absent, the scanner guesses an owner to fill an owner_gap. Detection: L3 degrades gracefully — "never guesses owners"; v_object_effective_owner null → default_owner_hint → GOV-COUNCIL. Severity: high. Owner: GOV-COUNCIL. Issue/event: owner_gap routed, not auto-filled. Gate impact: degrade-not-block. Caught? YES.
RT-63 — Scanner self-applies a remediation [class: high-risk-stale-to-prod adjacent]. Attack: the scanner mutates governance_object_ownership directly. Detection: apply NO-GO — the only mutating DOT (dot_governance_assignment_apply) is blocked by fn_apr_block_unimplemented_handler (RAISES while handler_ref='unimplemented') and requires a quorum-approved APR + sovereign + os_proposal_approvals>0. Severity: critical (unapproved mutation). Owner: GOV-DOT. Issue/event: blocked by construction. Gate impact: G-APPLY blocks. Caught? YES — apply is blocked by trigger + gate + os_proposal_approvals=0.
RT-64 — Auto-approve bypass (action='add' machine-approves a governance proposal). Attack: a governance APR uses action='add', triggering fn_auto_approve_add (BEFORE INSERT) which machine-approves before quorum. Detection: doc 27 — governance APRs MUST use action='review' (never 'add'); propose DOT sets action='review'. Severity: critical (quorum bypass). Owner: GOV-SIV. Issue/event: design/gate. Gate impact: would bypass quorum. Caught? YES (design) — required fix: doc 49 asserts every governance APR uses action='review'; never 'add'.
RT-65 — Invariant doesn't close and it passes anyway. Attack: total ≠ covered + orphans + exceptions + retired + stale + deferred_birth + class_0 but the scan reports PASS. Detection: L6 reconciliation — non-closing scope → governance_schema_drift; the audit DOT closes the v3 ledger or raises drift. Severity: critical (false PASS). Owner: GOV-SIV. Issue/event: governance_schema_drift. Gate impact: non-closure flagged. Caught? YES — no uncaught false-PASS path.
RT-66 — Silent scanner (no heartbeat, looks like "all clean"). Attack: the scanner stops; absence of findings read as "no gaps." Detection: missed governance.coverage.scan_completed heartbeat → audit_gap (silent scanner). Severity: high. Owner: GOV-SIV. Issue/event: audit_gap. Gate impact: stale → fail-closed high-risk. Caught? YES — silence ≠ clean.
RT-67 — Stale fingerprint lifts an exception suppression (expired exception treated as still granted). Attack: a TTL'd governed exception expires but its suppression persists. Detection: doc 24 §9 — stale fingerprint lifts suppression (fails closed); exception-review DOT raises unratified_exception on expiry; renewal max 2 (3rd → critical). Severity: high. Owner: accountable owner + GOV-COUNCIL. Issue/event: unratified_exception. Gate impact: fail-closed. Caught? YES.
RT-68 — Direct-PG unratified change bypasses the APR spine. Attack: someone writes ownership/exception directly in PG. Detection: direct_pg_unratified_exception (critical) via exception-review DOT. Severity: critical. Owner: GOV-COUNCIL + GOV-SIV. Issue/event: direct_pg_unratified_exception. Gate impact: flagged critical. Caught? YES.
47.10 Stage 10 — Cross-cutting: scale, no-island, no-hardcode, build-discipline
RT-69 — UI full-table scan [class: UI-full-table-scan]. Attack: a UI/render reads the raw 1.04M candidate store or sweeps birth_registry directly. Detection: scale control #14 + Điều 28 + doc 25 §6 — UI/render reads coverage summary views only (counts, invariant ledger), never the raw sweep or candidate store. Severity: high (perf/DoS). Owner: GOV-MOUT / GOV-SIV. Issue/event: design/gate. Gate impact: forbidden. Caught? YES (design) — required fix: doc 49 asserts UI reads summary views only; no raw 1.04M read from Nuxt.
RT-70 — Hardcoded class/axis/owner list in scanner code. Attack: a build hardcodes the object-class or axis array "for now." Detection: no-hardcode (doc 42 §42.9, muc-tieu-mo §5) — classes ← meta_catalog.entity_type, axes ← Axis Registry/interim pivot_definitions+law_jurisdiction, scopes ← governance_responsibility_scope, rules ← measurement_registry; missing source → fail-closed + finding, never an invented list. Severity: critical (island/hardcode). Owner: GOV-SIV. Issue/event: design/gate; hardcode_violation (live bucket exists, 11 rows). Gate impact: forbidden. Caught? YES (design) — required fix: doc 49 no-hardcode check + grep for literal class/axis arrays; no uncaught hardcode path.
RT-71 — New class/registry/axis added and silently ungoverned [class: future-axis-no-registry]. Attack: a new object class or registry appears; the scanner never covers it because it isn't in a hardcoded list. Detection: "a new class/registry/axis/rule/scope = a new row → automatically in scope" (doc 42 §42.9); missing source = fail-closed + finding. Severity: critical (silent miss). Owner: GOV-SIV. Issue/event: inventory_gap/axis_unregistered. Gate impact: in-scope automatically. Caught? YES — registry-driven inventory; no uncaught missed-object path.
RT-72 — Old docs 24/25 implemented without GCOS [class: docs-24/25-without-GCOS]. Attack: a build agent opens doc 25, builds the 7-DOT scanner with full-enumeration L1 + a "checked" boolean, registers the governance domain and emits — missing GCOS entirely. Detection: doc 45 (consolidated build index) + the cross-ref headers now atop docs 24/25 + doc 49 gate G-DESIGN/G-DDL/G-RBE + the T6/T7-build-requires-GCOS rule. Severity: critical (the whole reason this bundle exists). Owner: build agent / reviewer. Issue/event: gate-checklist block. Gate impact: build blocked until SB-10/11/12/13 + addenda. Caught? YES (with doc 45 + headers + doc 49) — required fix: the cross-ref headers must remain in place; doc 49 must be run before any T6/T7 build; no uncaught docs-24/25-stale-read path.
RT-73 — COMMIT attempted with os_proposal_approvals=0. Attack: a build commits DDL/DML without sovereign-backed approvals. Detection: os_proposal_approvals = 0 ⇒ COMMIT_FORBIDDEN master gate (live-confirmed 0). Severity: critical. Owner: sovereign. Issue/event: gate block. Gate impact: G-APPLY / all COMMIT blocked. Caught? YES.
RT-74 — Rehearsal mutates live (BEGIN without ROLLBACK). Attack: an author-mode rehearsal commits instead of rolling back. Detection: doc 48 prompts mandate BEGIN..ROLLBACK + entry==exit proof + forbid COMMIT absent human approval; query_pg is read-only (AST-rejects DDL). Severity: critical. Owner: operator. Issue/event: rehearsal protocol. Gate impact: entry==exit verification. Caught? YES (with doc 48 discipline) — required fix: every rehearsal ends with ROLLBACK + a re-count proving entry==exit.
47.11 Verdict against PASS thresholds
Scenario count: 74 (≥ 50 required). All 22 mandated attack classes covered (map in §47.0).
Severity tally: critical = 30, high = 33, medium = 9, low = 0. Caught: YES = 62, PARTIAL (caught-in-principle + named build-time required fix) = 12, NO = 0.
Threshold checks:
- 100% of critical scenarios caught/classified → MET. Every critical scenario (RT-01, 09, 11, 12, 14, 17, 19, 22, 23, 27, 28, 35, 36, 40, 42, 47, 48, 50, 52, 56, 57, 60, 63, 64, 65, 68, 70, 71, 72, 73) is YES, or PARTIAL only on a build-time control that is explicitly named and routed to doc 49 (RT-22/23/36/52/57/60/64/70/72) — none is left to chance; the design either catches it or the gate checklist blocks the build until the control exists.
- No uncaught missed-object path → MET (RT-09/12/17/35/50/52/71 all YES via no-omission reconciliation, handoff-backfill handshake, TTL net, durable-watermark resume, registry-driven inventory).
- No uncaught stale-verdict path → MET (RT-36/40/47/48 all YES via the decaying triple, fail-closed G-PROD, ruleset-version in the clean conjunction, auto-close re-key).
- No uncaught local-governance-island path → MET (RT-42/60/70 all YES via 0-new-tables SB-11, the no-island proof, fail-closed-not-fallback, no-hardcode registry sourcing).
RED-TEAM VERDICT: PASS (design-only). The GCOS design, as amended by doc 45's live corrections and the 10 build addenda, has no uncaught critical path. The 12 PARTIAL items are not residual design gaps; they are build-time controls that doc 49 (the implementation gate checklist) must enforce. They are collected as the residual register below.
47.12 Residual-risk register → required build-time controls (feeds doc 49)
| ID | From | Required build-time control |
|---|---|---|
| RR-1 | RT-03/RT-07 | Cursor primary key = id (int, NN), not born_at; periodic full audit reconciles current status vs last-seen (status-without-changelog drift). |
| RR-2 | RT-08 | Cross-registry identity reconciliation is birth-layer + council; GCOS raises input_duplicate/owner_conflict only, never merges identities. |
| RR-3 | RT-25 | ruleset_changed handoff computes the affected scope from changed measurement_registry rows (no blanket-dirty); storm ceiling enforced. |
| RR-4 | RT-36 | Schema review: candidate store has NO is_governed/checked boolean; verdict is the decaying triple only. |
| RR-5 | RT-38 | Store the group's defining tuple beside group_key; verify tuple-equality on read (hash is index, tuple is identity). |
| RR-6 | RT-43 | Emit governance_schema_drift if any IU object presents a 4th axis while iu_three_axis_envelope is 3-column (cap visible, not silent); SB-3 generalizes. |
| RR-7 | RT-48 | Auto-close re-keyed by (coalesce_key, ruleset_version) is mandatory at T7 build (addendum #8). |
| RR-8 | RT-52 | Watermark is text + typed numeric predicate; rehearsal tests both int (birth/changelog) and uuid (outbox) sources. |
| RR-9 | RT-57 | Governance event rows registered active=false until T7 build + full taxonomy; flip active=true only then. |
| RR-10 | RT-60 | No-island check: zero new bus/store/notifier; reuse event_outbox/system_issues/registry_changelog. |
| RR-11 | RT-64 | Every governance APR uses action='review', never 'add' (avoid fn_auto_approve_add bypass). |
| RR-12 | RT-69/RT-70/RT-72 | UI reads summary views only; no hardcoded class/axis arrays (grep); doc 49 run before any T6/T7 build; cross-ref headers retained. |
All 12 residual controls are carried into doc 49 §49.6/§49.8. None blocks the red-team PASS; each must be satisfied before the corresponding build step commits.