Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
9803539
perf(testmap,situ): a runner command pasted the whole checkout prefix…
joyful-ii-V-I Sep 13, 2026
0617c09
perf(situ): the one report with no attributes said every disclosure t…
joyful-ii-V-I Sep 13, 2026
fe0a13a
feat(situ): the files a change drags with it were the ones no walk co…
joyful-ii-V-I Sep 13, 2026
1635a16
Merge remote-tracking branch 'origin/lane/tests-rows' into lane/situ-…
joyful-ii-V-I Sep 13, 2026
52e07cc
chore(gatecount): the merged gate count is 614 — the generator's own …
joyful-ii-V-I Sep 13, 2026
f06b0c1
refactor(situ): the sibling rule re-rolled a directory helper the rep…
joyful-ii-V-I Sep 13, 2026
985e2be
chore(quality): ack the nine contract-change rows A3's root parameter is
joyful-ii-V-I Sep 13, 2026
34467ec
fix(gates): three --situ readers treated the new attribute line as a …
joyful-ii-V-I Sep 13, 2026
e992877
Merge remote-tracking branch 'origin/lane/tests-rows' into lane/situ-…
joyful-ii-V-I Sep 13, 2026
26930ae
fix(situ,testmap,sarif,mcpedit,flip): a relative command with no anch…
joyful-ii-V-I Sep 13, 2026
7278b94
Merge remote-tracking branch 'origin/main' into lane/situ-shape
joyful-ii-V-I Sep 13, 2026
d28fb9d
chore(parity): re-pin help_all for the MERGED help text — neither sid…
joyful-ii-V-I Sep 13, 2026
79b3047
chore(quality): ack the three rows the conditional root sentence is
joyful-ii-V-I Sep 13, 2026
b736c4e
docs(limits,tuning): the 211th cap was in src/ and in no generated do…
joyful-ii-V-I Sep 13, 2026
d82248c
Merge remote-tracking branch 'origin/main' into lane/situ-shape
joyful-ii-V-I Sep 13, 2026
f7515aa
Merge remote-tracking branch 'origin/main' into lane/situ-shape
joyful-ii-V-I Sep 13, 2026
9c707f2
Merge remote-tracking branch 'origin/lane/tests-rows' into lane/situ-…
joyful-ii-V-I Sep 13, 2026
1e4a0b4
Merge remote-tracking branch 'origin/lane/tests-rows' into lane/situ-…
joyful-ii-V-I Sep 14, 2026
f172a67
fix(sarif): the filesystem root is its own separator, so it stripped …
joyful-ii-V-I Sep 14, 2026
6cc9097
perf(situ): the sibling cap bounded the answer and never the work
joyful-ii-V-I Sep 14, 2026
34c4482
Merge remote-tracking branch 'origin/main' into lane/situ-shape
joyful-ii-V-I Sep 14, 2026
a302644
merge(main): 0b118ac1 (#214), and prLegendText takes BOTH lanes' facts
joyful-ii-V-I Sep 14, 2026
d3e1cf6
chore(quality): ack the A3 arity change #214's landing made visible
joyful-ii-V-I Sep 14, 2026
37b7c8b
merge(main): a2f90ba6 (#215) — two pins where NEITHER side was right
joyful-ii-V-I Sep 14, 2026
eebe679
fix(sarifcheck,testrowruncheck): a gate eval'd a generated file, on CI
joyful-ii-V-I Sep 14, 2026
106ddd1
fix(sarifcheck): the parser's refusal reached the caller as "no flags"
joyful-ii-V-I Sep 14, 2026
fd0e4a6
fix(situ,sarifcheck,receiptpostcheck): a heading named a root its rep…
joyful-ii-V-I Sep 14, 2026
f26832c
merge(main): c22d4f2f (#212, #216, #227) — five build products re-der…
joyful-ii-V-I Sep 14, 2026
97646f9
fix(testmap): a run= command did not quote its path, and the corpus w…
joyful-ii-V-I Sep 14, 2026
2c0fa4e
fix(runhintcheck): the security arm certified a command it never ran
joyful-ii-V-I Sep 15, 2026
92d82f1
fix(testmap): the quoted path still reached the interpreter as an option
joyful-ii-V-I Sep 15, 2026
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
14 changes: 13 additions & 1 deletion .ripwire_quality_acks

Large diffs are not rendered by default.

231 changes: 231 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

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

<details>
<summary><b>616 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>617 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 **616 gate scripts** and is the authoritative list; <!-- gatecount -->
`test/regression.sh` names **617 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 Expand Up @@ -2543,7 +2543,7 @@ python3 test/pargates.py . ./build/ripwire -j 6
A new gate script must be added to `test/regression.sh` in the same change. The gate
`test/manifestcheck.sh` enforces this rule.

Another gate derives the cap inventory. The tool has 211 compile-time caps and 7 ranking parameters.
Another gate derives the cap inventory. The tool has 212 compile-time caps and 7 ranking parameters.
`docs/LIMITS.md` lists each cap, its value, and whether the file discloses a truncation when the cap
fires, and `python3 docs/limits_build.py --check` proves that list against `src/`. `docs/TUNING.md`
lists the measured cost of each cap.
Expand Down
2 changes: 1 addition & 1 deletion docs/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1144,7 +1144,7 @@ $ ./build/ripwire . --handoff

**Answers:** before a PR: name the tests to run and the untested blast radius;

exit 4 if either is non-empty agent self-check before a PR (pair with --quality-delta): names the tests to run + the UNTESTED blast radius; exit 4 if either obligation is non-empty (run the tests, then rely on green). (default = git diff) run= on a test row --affected/--situ/--test-gate/--exercises/--pr-context/--pack-task name harness FILES, not commands. A row carries run="<cmd>" when a runner is DERIVABLE from real evidence: a test-dir .sh/.py whose basename stem matches the harness's, or whose TEXT names the harness file. Spelled with the same root you scanned, so it pastes straight into a shell. NO run= means NOT DERIVABLE -- never a guessed suite command
exit 4 if either is non-empty agent self-check before a PR (pair with --quality-delta): names the tests to run + the UNTESTED blast radius; exit 4 if either obligation is non-empty (run the tests, then rely on green). (default = git diff) run= on a test row --affected/--situ/--test-gate/--exercises/--pr-context/--pack-task name harness FILES, not commands. A row carries run="<cmd>" when a runner is DERIVABLE from real evidence: a test-dir .sh/.py whose basename stem matches the harness's, or whose TEXT names the harness file. Spelled RELATIVE to the root= the document declares, so it pastes into a shell run from there, and the document does not change with where the tree is checked out (a MULTI-ROOT run declares no single root, so it stays absolute). NO run= means NOT DERIVABLE -- never a guessed suite command

**Try it**

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` | 616 gate scripts plus the determinism, cache-transparency and golden contracts. <!-- gatecount --> |
| **The gate suite** | `test/regression.sh`, `test/pargates.py` | 617 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 @@ -5834,7 +5834,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 **616 gate scripts**, all of which exist on disk. <!-- gatecount -->
naming **617 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 @@ -6846,7 +6846,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 616. The <!-- gatecount -->
~210; `test/argvdiffcheck.sh` says 200+), while the loop in `test/regression.sh` names 617. 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
13 changes: 7 additions & 6 deletions docs/LIMITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ it once, marked `×N`.

| total caps | files | caps whose file discloses | caps whose file discloses NOTHING |
| --- | --- | --- | --- |
| 211 | 84 | 117 | **94** |
| 212 | 84 | 118 | **94** |

Plus 7 ranking and apportionment parameters, in their own table below: they are not caps, they
are not counted as caps, and 211 + 7 is the 218 constants this generator parses out of `src/`.
are not counted as caps, and 212 + 7 is the 219 constants this generator parses out of `src/`.

## INDEXING, OUTPUT or BOUNDARY — which half of the answer a cap bounds

Expand All @@ -37,8 +37,8 @@ None of them truncates anything, so none can be judged by `shown=`/`total=` and
a disclosure — labelling them OUTPUT would ask for a `capped="1"` that could never honestly fire.
The distinction was named in review on #108 and the rows below now carry it.

The `class` column below carries that answer where it is known. **114 of 211 caps are classified
(39 INDEXING, 40 OUTPUT, 35 BOUNDARY); the remaining 97 render `—`, which means NOT YET
The `class` column below carries that answer where it is known. **114 of 212 caps are classified
(39 INDEXING, 40 OUTPUT, 35 BOUNDARY); the remaining 98 render `—`, which means NOT YET
CLASSIFIED — never "neither".** Classifications live in `docs/limits_classes.tsv`, a sidecar with
a known expiry:
the tag belongs on the declaration itself, and this file exists only because the round that
Expand Down Expand Up @@ -88,7 +88,7 @@ refuse to write, so the column cannot be satisfied by pointing at nothing.

## Caps, by file

One table for each of the 84 files that declare a cap — the 211 caps counted above, and no parameter.
One table for each of the 84 files that declare a cap — the 212 caps counted above, and no parameter.

### `src/abicheck.h`

Expand Down Expand Up @@ -559,7 +559,7 @@ Discloses: **none**

### `src/mcpverbs.h`

Discloses: `blast_radius_capped`, `coboost_commits_capped`, `forgotten_capped`, `hits_capped`, `unindexed_candidates_capped`
Discloses: `blast_radius_capped`, `coboost_commits_capped`, `forgotten_capped`, `hits_capped`, `siblings_capped`, `unindexed_candidates_capped`

| constant | value | class | note |
| --- | --- | --- | --- |
Expand Down Expand Up @@ -800,6 +800,7 @@ Discloses: `tests_capped`, `untested_capped`
| `kSituBlastFilesShown` | `8` | OUTPUT | section [1] — blast-radius file rows; a raisable DEFAULT |
| `kSituPartnerFileRowsShown` | `4` | OUTPUT | section [1] — decl/def partner rows |
| `kSituPartnerRowsShown` | `8` | OUTPUT | section [3] — co-change partner rows; a raisable DEFAULT |
| `kSituSiblingRowsShown` | `8` | — | section [1] — L-D lexical sibling rows; a raisable DEFAULT |

### `src/skillscan.h`

Expand Down
24 changes: 12 additions & 12 deletions docs/TUNING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,25 @@ to production at defaults; that control is what makes these numbers mean anythin

| cap declarations | distinct names | tunable | must stay `constexpr` | move >= 1 invocation | move nothing measurable |
| --- | --- | --- | --- | --- | --- |
| 129 | 128 | 112 | 12 | **37** | 75 |
| 130 | 129 | 112 | 12 | **37** | 75 |

The first two columns are not the same number, and the gap is not a rounding: `src/` holds **129 cap
declarations** under **128 distinct names** (`kRowCap` declared in more than one file). The sweep
The first two columns are not the same number, and the gap is not a rounding: `src/` holds **130 cap
declarations** under **129 distinct names** (`kRowCap` declared in more than one file). The sweep
patches by NAME, so its own population is NAMES and not declarations — and the two columns beside
those are frozen at the commit named under Provenance while this census is re-read from `src/` on
every run, which makes them a third population again. Enumerated over the 128 names `src/` declares
today: **111 tunable**, **12 must stay `constexpr`**, and **5 declared since the sweep was prepared,
every run, which makes them a third population again. Enumerated over the 129 names `src/` declares
today: **111 tunable**, **12 must stay `constexpr`**, and **6 declared since the sweep was prepared,
which no measurement has touched** (`kChurnMergeBombMaxFiles`, `kFieldIdCapacity`,
`kForPageRowsDefault`, `kForPageUnionSymbolCap`, `kMaxBlockBytes`). 111 + 12 + 5 = 128, and `emit`
refuses to render a partition that does not add up. The `tunable` column above reads 112 rather than
111 because one name the sweep classified is no longer declared in `src/` at all:
`kSituTestRowsShown`. Quoting "113 of 129" would be wrong in both halves at once, which is the shape
of error a generated table exists to prevent.
`kForPageRowsDefault`, `kForPageUnionSymbolCap`, `kMaxBlockBytes`, `kSituSiblingRowsShown`). 111 +
12 + 6 = 129, and `emit` refuses to render a partition that does not add up. The `tunable` column
above reads 112 rather than 111 because one name the sweep classified is no longer declared in
`src/` at all: `kSituTestRowsShown`. Quoting "113 of 130" would be wrong in both halves at once,
which is the shape of error a generated table exists to prevent.

## Read this ratio before the tables

**37 of 112 tunable caps move any invocation at all. 75 move nothing measurable.** That is the
finding, and it says what NOT to do: this is not a 129-cap audit. Most of these constants are
finding, and it says what NOT to do: this is not a 130-cap audit. Most of these constants are
inert on real invocations and should be left alone. The work worth doing is the small set below,
plus the caps that fire SILENTLY — a cap that bites without disclosing is a defect independent of
whether its value is right, and that fix is both cheaper and larger than any retuning.
Expand Down Expand Up @@ -308,7 +308,7 @@ moving. Re-run `prepare|screen|sweep` to refresh them.

### `kBatchCap` = `16`

`src/mcpverbs.h` — discloses: `blast_radius_capped`, `coboost_commits_capped`, `forgotten_capped`, `hits_capped`, `unindexed_candidates_capped` — probe value `128` — **1 verb(s) respond**
`src/mcpverbs.h` — discloses: `blast_radius_capped`, `coboost_commits_capped`, `forgotten_capped`, `hits_capped`, `siblings_capped`, `unindexed_candidates_capped` — probe value `128` — **1 verb(s) respond**

| invocation | default | at probe | delta |
| --- | --- | --- | --- |
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 @@ -1068,7 +1068,7 @@ function storyCards(s, { kick, head, stories, footText }){
kicker(s, "// how it stays true", AMBER);
title(s, "Proven, not promised");
const cards = [
["616 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
["617 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 @@ -1092,7 +1092,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, "616", "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, "617", "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 @@ -1342,7 +1342,7 @@ function storyCards(s, { kick, head, stories, footText }){
["180 long flags · 33 slides", "bash test/deckclaimcheck.sh"],
["every --flag named here exists", "bash test/deckcheck.sh"],
["74.7% fewer element bytes", "bash test/showcasecapturecheck.sh"],
["616 gate scripts", "bash test/manifestcheck.sh"], // gatecount
["617 gate scripts", "bash test/manifestcheck.sh"], // gatecount
["49 repos · 70 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
6 changes: 4 additions & 2 deletions src/cli.h
Original file line number Diff line number Diff line change
Expand Up @@ -1175,8 +1175,10 @@ inline constexpr char kHelpHead[] =
" exit 4 if either obligation is non-empty (run the tests, then rely on green). (default = git diff)\n"
" run= on a test row --affected/--situ/--test-gate/--exercises/--pr-context/--pack-task name harness FILES, not commands. A row carries\n"
" run=\"<cmd>\" when a runner is DERIVABLE from real evidence: a test-dir .sh/.py whose basename stem\n"
" matches the harness's, or whose TEXT names the harness file. Spelled with the same root you scanned,\n"
" so it pastes straight into a shell. NO run= means NOT DERIVABLE -- never a guessed suite command\n"
" matches the harness's, or whose TEXT names the harness file. Spelled RELATIVE to the root= the\n"
" document declares, so it pastes into a shell run from there, and the document does not change\n"
" with where the tree is checked out (a MULTI-ROOT run declares no single root, so it stays\n"
" absolute). NO run= means NOT DERIVABLE -- never a guessed suite command\n"
" --grep=STR | --regex=PAT search for a literal or a regex; every hit comes back with its enclosing symbol\n"
" literal / regex search + enclosing symbol + the matched line. SPAN-TIERED by default (see\n"
" --grep-in below): the scan itself is exhaustive, the ANSWER serves one tier and discloses\n"
Expand Down
Loading
Loading