diff --git a/.claude/skills/ci-gates/SKILL.md b/.claude/skills/ci-gates/SKILL.md index 7fa81e696..9e9e2fe6c 100644 --- a/.claude/skills/ci-gates/SKILL.md +++ b/.claude/skills/ci-gates/SKILL.md @@ -11595,3 +11595,91 @@ always -- the number was implausible for the size of the tree. **The finding survives; the tool does not.** One of the fourteen was the 30-line window, and it was found by reading a short list rather than by shipping a check that would cry wolf 50 times. + +## 453. A cap that decides nothing about the number, and everything about termination + +The sweep in §452 produced fourteen candidates and about five that looked like +decisions. Read by hand, priced one at a time: + +**`quant.rs` `depth > 8`** -- the recursion cap in `size_of`, which feeds the +walkable-clause census. Instrumented over the live corpus: + + calls to size_of 2078 + maximum depth reached 1 + guard taken 0 + +and the census reads `119 / 308 / 472` with the cap at **1, 2, 4, 6, 8, 12, 16, +32 or 64**. Flat everywhere. So by the §450 test it is not load-bearing, and by +the §426 rule -- *a guard clause you have not executed is a comment* -- it looks +deletable. + +**It is not.** Remove it and the suite does not fail; it **stack-overflows**, on +a struct whose field is itself. #2949 established one in real code +(`BTreeNode** children` inside `BTreeNode`), and this corpus simply has none +today. + +So the treatment is neither deletion nor silence: **make the unreached branch +reachable.** `a_self_referential_struct_terminates` plants the cycle and asserts +`Unbounded`; `a_chain_shallower_than_the_cap_is_still_measured` plants a +four-deep finite chain and asserts a real size, or the cap would be +indistinguishable from *give up on anything nested*. Both mutations bite. + +**The general shape.** Two questions look alike and are not: + +* *does this constant change a published number?* -- sweep it and read the table; +* *does this constant prevent a failure?* -- remove it and see what happens. + +A constant can answer **no** to the first and **yes** to the second, and only +the second question distinguishes a dead guard from an unexercised one. + +## 454. Two literals that must agree, linked only by prose + +`tri gates red` asks GitHub for `per_page=30` runs and prints a streak that +fills the page as `30+`, because a full page is a lower bound. The marker was +`n >= 30` -- a *second* literal, two hundred lines from the first, with a +comment explaining why they match. + +Raising the query alone would have kept printing `+` on streaks that are exact: +**a truncation marker that has stopped marking truncation**, in the command +whose whole subject is silent truncation. + +One constant now, and the test does not assert the constant against itself -- +which is where the first version went wrong: + +```rust +assert!(PAGE >= PAGE); // a control that cannot fail +assert!(!(PAGE - 1 >= PAGE)); // and its twin +``` + +The real check reads the page size back **out of the URL the command sends** +and compares it to the count at which the marker flips. Hard-coding `100` into +the URL turns it red; moving the marker to `n > PAGE` turns it red. Two +literals cannot pass it; one constant does. + +**Where two numbers must be equal, a comment is not the mechanism.** Give them +one definition, and let the test read one of them from the artefact the other +produces. + +## 455. A mutation run over a file that was never mutated + +Three mutants "passed" in a row, all green, all meaningless: the rewrite that +was supposed to change the source had aborted on a failed anchor assertion +**before writing the file**, so every mutant ran against the original. + +This is §440 wearing different clothes -- there the sample was empty, here the +*treatment* was. Both print a clean table. + +The guard is the same one line, moved: the mutation helper now **exits +non-zero and says so** when the anchor is not found exactly once, instead of +letting the caller read a green. + +```python +if s.count(old) != 1: + print(f"ANCHOR NOT FOUND ({s.count(old)}) -- mutation NOT applied"); sys.exit(9) +``` + +**A harness that cannot tell "the mutant survived" from "the mutant was never +built" is not a harness.** The repository already learned this for mutants the +compiler rejected (§382, three arms: killed, survived, never built); a mutant +the *editor* never wrote is the fourth arm, and it looks exactly like the +second. diff --git a/cli/tri/src/quant.rs b/cli/tri/src/quant.rs index 70002ea35..53616f0fa 100644 --- a/cli/tri/src/quant.rs +++ b/cli/tri/src/quant.rs @@ -132,9 +132,22 @@ fn scan_structs(specs: &[(PathBuf, String)]) -> Structs { Structs { fields, conflicted } } +/// How deep `size_of` will follow a type before calling it unbounded. +const MAX_TYPE_DEPTH: usize = 8; + fn size_of(ty: &str, s: &Structs, depth: usize) -> Size { let ty = ty.trim().trim_end_matches(',').trim(); - if depth > 8 { + // A recursion cap, and it has never fired here. Instrumented over the live + // corpus: 2078 calls to this function, maximum depth reached **1**, guard + // taken **0** times -- and the census is identical with the cap at 1, 2, 4, + // 6, 8, 12, 16, 32 or 64. So it decides nothing about any published number. + // + // It is kept because what it guards is real and simply absent from this + // corpus: a struct that contains itself. #2949 established one in C + // (`BTreeNode** children` inside `BTreeNode`), and a walk of that without a + // cap does not terminate. A guard clause nobody has executed is a comment, + // so `a_self_referential_struct_terminates` executes this one. + if depth > MAX_TYPE_DEPTH { return Size::Unbounded; } if let Some(n) = primitive(ty) { @@ -1623,6 +1636,42 @@ mod tests { } } + /// The recursion cap, executed on purpose. + /// + /// Instrumented over the live corpus: **2078** calls to `size_of`, maximum + /// depth reached **1**, cap taken **0** times -- and the census is identical + /// with it at 1, 2, 4, 6, 8, 12, 16, 32 or 64. Nothing in this repository + /// exercises it, and a guard clause nobody has run is a comment. This runs + /// it: a struct whose field is itself, which #2949 established exists in + /// real code (`BTreeNode** children` inside `BTreeNode`) and which without + /// the cap does not terminate. + #[test] + fn a_self_referential_struct_terminates() { + let mut fields = std::collections::BTreeMap::new(); + fields.insert("Node".to_string(), vec!["Node".to_string()]); + let st = Structs { + fields, + conflicted: Default::default(), + }; + assert_eq!(size_of("Node", &st, 0), Size::Unbounded); + } + + /// And a chain that is deep but FINITE must still be measured, or the cap + /// would be indistinguishable from "give up on anything nested". + #[test] + fn a_chain_shallower_than_the_cap_is_still_measured() { + let mut fields = std::collections::BTreeMap::new(); + fields.insert("L0".to_string(), vec!["Trit".to_string()]); + for i in 1..=3 { + fields.insert(format!("L{i}"), vec![format!("L{}", i - 1)]); + } + let st = Structs { + fields, + conflicted: Default::default(), + }; + assert_eq!(size_of("L3", &st, 0), Size::Finite(3)); + } + #[test] fn a_trit_is_three_and_a_struct_is_the_product() { assert_eq!(size_of("Trit", &s(), 0), Size::Finite(3)); diff --git a/cli/tri/src/red.rs b/cli/tri/src/red.rs index 74fdf4fb6..203fdcedf 100644 --- a/cli/tri/src/red.rs +++ b/cli/tri/src/red.rs @@ -81,10 +81,29 @@ struct Red { /// How many of the most recent runs, newest first, share the failing verdict. /// A single red is noise; nine in a row is an outage nobody is reading. +/// How many runs one page of the API returns. +/// +/// A constant because TWO places need the same number and only prose linked +/// them: the query asks for a page this size, and a streak that FILLS the page +/// is a lower bound, printed as `30+`. Raising the query alone would have kept +/// printing `+` on streaks that are exact -- a truncation marker that has +/// stopped marking truncation, which is this command's own subject. +const PAGE: usize = 30; + +/// The query, built from `PAGE` so the URL and the marker cannot disagree. +fn runs_url(repo: &str, id: &str, branch: &str) -> String { + format!("repos/{repo}/actions/workflows/{id}/runs?branch={branch}&per_page={PAGE}") +} + +/// Is a streak of `n` a LOWER BOUND rather than an exact count? +fn is_lower_bound(n: usize) -> bool { + n >= PAGE +} + fn streak(repo: &str, id: &str, branch: &str) -> Result<(usize, String)> { let raw = gh(&[ "api", - &format!("repos/{repo}/actions/workflows/{id}/runs?branch={branch}&per_page=30"), + &runs_url(repo, id, branch), "--jq", r#".workflow_runs[]|"\(.conclusion)\t\(.created_at)""#, ])?; @@ -146,7 +165,7 @@ fn now(repos: &[String], include_cancelled: bool) -> Result<()> { name: name.to_string(), since: since.chars().take(16).collect(), consecutive: n, - at_least: n >= 30, + at_least: is_lower_bound(n), }); } } @@ -200,3 +219,35 @@ mod tests { ); } } + +#[cfg(test)] +mod page_tests { + use super::*; + + /// The query and the truncation marker must be the same number. + /// + /// They were two literals, both `30`, linked only by a comment. A raise of + /// the query alone would have kept printing `+` on streaks that are exact + /// -- a truncation marker that has stopped marking truncation, which is + /// what this command exists to surface. + #[test] + fn the_query_and_the_marker_read_one_constant() { + // Read the page size back OUT of the URL the command sends and check + // it against the count at which the marker flips. Two literals cannot + // pass this; one constant does. + let url = runs_url("o/r", "wf.yml", "master"); + let sent: usize = url + .rsplit("per_page=") + .next() + .and_then(|v| v.parse().ok()) + .expect("the query states a page size"); + assert!( + !is_lower_bound(sent - 1), + "one short of a full page is an exact count" + ); + assert!( + is_lower_bound(sent), + "a full page is a lower bound and must print as `{sent}+`" + ); + } +} diff --git a/docs/now/2026-09-03-four-thresholds-priced-one-guards-a-hazard-three-were-not-th.md b/docs/now/2026-09-03-four-thresholds-priced-one-guards-a-hazard-three-were-not-th.md new file mode 100644 index 000000000..d0f80972b --- /dev/null +++ b/docs/now/2026-09-03-four-thresholds-priced-one-guards-a-hazard-three-were-not-th.md @@ -0,0 +1,7 @@ +# NOW -- Four thresholds priced: one guards a hazard, three were not the class (2026-09-03) + +## Four thresholds priced: one guards a hazard, three were not the class (Refs #2994) + +- quant.rs depth>8: instrumented over the live corpus -- 2078 calls, maximum depth reached 1, guard taken 0 times, and the census is identical at 1/2/4/6/8/12/16/32/64. It decides nothing about any published number. +- It is kept because removing it STACK-OVERFLOWS on a struct whose field is itself, which #2949 established exists. A guard nobody had run is now run by a test, plus a finite-chain counter-example so the cap is not 'give up on anything nested'. +- red.rs at_least: n>=30 and per_page=30 were two literals linked only by prose. One constant now, with a test that reads the page size back OUT of the URL and checks the count at which the marker flips.