09 — Governance Coverage Invariant v2 (Branch I) (2026-06-01)
09 — Governance Coverage Invariant v2 (Branch I)
Revises the coverage invariant to avoid scale traps and to absorb future axes, exceptions, drafts, previews, and bulk children without flooding or hiding. Builds on M-DEF-7 (governance grain) and the severity-aware gate. Defines completion formulas + acceptance tests. No SQL designed here (doc 10 sets detection obligations).
9.1 The grain (why v1 didn't scale)
v1 implicitly counted per-object, so 10⁸ inheriting children would make the identity unmaintainable and let gaps hide. v2 computes the identity at the governance grain:
Governance grain = roots + non-inheriting classes + containers. Inheriting leaf records are NOT counted individually. Adding 10⁶ inheriting children changes
total_governedby 0.
Non-inheriting classes (always counted individually, never aggregated away): policy, axis, DOT/action, route/surface, exception, law/design, event-type, IU-operation — i.e. every class whose authority links are per-object (doc 06 §6.4).
9.2 The accounting identity
At the governance grain, for the in-scope population (Class 1–12; Class 0 excluded):
total_governed
= covered
+ orphans (missing ≥1 mandatory link; split below)
+ approved_exceptions (covered-by-exception; NOT owners)
+ retired_or_approved_ignore (gated permanent exceptions only)
+ stale_unverifiable (cannot be evaluated this scan; bounded)
with the orphan term decomposed by severity for the gate:
orphans = anarchic_critical (authority-critical link missing on a mutating/high-risk object)
+ high_orphans (authority link missing, non-critical)
+ warning_orphans (descriptive link missing; e.g. design_ref, audit on RO)
Invariant (must hold every scan): covered + Σorphans + approved_exceptions + retired_or_approved_ignore + stale_unverifiable == total_governed. If it doesn't balance, the scanner itself is faulted (scan_integrity_fail, critical) — a closure check, not an absolute count (the population fluctuates with births; see prior packages' net_gap proofs).
9.3 Category rules (the scale-trap absorbers)
| Category | Rule | Why |
|---|---|---|
| container-grain inheritance | owner-link inherits to members; risk links per object | 10⁶ children, 0 added rows; no hiding (M-DEF-7) |
| future axes | each registered axis is a governance-grain object (Class 3); an unregistered axis is axis_unregistered (counts as anarchic_critical until registered) |
open model (doc 02); future axis enters the identity automatically |
| non-governed (Class 0) | excluded from total_governed; the exclusion list is COUNCIL-owned |
no over-governance noise (M-DEF-1) |
| governed exceptions | counted in approved_exceptions, not covered; an exception is a coverage state, not an owner |
B5 resolution; exceptions stay visible, never disappear into "covered" |
| retired objects | counted in retired_or_approved_ignore; must have a retire record (Đ30) |
retired ≠ orphan, but ≠ silently gone |
ignored objects |
only counted in retired_or_approved_ignore if a gated exception exists (COUNCIL + law-ref + replacement_plan); ungated ignored = unratified_exception (critical) |
red-team #24, no gate-gaming |
| draft objects | a draft object is in-scope but gated by phase (G-DESIGN tier, doc 9.4); a draft owner-agency triggers context re-scan of dependents (G3) | drafts don't escape; but they don't block production they aren't in yet |
| preview artifacts | Class 7 EXCEPTION (temporary route) or Class 0 if non-shared; counted as approved_exceptions if shared/preview-route |
preview is governed-as-exception, not invisible |
| production artifacts | Class 6 SURFACE; full mandatory links; G-ROUTE/G-PROD gates apply | production is the strictest tier |
| bulk child records | aggregated under their container at the governance grain; per-row issues forbidden (doc 11) | 10⁸ scale, no per-row spam |
| policy/action/route/exception | never inherit risk links; each evaluated individually even under a covered parent | anti-hiding (red-team #20/#28) |
9.4 The gate predicate (severity-aware, tiered)
The gate is NOT covered == total_governed. It is:
GATE(phase, touched_objects) passes ⟺ for every object in
touched_objectsrelevant tophase, there are zeroanarchic_criticaland zerohigh_orphans.warning_orphansdo not block but create a deadline-tracked TARGET (default 30 days, OQ-F3).infois ignored.
Tiered by phase (K1):
| Phase | Gate scope | Blocks on |
|---|---|---|
| G-DESIGN | design artifacts (Class 8) | §0-GOV hook missing; unowned design |
| G-IMPL | objects being implemented | owner gap, approval-path gap, dot-authority gap (critical/high) |
| G-ROUTE | routes/surfaces (Class 6) | route_orphan, hardcode_violation, unratified Direct-PG exception |
| G-PROD | production promotion | any critical/high on touched objects; ungated ignored; expired exception |
Waiver: only the president, TTL-bounded, recorded (K3). A waiver is itself a governed exception (Class 7) — so it has a replacement plan and expiry.
9.5 The stale/unverifiable bound
stale_unverifiable must be bounded and decreasing, never a dumping ground:
- an object lands here only if a ground-truth source is unreachable this scan (e.g. file/config substrate offline);
- it carries an age;
stale_unverifiableolder than a threshold escalates tohigh(it's effectively uncovered); - a growing
stale_unverifiablecount is itself an anomaly (scan_coverage_degraded).
9.6 Completion formulas (acceptance-testable)
- Closure:
covered + Σorphans + approved_exceptions + retired_or_approved_ignore + stale_unverifiable == total_governed(every scan). - Grain stability:
Δtotal_governed == 0when only inheriting children are added. - Coverage ratio:
coverage_ratio = covered / (total_governed − retired_or_approved_ignore); a health metric, not the gate. - Gate:
GATE(phase, touched) = (count(anarchic_critical ∪ high_orphans in touched∩phase) == 0). - Exception visibility:
approved_exceptionsis always reported and never folded intocovered. - Anti-hiding: for any covered container C with member M, M's risk-link gaps are reported independently of C's coverage.
9.7 Acceptance tests
| Test | Setup | Expected |
|---|---|---|
| AT-1 grain | add 10⁶ inheriting IU pieces under a covered doc | Δtotal_governed == 0; closure holds |
| AT-2 anti-hiding | child policy (no approval path) under covered parent | child APPROVAL_PATH_GAP (high); gate G-IMPL fails |
| AT-3 exception | park object with approved exception | appears in approved_exceptions, not covered; gate passes only if not touched-and-critical |
| AT-4 ignored | park object in ignored without gated exception |
unratified_exception (critical); gate G-PROD fails |
| AT-5 future axis | introduce a pivot group-by not in Axis Registry | axis_unregistered (anarchic_critical); gate fails |
| AT-6 draft | object in draft phase | counted, G-DESIGN gate applies, G-PROD not (not promoted) |
| AT-7 stale | ground-truth source offline | object → stale_unverifiable (bounded); aged ones escalate to high |
| AT-8 closure-fault | inject a miscount | scan_integrity_fail (critical) |
| AT-9 severity | touched object with only a warning gap | gate passes; 30-day TARGET created |
9.8 Why v2 handles scale AND inheritance
The two failure modes v1 risked — (a) flooding at 10⁸ and (b) gaps hiding behind containers — are resolved by the same grain choice plus the link-split: aggregate the owner-link up the container tree (cheap, no flooding), keep the risk links per object (safe, no hiding). The identity is a closure check, not an absolute, so it stays true under continuous births. Exceptions and ignored are explicit terms so nothing disappears into "covered."
Branch-I verdict
Invariant v2 is scale-safe (grain stability Δ=0), non-hiding (risk links per object), honest (exceptions/ignored/stale are explicit terms, not hidden in covered), and gated by severity+phase (not by a boolean). Nine acceptance tests pin the behavior. Future axes enter the identity automatically via the Axis Registry.