04 · Full-Population Drill Invariant Design (predicates, statuses, UI behavior)
04 · Full-Population Drill Invariant Design
Area 3 verdict: MISSING (P0). No view evaluates a drill invariant across the full node population. Live evidence: 12/87 nodes violate the invariant in the deployed-intended v1 contract (01 §4), unflagged at the population level.
1. The invariant (informal)
For every node and every child-group node in the contract, exactly one of these must hold, and it must be computed, not asserted:
- the node drills (has real children that cover its count), or
- the node shows substrate that actually represents its count, or
- the node carries an expected-blocker status (needs-grouping / empty-by-design / authority-blocked) that the UI renders honestly — never a green substrate page that misrepresents the count.
A node that has count_value>1, no children, and routes to a substrate page that does not contain those count_value objects is a false affordance (case 12 / the 10 PXT nodes). The invariant must mark it FAIL, not PASS.
2. Predicate spec — v_rp_full_population_drill_invariant
Source: v_rp_universal_node_ui_contract_current (all 87 nodes) UNION the child-group nodes from v_rp_pxt_grouping_surface (so grouped children are also covered → true full population, not just top nodes).
Per node compute drill_integrity from these rules (first matching FAIL wins; else first matching expected-blocker; else PASS):
| Rule | Predicate | Result |
|---|---|---|
| R0 non-null route | drill_action IS NULL OR next_route IS NULL |
FAIL_NULL_ROUTE |
| R1 count→children/substrate | count_value > 1 AND has_children = false AND grouping_status NOT IN ('OK','LEAF_EMPTY_BY_DESIGN') AND drill_action='SHOW_SUBSTRATE' AND substrate_represents_count = false |
FAIL_MISLEADING_SUBSTRATE |
| R2 leaf→substrate or reason | has_children = false AND drill_action='SHOW_SUBSTRATE' AND substrate_available = false AND empty_reason IS NULL |
FAIL_DEAD_END |
| R3 grouped→children cover count | drill_action='DRILL_GROUPS' AND (child_total IS NULL OR group_count = 0) |
FAIL_EMPTY_GROUPING |
| EB1 needs grouping | count_value > 1 AND has_children=false AND grouping_status='NEEDS_GROUPING' |
EXPECTED_NEEDS_GROUPING |
| EB2 empty by design | grouping_status IN ('LEAF_EMPTY_BY_DESIGN','EMPTY') AND empty_reason IS NOT NULL |
EXPECTED_EMPTY |
| EB3 authority blocked | owner_blocker IS NOT NULL AND drill_action IN ('SHOW_SUBSTRATE','DISPLAY') AND count_value reflects reality (not faked-green) |
EXPECTED_AUTHORITY_BLOCKED |
| W1 count/substrate mismatch | drill_action='SHOW_SUBSTRATE' AND count_semantics='ADDITIVE' AND substrate_row_count <> count_value |
WARN_COUNT_SUBSTRATE_MISMATCH |
| W2 child-sum mismatch | has_children AND count_semantics='ADDITIVE' AND sum(child counts) <> count_value |
WARN_CHILD_SUM_MISMATCH |
| W3 non-additive | count_semantics IN ('NON_ADDITIVE','PARTIAL','MIXED') |
WARN_NON_ADDITIVE (informational, never FAIL) |
| PASS | none of the above | PASS |
substrate_represents_count — the key new computation
For a SHOW_SUBSTRATE leaf, T1 must compute whether the substrate the node routes to actually contains count_value rows. Two implementations (either acceptable):
- (a) cardinality probe — resolve
final_substrate_refand compare its row count under the node's filter tocount_value(withincount_semanticstolerance); - (b) declared contract —
final_substrate_refcarries a declared cardinality in the substrate contract (v_rp_node_final_substrate_contract); compare declared vscount_value. The 10 PXT NEEDS_GROUPING nodes fail this: theirfinal_substrate_refis the 12-row gap ledger, not the 408/42/… objects →substrate_represents_count=false→ currently they should beEXPECTED_NEEDS_GROUPING(the honest status), and onlyFAIL_MISLEADING_SUBSTRATEif the UI claims it is the substrate.
Design note: in v1 those nodes have
drill_action=SHOW_SUBSTRATE(claims substrate) +grouping_status=NEEDS_GROUPING→ contradiction. The invariant resolves the contradiction to a FAIL in v1, and to PASS in v2/_current (where they becomeDRILL_GROUPS). This is exactly how the invariant exposes the deploy gap.
3. Status taxonomy
FAIL statuses (block "trusted" — must be 0 for system PASS):
FAIL_NULL_ROUTE, FAIL_MISLEADING_SUBSTRATE, FAIL_DEAD_END, FAIL_EMPTY_GROUPING.
Expected-blocker statuses (honest, allowed; counted separately):
EXPECTED_NEEDS_GROUPING, EXPECTED_EMPTY, EXPECTED_AUTHORITY_BLOCKED.
Warn statuses (labeled, allowed; surfaced as badges):
WARN_COUNT_SUBSTRATE_MISMATCH, WARN_CHILD_SUM_MISMATCH, WARN_NON_ADDITIVE.
PASS.
4. Full-population coverage requirement
The invariant must be exhaustive, not curated:
- It must produce one row per node in
_currentplus one row per grouped child (today: 87 top + 250 child groups[ckpt]). - Add a coverage assertion
v_rp_drill_invariant_coverage:invariant_row_count == (_current row_count + grouping_surface child rows)AND0 nodes in _current absent from the invariant. - A node missing from the invariant is itself a FAIL (
FAIL_UNCOVERED).
This is the explicit antidote to "26 curated cases": the invariant is defined over SELECT … FROM _current, so by construction it covers whatever the contract emits, forever, including future axes (doc 03).
5. UI behavior per status (the render contract — see also doc 09)
drill_integrity |
UI behavior |
|---|---|
PASS |
normal: DRILL / DRILL_GROUPS / SHOW_SUBSTRATE per drill_action. |
EXPECTED_NEEDS_GROUPING |
render flat list capped at policy threshold + a "grouping pending" banner; must not claim the substrate page is the node's objects. Offer "view raw source" as explicitly-labeled. |
EXPECTED_EMPTY |
empty banner with empty_reason; no substrate table. |
EXPECTED_AUTHORITY_BLOCKED |
status chip (e.g. BLOCKED_NO_PRESIDENT_VOTE) + owner_blocker; never green; show the real count (0 or candidate). |
WARN_* |
render normally plus a reliability badge (e.g. "count is non-additive / may not match list"). |
FAIL_* |
safe fallback: do not route to the misleading/empty page; show a red debug badge in dev, a generic "this view is being reconciled" notice in prod, and log to the guard. A FAIL must never present as a normal green page. |
6. Relationship to the guard (doc 02 §3.3)
v_rp_contract_current_guard consumes the invariant: guard_status='FAIL' if COUNT(*) FILTER (WHERE drill_integrity LIKE 'FAIL_%') > 0. So deploying a contract with any FAIL is blocked at the guard.
7. Classification
| Issue | Class | Severity |
|---|---|---|
| No full-population invariant view | ARCHITECTURE_GAP | P0 |
| 10 PXT false-affordance + 2 dead-end nodes live in v1 (deployed-intended) | HARD_BUG (design) | P0 (closes when _current→v2/reliability deployed) |
No substrate_represents_count computation |
ARCHITECTURE_GAP | P1 |
| No coverage assertion (curated proof only) | DESIGN_DRIFT | P1 |
8. Required technical spec for T1 (Area 3)
- Build
v_rp_full_population_drill_invariantover_current∪ grouping children with the R/EB/W rules (§2). - Implement
substrate_represents_count(cardinality probe or declared contract viav_rp_node_final_substrate_contract). - Build
v_rp_drill_invariant_coverage(row-count + no-uncovered assertion). - Wire
v_rp_contract_current_guardto FAIL on anyFAIL_%. - Acceptance: with
_current→ reliability superset, invariant returns 0FAIL_*(the 12 v1 violations are resolved as GROUPED/EMPTY); with_current→ v1, invariant returns 12FAIL_*(proving the invariant actually bites). The invariant must distinguish these two — that is the regression test.