Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,49 @@ not published here — see `docs/EVALS.md` for the instruments behind the headli

---

## [Unreleased]


### Added — RSpec's `described_class` is the class its example group names, so a spec's calls pin to the class under test (#338)

`described_class` is how RSpec spells the class under test. Inside `RSpec.describe Calc do … end`,
`described_class.m( 1 )` is `Calc.m( 1 )`. Its receiver is a bare identifier that no binding names, though, so every
such call declined. A spec reached nothing through it, and `tested=`, `--seams` and `--test-gate` read the class under
test as unreached by the spec written for it.

The call now pins the way a written `Calc.m( 1 )` does (#267's constant-receiver arm). The rule is RSpec's own
(rspec-core 3.13, `Metadata::ExampleGroupHash#described_class`): a group's described class is its first description
argument, unless that is `nil` or a String, in which case it is the parent group's. So the innermost enclosing example
group with a constant first argument answers. An example group is `describe` / `context` (and `feature`,
`example_group` and the `x`- / `f`- spellings), called bare or on `RSpec`, with a block. A shared group
(`shared_examples`, `shared_examples_for`, `shared_context`) stops the walk with no answer, because its body runs in
whichever group includes it.

Stated floors, each pinned by `test/rubydescribedclasscheck.sh`:
- **(a)** A chained receiver (`described_class.new.m`) is untouched: #267's one-hop bound.
- **(b)** A group with no constant (`describe "text"` at the top, `describe :sym`) names no class.
- **(c)** A `describe` on any other receiver (`Docs.describe Calc do`) is not an RSpec example group.
- **(d)** `subject`, the implicit `described_class.new`, is not modelled.
- **(e)** A redefined `described_class` declines. A **method** of that name (any `:described_class` symbol, as in
`let( :described_class )`, or `def described_class`) declines every site in the file. A **local** of that name (an
assignment, `||=`, a multiple-assignment target, or a block or method parameter) declines the sites Ruby reads as
that local: after the binding in its own scope and in the blocks nested inside it, never in a sibling block, and
never across a `def`. The name is matched as a whole word, so `my_described_class = x` redefines nothing.
- **(f)** A qualified describe is named by its final segment: `RSpec.describe Cask::Tab` reads as `Tab`. Where another
`Tab` defines the method too, the call splits between them. Where only the other `Tab` defines it, the call pins
there, unmarked. A written `Cask::Tab.m` behaves the same way.

Measured with `--no-cache`, `--report` edge totals, `main` (3fcd515f) against this change:

| Corpus | Edges |
| --- | --- |
| Rails app A | 26,649 → 26,755 (+106) |
| Rails app B | 20,807 → 21,149 (+342) |
| activerecord, activesupport, actionpack 8.1.3 `lib/`; this repo's `src/` | default map byte-identical |

`kParserVer` → 125 (carried as 121 on the PR): `RawRef::recv` / `recvVar` change value for these call sites.
The record layout is unchanged, so `kCacheVersion` stays 25.

## [0.6.5] — 2026-09-27


Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1977,9 +1977,9 @@ wrong, and it has. These are the results that say so, all in-tree, all published
### In the tests

<details>
<summary><b>650 gate scripts</b>, five contracts no unit test can hold, and the house rule: write the gate before the code it measures</summary> <!-- gatecount -->
<summary><b>651 gate scripts</b>, five contracts no unit test can hold, and the house rule: write the gate before the code it measures</summary> <!-- gatecount -->

`test/regression.sh` names **650 gate scripts** and is the authoritative list; <!-- gatecount -->
`test/regression.sh` names **651 gate scripts** and is the authoritative list; <!-- gatecount -->
`python3 test/pargates.py . ./build/ripwire -j 6` runs the same set in parallel. On top of them sit the
contracts that do not fit a unit test: two runs byte-identical, warm output identical to cold, output
that pipes clean through `xmllint --noout`, a sanitizer build with `-fno-sanitize-recover=all`, and a
Expand Down
6 changes: 3 additions & 3 deletions docs/EVALS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ section, and it is not an afterthought.
| **Co-change / known-item evals** | `--eval`, `--eval-retrieval` (see `bench/ANSWERQUALITY.md`) | Whether the tool surfaces the other files a real historical commit touched; and known-item retrieval across four rankers. |
| **Ensemble calibration harness** | `bench/ensemblecal/` | Whether `--ensemble`'s four evidence families are actually orthogonal, how often each fires, how stable each is across commits — and the preset ladder derived from that (§9). |
| **Differential argv harness** | `test/argvdiffcheck.sh` | That a refactor changed *nothing observable*: two binaries, every argv vector, stdout + stderr + exit code byte-identical. |
| **The gate suite** | `test/regression.sh`, `test/pargates.py` | 650 gate scripts plus the determinism, cache-transparency and golden contracts. <!-- gatecount --> |
| **The gate suite** | `test/regression.sh`, `test/pargates.py` | 651 gate scripts plus the determinism, cache-transparency and golden contracts. <!-- gatecount --> |
| **`--quality-delta`** | `src/quality.h` | Ten measured code-quality failure modes, reported only where a change made them worse. |

### The labeling protocol (why the held-out eval is allowed to disagree with the ranker)
Expand Down Expand Up @@ -5866,7 +5866,7 @@ copy here would be exactly the dialect divergence that gate exists to catch. Com
tags, wrap, stable-order defaults), seven individually invoked standalone gates (`g1freshcheck`,
`skillscan`, `htmlexport`, `compresscheck`, `handoffcheck`, `releaseinstallcheck`,
`taskroutecheck`), and a single loop
naming **650 gate scripts**, all of which exist on disk. <!-- gatecount -->
naming **651 gate scripts**, all of which exist on disk. <!-- gatecount -->

