VibeVM
Contents
On this page
en
Publisher
org.vibevm.ai-native
Version
1.0.0latest
Audiences
Reading time
8 min
Rendered
Read aloud
never

CARD: rule-declared-test-matrices — Test matrices are declared, never swept

01Discipline v0.2 · BETA · T2 · Rust (lineage home of the shared rule; the checker serves Go and TypeScript too)

Band 1 — Identity & Recognition

02Classification: layer=E (verification) + H (weak-reader); mechanism=rule.

03Intent: a test matrix is DECLARED as data — a table of cases iterated once — never GENERATED by a full 2^n enumeration, because a swept matrix hides a case count that grows exponentially with the axes, so a regression slips into the exponent where a reader cannot count it by eye.

04Also Known As: declared-test-matrices; declared-test-matrices; no-combinatorial-sweep; table-driven-not-bitmask; R-060.

05Applicability / Recognition: a test file (a #[cfg(test)]/#[test] site for rust-syn, a _test.go file for go-extract, a *.test.ts/*.spec.ts/__tests__ file for ts-tsc) carries EITHER (a) a loop bound that sweeps a power of two — for mask in 0..(1 << n) (Rust), for mask := 0; mask < 1<<n / math.Pow(2, n) (Go), for (let m = 0; m < 1 << n / 2 ** n / Math.pow(2, n) (TS) — OR (b) a Cartesian product of three-or-more nested GENERATED-axis loops — a Rust range for i in 0..n, a Go C-style for i := 0; i < n; i++, a TS for (let i = 0; i < n; i++). Detector seed: in test context only, walk the loop nodes; a 1 << shift with a literal 1 left operand (or a 2 **/pow(2, form) in a loop bound is a bit-mask sweep, and a nest of three-or-more GENERATED-axis loops is a Cartesian sweep. The line is WHAT THE LOOP ITERATES — a generated numeric range sweeps; a loop over a DECLARED collection (a Rust for a in STAGES / [a, b], a Go for _, tc := range tests, a TS for (const a of arr)) is a declared axis and does NOT count, so exhausting a closed set by nesting collection loops is the compliant table shape and emits nothing.

Band 2 — Justification & Tradeoffs

06Motivation: a test written for mask in 0..(1 << n) { check(mask) } enumerates 2^n cases the reader must derive mentally to know what is covered; add a fifth axis and the suite quietly runs 32× more (or, if the bound was wrong, fewer) with no name on the case that regressed. Declaring the cases as a table — for case in CASES { check(case) } — puts a name and a count the reader sees at a glance, and a regression is a row that changed. The same logic bans a 3+-deep Cartesian nest of GENERATED-axis loops (for i in 0..n / for i := 0; i < n / for (let i = 0; ...)): its case count is a product no one multiplies out. It deliberately does NOT ban a 3+-deep nest of COLLECTION loops (for a in STAGES, for _, tc := range tests, for (const a of arr)) — those iterate a closed set someone wrote down, so the cases are recorded data and the product is merely expressed by the nesting, which is the good test the bare-depth heuristic used to red.

07Structure & Participants: the conform fact Fact::TestSweep { kind, line, detail } (emitted by all three frontends, only in test context) → the declared-test-matrices rule turns each into one finding. kind is "bitmask" (a 2^n loop bound; detail is the bound text) or "nested-loops" (a ≥3-deep nest of generated-axis loops; detail is the depth). Fingerprints key on (file, kind, ordinal), never line — a line-keyed baseline rots on any edit above the loop (the stop.rs lesson), and a baseline that rots on unrelated edits is a checker that lies.

08Collaborations: rides the same conform gate, SARIF output, and ratchet baseline as the other rules; one engine rule serves Rust, Go, AND TypeScript — all three drivers mount DeclaredTestMatrices, since every frontend emits the same Fact::TestSweep in test context; the detection is per-language syntax (the extractor owns it), the judgement is one rule; every finding renders through the one req_message renderer (Class-F grammar, R3-011).

09Goals / Non-Goals: Goals: red a swept test matrix — a 2^n bit-mask loop or a ≥3-deep generated-axis (range / C-style-for) nest — in test context, so a reader can count the cases by eye. Non-Goals: NOT a ban on loops in tests (a single declared loop over a case table, OR any nest of collection/constant loops exhausting a closed set, is the compliant shape); NOT a coverage or property-test checker (property tests deliberately generate many inputs — they are a different, declared-by-framework strategy, not a hand-rolled 1 << n); NOT for production code (the extractor emits only in test context); NOT a measure of test VALUE, only of whether the matrix is declared or swept.

10Consequences: (+) a swept matrix becomes a named, reviewable finding rather than an invisible exponent; (+) the table-driven remedy is the same idiom every guide already recommends, so the fix is mechanical; (+) one rule reads three languages through one fact. (−) a genuinely combinatorial test (a truth-table over many booleans) that SHOULD enumerate all 2^n will fire — the remedy there is a named, documented enumeration (a comment plus the rule honoured by a frozen baseline), not a weakening; (−) the rule is a vacuum on a tidy tree (see Evidence), so its value is prospective.

11Alternatives: no check at all — the pre-rule state: a swept matrix is invisible until a regression escapes it; ban ALL nested loops in tests (too blunt — a 2-deep nest is readable and common); a coverage-gate that demands N cases (wrong grain — it counts cases, not whether they are declared). The declared-vs-swept distinction is the load-bearing one; the others lose it.

12Risks & Assumptions: assumes a bit-mask loop bound (1 << n) is generating test cases, not computing a real bitmask for the system under test (in test context this is near-always true — a test computing a bitmask would assert it, not iterate it). Scope (intentionally OUT of the rule): exhausting a CLOSED set by nesting collection/constant loops — for a in Stage::ALL { for b in State::ALL { for at in Stage::ALL { … } } }, for mode in [Snap, Hard] { for b in [false, true] { for c in [false, true] { … } } } — is NOT a sweep: every axis is a declared enumeration whose cases are written as data, so the count is visible and a regression is a row that changed. The predicate keys on WHAT THE LOOP ITERATES (a generated numeric range sweeps; a declared collection/array/constant does not), not on nesting depth alone — a bare-depth heuristic reds these good tests (it did, on this tree's own progress-core and vibe-workspace), so the narrowed predicate is what makes the rule fit a real codebase. Heuristic limits (what it does NOT see): (1) a swept matrix whose bound is a computed value, not a literal 1 <</2 **/pow(2, — e.g. let n = 1 << k; for i in 0..n — the bound is an identifier, not a shift, so it is invisible (a documented limit, not a silent claim); (2) a Cartesian product built with itertools::product / a flat generated Vec, not nested loops — invisible, since the nest is collapsed into one iterator; (3) a 2-deep nest is below the ≥3 threshold and compliant by design; (4) a generated axis the syntax cannot reliably name — a Rust while/loop or TS while/do (indistinguishable from a data-driven while let Some(x) = iter.next()), or a Go 1.22 for i := range n integer-range (indistinguishable from range slice) — does NOT count, erring toward silence (the safe direction for a narrowing). Sunset: if a project's test framework makes a swept matrix un-writable (every matrix flows through a declared-table helper that cannot iterate a bit-mask), or if coverage tooling subsumes the declared-vs-swept distinction, this card retires with its checker (R-050).

13Evidence & Transfer-strength: checker shipped (declared-test-matrices, doctested in core-ai-native-conform/src/rules/matrices.rs, mounted in rust-ai-native-conform, go-ai-native-conform, and typescript-ai-native-conform); R-060. Honest vacuum: a host-wide census found zero generated-axis sweeps in the tree — no 1 << n loop bound, no 2 ** n, no math.Pow(2, …), and no 3+-deep nest of range/C-style-for loops. The tree's 3-deep test nests (progress-core's Stage::ALL × State::ALL × Stage::ALL, vibe-workspace's [Snap,Hard] × [bool] × [bool]) are all DECLARED-axis exhaustions — compliant by the narrowed predicate, and the case that motivated the narrowing (the bare-depth heuristic red'd them). So the rule is demonstrated on fixtures (a dirty 1 << 3 bit-mask AND a 3-deep range/C-style-for nest per stack red; a declared table+loop AND a 3-deep collection-loop nest green), NOT on host code. This is a fact about the tree's tidiness, not an argument against a rule the discipline forbids weakening for disuse: the value is prospective — the next swept matrix someone writes is caught at the gate, not in a regression. Portability: Rust + Go + TypeScript (all three mount the rule and emit Fact::TestSweep). Tag: [E-mid] — the detection is unit-tested across three syntaxes and the red/green fixtures are live, but no host regression has been prevented yet (parallel to rule-position-is-a-resource).

Band 3 — Operation

14trigger: WHEN a test-context loop bound sweeps 2^n (a `1 << n` / `2 ** n` / `Math.pow(2, n)` bitmask) OR a test nests GENERATED-axis loops 3+ deep (a Rust range `for i in 0..n`, a Go/TS C-style `for` — NOT a collection/array/constant for, which is a declared axis), THEN apply
mode: gate            # a conform rule: runs over the scanned file set at conform time (per-merge), not per-edit (inline) or on a schedule (raid) — the finding arrives through the normal gate
routine:
  1. Read the finding: it names the kind (`bitmask` or `nested-loops`) and the detail (the bound text or the depth).
  2. Replace the swept enumeration with a DECLARED table of cases: name each case, iterate the table once.
  3. If a case is genuinely combinatorial (a truth-table that must enumerate all 2^n), keep it — but name it and document why, and let the ratchet freeze that one finding as a recorded, deliberate enumeration.
  4. Re-run `rust-ai-native conform check --scope <crate>` (or the go/ts twin); the finding clears and the baseline only shrinks.
  5. A 2-deep nest, a single loop over a collection, OR any depth of collection/constant-loop nesting (a closed set exhausted by nesting) is compliant — do not split it; the rule fires only at a 2^n bound or at ≥3-deep GENERATED-axis (range / C-style-for) nesting.
checker: declared-test-matrices (core-ai-native-conform, T-syn over TestSweep facts; mounted in rust-ai-native-conform, go-ai-native-conform, typescript-ai-native-conform)
raid_role: layer=conform; order=after:cell-closure; batch=crate
budget: active_rules=1; first_signal=conform scan (content-addressed, cached; <1s/file warm)

For an agent

This page has a machine mirror. The citation carries the version rather than latest, so what an agent quotes does not move under it.

spec://org.vibevm.ai-native/rust-ai-native-lang@1.0.0/cards/rule-declared-test-matrices

.md.xmlllms.txt