`python3 test/pargates.py . ./build/ripwire -j 6` runs the same scripts in parallel so a full
verification fits in one sitting. It does not modify `regression.sh`.
Expand Down Expand Up @@ -6885,7 +6885,7 @@ Listed because the reason is more useful than the silence.
shipped**. See `bench/locbench/anchorhop_calib.json`. The mention anchor's reproducible numbers are
the ablations in §4.
- **A single round gate-count.** Two in-tree numbers disagree (`test/pargates.py`'s docstring says
~210; `test/argvdiffcheck.sh` says 200+), while the loop in `test/regression.sh` names 650. The <!-- gatecount -->
~210; `test/argvdiffcheck.sh` says 200+), while the loop in `test/regression.sh` names 651. The <!-- gatecount -->
loop is the authority; the stale docstrings are a known drift. Since 2026-09-10 the number is not
written by hand anywhere: `docs/gatecount_build.py` derives it from the loop and rewrites every
published site, `test/gatecountcheck.sh` fails if any of them drifts, and `test/manifestcheck.sh`
Expand Down
6 changes: 3 additions & 3 deletions present/deck5_ripwire_build.js
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,7 @@ function storyCards(s, { kick, head, stories, footText }){
kicker(s, "// how it stays true", AMBER);
title(s, "Proven, not promised");
const cards = [
["650 gate scripts", "the suite runs on every push — plus determinism, cache-transparency and golden contracts; the gate count itself is gated against the runner's own loop"], // gatecount
["651 gate scripts", "the suite runs on every push — plus determinism, cache-transparency and golden contracts; the gate count itself is gated against the runner's own loop"], // gatecount
["byte-identical, always", "two runs over the same tree produce the same bytes; warm equals cold. Enforced in CI, twice — Release AND a plain flavour, because NDEBUG once blinded a whole class of checks"],
["differential refactoring", "a refactor must prove it changed nothing observable: two binaries, hundreds of argv vectors, stdout + stderr + exit codes byte-identical"],
["held-out labels, authored blind", "eval labels were written by reading source before the ranker ever ran on them — so the eval is allowed to say the ranker is wrong. It has."],
Expand All @@ -1147,7 +1147,7 @@ function storyCards(s, { kick, head, stories, footText }){
title(s, "Claims you can trust, because we publish what failed", { size: 32 });

card(s, MX, 1.72, 3.86, 1.72);
stat(s, "650", "gate scripts named by test/regression.sh — and the COUNT itself is gated against the runner's own loop, so it cannot go stale quietly", // gatecount
stat(s, "651", "gate scripts named by test/regression.sh — and the COUNT itself is gated against the runner's own loop, so it cannot go stale quietly", // gatecount
MX+0.15, 1.86, 3.56, CYAN, { bsize: 42, bh: 0.66, lsize: 9.5 });
card(s, 4.68, 1.72, 3.86, 1.72, CARD2);
stat(s, "8", "registered NEGATIVES — changes built, gated green, measured against a band written before the code, and reverted rather than tuned",
Expand Down Expand Up @@ -1397,7 +1397,7 @@ function storyCards(s, { kick, head, stories, footText }){
["183 long flags · 34 slides", "bash test/deckclaimcheck.sh"],
["every --flag named here exists", "bash test/deckcheck.sh"],
["74.7% fewer element bytes", "bash test/showcasecapturecheck.sh"],
["650 gate scripts", "bash test/manifestcheck.sh"], // gatecount
["651 gate scripts", "bash test/manifestcheck.sh"], // gatecount
["49 repos · 71 papers · 237 surveyed","bash test/readmedriftcheck.sh"],
["the ten moments, any row", "ripwire . --callers=SYM | wc -c"],
["the head-to-head table", "bench/headtohead/r4-2026-08-06/"],
Expand Down
Loading
Loading