Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
fc3fbe0
fix(churn): the churn-decay window hid the commits its merge-bomb rul…
joyful-ii-V-I Sep 13, 2026
ef6d0f7
feat(churn): --in=DIR scopes the recent-changes block to a directory …
joyful-ii-V-I Sep 13, 2026
6166053
fix(churn): an all-merge-bomb window printed no <recent> block, hidin…
joyful-ii-V-I Sep 13, 2026
ac99f64
test(churn): the three CI reds #212 opened with — a stale cap census,…
joyful-ii-V-I Sep 13, 2026
030df03
fix(churn): the second CI round on #212 — new open tags compose on st…
joyful-ii-V-I Sep 13, 2026
1f432a7
fix(churn): --in=DIR was accepted and ignored whenever a report verb …
joyful-ii-V-I Sep 13, 2026
516d7c4
merge: origin/main (#209, #213) into lane/recent-scope
joyful-ii-V-I Sep 13, 2026
996dbb8
fix(churn): ten findings on #212 — the stub said a page size where th…
joyful-ii-V-I Sep 13, 2026
fd8ec44
merge: origin/main (#217) into lane/recent-scope
joyful-ii-V-I Sep 13, 2026
02f5273
fix(churn): the review round left a dispatcher six complexity points …
joyful-ii-V-I Sep 13, 2026
837997f
merge: origin/main (#221) into lane/recent-scope
joyful-ii-V-I Sep 13, 2026
9fb7017
fix(gate): legendcoveragecheck (E) probed a rung edge, so the checkou…
joyful-ii-V-I Sep 13, 2026
2765559
merge: origin/main (#218) into lane/recent-scope
joyful-ii-V-I Sep 13, 2026
3c191bd
test(legend-coverage): re-measure the rung band on the merged tree, w…
joyful-ii-V-I Sep 13, 2026
ab47b81
fix(next): the pasteable attribute quoted HEAD~1, and XML turned the …
joyful-ii-V-I Sep 14, 2026
f86bad8
test(showcase): pick the --in demo's window by measurement, because t…
joyful-ii-V-I Sep 14, 2026
4c91a9e
docs(capture): re-splice the --in section, which published the entity…
joyful-ii-V-I Sep 14, 2026
b86fba9
docs(commands): regenerate from the re-spliced capture
joyful-ii-V-I Sep 14, 2026
a4084a6
docs(contributing): the gate-count trap is gated now, and saying it i…
joyful-ii-V-I Sep 14, 2026
3c91d17
fix(recent,docs): a continuation hint pasted a different corpus, and …
joyful-ii-V-I Sep 14, 2026
5140c57
Merge remote-tracking branch 'origin/main' into lane/recent-scope
joyful-ii-V-I Sep 14, 2026
6b6362a
merge: #214 lands, and two pins both lanes raised to the same number …
joyful-ii-V-I Sep 14, 2026
c481e03
fix(nextverb,main): a pasteable command quoted a tilde no shell expan…
joyful-ii-V-I Sep 14, 2026
56bcc73
merge: #215 lands, and the same pin is raised to the same number by a…
joyful-ii-V-I Sep 14, 2026
350c426
fix(churn): the new --no-redact refusal fired on every run, --in or not
joyful-ii-V-I Sep 14, 2026
eeb0dae
test(showcase): the --in case's window is derived from what the block…
joyful-ii-V-I Sep 14, 2026
bcc2ce5
docs(capture): #215's newer capture left --in uncovered, so it is re-…
joyful-ii-V-I Sep 14, 2026
4ddf9f8
docs(changelog): the two review findings, and the arm count they moved
joyful-ii-V-I Sep 14, 2026
fa22320
fix(churn,serialize): the inert-vs-competing split is derived now, an…
joyful-ii-V-I Sep 14, 2026
28c5951
docs(capture): re-splice the --in section, whose legend published the…
joyful-ii-V-I Sep 14, 2026
0679bd8
fix(churn): a scoped run claimed a ranking it never ran, and would_sh…
joyful-ii-V-I Sep 14, 2026
eea7696
docs(capture): re-splice the --in section for the settled would_show …
joyful-ii-V-I Sep 14, 2026
c343309
fix(gate): arm 13e's verdict landed on ONE line, which is the spellin…
joyful-ii-V-I Sep 14, 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
19 changes: 15 additions & 4 deletions .ripwire_quality_acks

Large diffs are not rendered by default.

220 changes: 220 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

25 changes: 20 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -520,11 +520,26 @@ Release CI job covered it.
**The gate count is a build product.** It is stated in `README.md`, `docs/EVALS.md` and
`present/deck5_ripwire_build.js` — eight sites — and every one of them is written by
`docs/gatecount_build.py` from the single absorb loop in `test/regression.sh`, then gated by
`test/gatecountcheck.sh`. Hand-writing it is not a style preference: two lanes that each add one gate
both write N+1, git auto-merges the **identical** text clean, and the tree publishes N+1 against a loop
of N+2 with every existing check green (each branch's count matches its own loop, and the merged loop
matches main's — the member *sets* differ at the same number). That collided seven times in one night
on 2026-09-10. The merge recipe is therefore: **union the `for _g in …` sets, run the generator, done.**
`test/gatecountcheck.sh`. Hand-writing it is not a style preference. Two lanes that each add one gate
both write N+1, and git auto-merges that **identical** text clean in all three files — only the
`for _g in …` loop conflicts, so the loop is the only place anyone is forced to look. That collided
seven times in one night on 2026-09-10. The merge recipe is therefore: **union the `for _g in …` sets,
run the generator (no `--check`, so it WRITES), then `--check` it. Never hand-write the number and
never trust the clean auto-merge of the three published files.**

WHAT CATCHES A BOTCHED RESOLUTION, measured on this tree 2026-09-13 rather than assumed, because the two
ways to botch it are caught by *different* gates and neither is caught by both:

| botched how | the tree then has | red on |
| --- | --- | --- |
| loop unioned, generator not re-run | loop N+2, the eight sites N+1 | `gatecountcheck` (B), `manifestcheck`, `readmedriftcheck` (F2) — `deckclaimcheck` passes, but the deck's three sites are three of the eight `gatecountcheck` owns |
| one side of the loop taken instead of the union | loop N+1 and self-consistent, but a gate FILE present that the loop never names | `manifestcheck` only (`gatecountcheck` passes: the count really is consistent, and the generator has nothing to say) |

So the count is fail-closed **provided the full suite runs** — which is why the suite before every push
is not negotiable. An earlier revision of this paragraph claimed the first row went green on every
existing check; that was true when it was written and is not true now, and a stale claim that a defect
is ungated costs more than the defect, because it sends people to build process around something three
gates already cover.

**An advertised count is an enumeration, not a sentence.** Every number this project prints about
itself — flags, gates, skills, folded repositories, orchestrator prompts — is derived from something
Expand Down
38 changes: 20 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -812,9 +812,9 @@ Full retrieval tables — including the MRR figures behind the router numbers ab
## What it answers

<details>
<summary><b>179 long flags</b> across seven families, plus the MCP server — and <code>--help-task</code> names the ONE command a task wants, or abstains honestly when the evidence is too thin</summary>
<summary><b>180 long flags</b> across seven families, plus the MCP server — and <code>--help-task</code> names the ONE command a task wants, or abstains honestly when the evidence is too thin</summary>

Around the core sit 179 long flags advertised in `--help`, across seven families — plus an MCP
Around the core sit 180 long flags advertised in `--help`, across seven families — plus an MCP
Comment thread
coderabbitai[bot] marked this conversation as resolved.
server, so a coding agent can call any of them mid-task instead of grepping and reading whole files.
`--help` prints one line per flag (~4.5K tokens); `--help=--FLAG` prints that flag's full entry with
every caveat, `--help=SECTION` one family, and `--help=all` the whole catalog.
Expand Down Expand Up @@ -1204,7 +1204,7 @@ trailing `…`.
</details>

<details>
<summary><code>--callers</code> — a call graph built on the spot, and why <code>count="6"</code> ships labelled a floor</summary>
<summary><code>--callers</code> — a call graph built on the spot, and why <code>count="7"</code> ships labelled a floor</summary>

**Ten seconds, no index server, no embeddings, no API key** — a parse and a call graph, built on the
spot. The rows below are a real capture: the callers and their files are gate-held current
Expand All @@ -1213,21 +1213,22 @@ grow — nothing can keep a line number true in a document, so it is not claimed

```
$ ripwire . --callers=rankGraphTeleport
<callers of="rankGraphTeleport" defs="1" count="6" root="." hop_tested="0" hop_untested="6" counts_floor="1">
<s t="fn" n="runEval" p="src/eval.h:169"/>
<s t="fn" n="rankGraph" p="src/graph.h:3115"/>
<s t="fn" n="anchoredLexicalRank" p="src/graph.h:3664"/>
<s t="fn" n="churnRankedGraph" p="src/main.cpp:995"/>
<s t="fn" n="runDefaultMap" p="src/main.cpp:1120"/>
<s t="fn" n="getIndex" p="src/mcpindex.h:1104"/>
<callers of="rankGraphTeleport" defs="1" count="7" root="." hop_tested="0" hop_untested="7" counts_floor="1">
<s t="fn" n="runEval" p="src/eval.h:171"/>
<s t="fn" n="rankGraph" p="src/graph.h:3445"/>
<s t="fn" n="anchoredLexicalRank" p="src/graph.h:3995"/>
<s t="fn" n="churnDecayRanking" p="src/main.cpp:1157"/>
<s t="fn" n="churnRankedGraph" p="src/main.cpp:1191"/>
<s t="fn" n="runDefaultMap" p="src/main.cpp:1295"/>
<s t="fn" n="getIndex" p="src/mcpindex.h:1108"/>
</callers>
```

`counts_floor="1"` is the point. Call edges are extracted from source text by name, so dynamic
dispatch contributes no edge (a call through a function pointer or callback is an edge only when
ONE function is bound to that variable in scope and the variable never escapes — its address taken
or reference-bound — and a macro-generated call site — tagged
`role="macro"` — only when its function-like `#define` is indexed): `count="6"` is a **floor**,
`role="macro"` — only when its function-like `#define` is indexed): `count="7"` is a **floor**,
and the element says so before you read a single row.

</details>
Expand Down Expand Up @@ -1804,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>615 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>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 -->

`test/regression.sh` names **615 gate scripts** and is the authoritative list; <!-- gatecount -->
`test/regression.sh` names **616 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 @@ -2355,7 +2356,7 @@ identity of the index, and says which one is at fault.

### 5. Command families

The `--help` output groups 179 long flags advertised in `--help` into seven families. The `--help=`
The `--help` output groups 180 long flags advertised in `--help` into seven families. The `--help=`
column below is the argument that prints one family: `ripwire --help=navigate`. `ripwire
--help=--FLAG` prints one flag's full text — the caveats, the units, what it refuses and why.
`ripwire --help=all` is the whole catalog, about 46,000 tokens.
Expand Down Expand Up @@ -2398,12 +2399,13 @@ ripwire . --callers=rankGraphTeleport
The leading legend comment is elided here; the line numbers are a capture and move as files grow:

```xml
<callers of="rankGraphTeleport" defs="1" count="6" root="." hop_tested="0" hop_untested="6" graph_ambiguous="7801" graph_unresolved="4860" graph_unindexed="218" counts_floor="1" next="--uses=rankGraphTeleport">
<callers of="rankGraphTeleport" defs="1" count="7" root="." hop_tested="0" hop_untested="7" graph_ambiguous="7827" graph_unresolved="4865" graph_unindexed="218" counts_floor="1" next="--uses=rankGraphTeleport">
<s t="fn" n="runEval" p="src/eval.h:171"/>
<s t="fn" n="rankGraph" p="src/graph.h:3445"/>
<s t="fn" n="anchoredLexicalRank" p="src/graph.h:3995"/>
<s t="fn" n="churnRankedGraph" p="src/main.cpp:1031"/>
<s t="fn" n="runDefaultMap" p="src/main.cpp:1156"/>
<s t="fn" n="churnDecayRanking" p="src/main.cpp:1157"/>
<s t="fn" n="churnRankedGraph" p="src/main.cpp:1191"/>
<s t="fn" n="runDefaultMap" p="src/main.cpp:1295"/>
<s t="fn" n="getIndex" p="src/mcpindex.h:1108"/>
</callers>
```
Expand Down Expand Up @@ -2541,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 210 compile-time caps and 7 ranking parameters.
Another gate derives the cap inventory. The tool has 211 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
67 changes: 57 additions & 10 deletions bench/capsweep/capsweep.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
bench/capsweep/sweep.json into the answer to a query about a cap. assert_corpus_clean below keeps the
harness out of the frozen CORPUS; the file format keeps it out of the INDEX. Same rule, two surfaces.
"""
import argparse, hashlib, os, pathlib, re, shlex, shutil, subprocess, sys, collections
import argparse, hashlib, os, pathlib, re, shlex, shutil, subprocess, sys, textwrap, collections

HERE = pathlib.Path(__file__).resolve().parent
REPO = HERE.parent.parent
Expand Down Expand Up @@ -827,6 +827,61 @@ def cmd_sweep(a):

kRowsPerCap = 12 # rows per cap table; a cap in a document ABOUT caps, so it discloses below

def cap_partition(names, meta):
"""The 128 distinct cap names, split three ways, with the sum CHECKED rather than claimed.

CodeRabbit #212. The published summary ASSERTED that tunable + constexpr-only accounted for the
distinct names, and it did not: tunable.tsv's classification is FROZEN at the commit under
Provenance while the census is re-read from `src/` on every run, so a cap declared since the sweep
was prepared is in neither list, and a classified name `src/` has since dropped is in one of them
without being a live name at all. `112 + 12 = 124` shipped beside "128 distinct names" — four
short, in the one paragraph whose subject is that a wrong pair is wrong in both halves at once.

So the three parts are derived from the same data the table is built from and the partition refuses
to render unless it adds up: a name classified in BOTH lists is exactly the shape an asserted sum
cannot see, and it exits non-zero here instead of being published. `gone` is the fourth fact — the
frozen classification's own residue — reported, never folded into the three.
"""
tun, ce = meta['tunable'], meta['constexpr_only']
liveTun = [n for n in names if n in tun]
liveCe = [n for n in names if n in ce]
unswept = [n for n in names if n not in tun and n not in ce]
gone = sorted((set(tun) | set(ce)) - set(names))
if len(liveTun) + len(liveCe) + len(unswept) != len(names):
sys.exit('capsweep: the cap partition does not add up (%d tunable + %d constexpr-only + %d unswept '
'!= %d distinct names) — refusing to emit a summary whose arithmetic is wrong'
% (len(liveTun), len(liveCe), len(unswept), len(names)))
return liveTun, liveCe, unswept, gone

def partition_prose(caps, names, meta):
"""The paragraph that states the partition, with every number read from the partition itself.

WRAPPED, not hand-broken: every number in it is derived, so pinning the line breaks by hand would
make a rewrap a second place to get the paragraph wrong.
"""
liveTun, liveCe, unswept, gone = cap_partition(names, meta)
dup = sorted(n for n in names if sum(1 for c in caps if c[0] == n) > 1)
shown = ', '.join('`%s`' % n for n in unswept[:6]) + ('' if len(unswept) <= 6 else ', +%d more' % (len(unswept) - 6))
frozen = len(meta['tunable'])
goneSay = ('' if not gone else
' The `tunable` column above reads %d rather than %d because %s no longer declared in `src/` at all: %s.'
% (frozen, len(liveTun),
'one name the sweep classified is' if len(gone) == 1 else '%d names the sweep classified are' % len(gone),
', '.join('`%s`' % n for n in gone[:4])))
para = ('The first two columns are not the same number, and the gap is not a rounding: `src/` holds '
'**%d cap declarations** under **%d distinct names** (%s 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 %d names `src/` declares '
'today: **%d tunable**, **%d must stay `constexpr`**, and **%d declared since the sweep was prepared, '
'which no measurement has touched** (%s). %d + %d + %d = %d, and `emit` refuses to render a partition '
'that does not add up.%s Quoting "%d of %d" would be wrong in both halves at once, which is the shape '
'of error a generated table exists to prevent.'
% (len(caps), len(names), ', '.join('`%s`' % n for n in dup) or 'no name', len(names),
len(liveTun), len(liveCe), len(unswept), shown,
len(liveTun), len(liveCe), len(unswept), len(names), goneSay, frozen + 1, len(caps)))
return textwrap.fill(para, width=100)

def render(sweep, meta, caps, disc):
"""docs/TUNING.md as a PURE FUNCTION of (frozen measurements, live cap census).

Expand Down Expand Up @@ -854,15 +909,7 @@ def render(sweep, meta, caps, disc):
L.append('| --- | --- | --- | --- | --- | --- |')
L.append('| %d | %d | %d | %d | **%d** | %d |\n'
% (len(caps), len(names), len(tun), len(ce), len(sweep), len(tun) - len(sweep)))
dup = sorted(n for n in names if sum(1 for c in caps if c[0] == n) > 1)
L.append('The first two columns are not the same number, and the gap is not a rounding: `src/` holds')
L.append('**%d cap declarations** under **%d distinct names** (%s declared in more than one file). The'
% (len(caps), len(names), ', '.join('`%s`' % n for n in dup) or 'no name'))
L.append('sweep patches by NAME, so `%d + %d` accounts for the %d NAMES — not the %d declarations. Quoting'
% (len(tun), len(ce), len(names), len(caps)))
L.append('"%d of %d" would be wrong in both halves at once, which is exactly the shape of error a')
L.append('generated table exists to prevent.\n')
L[-2] = L[-2] % (len(tun) + 1, len(caps))
L.append(partition_prose(caps, names, meta) + '\n')
L.append('## Read this ratio before the tables\n')
L.append('**%d of %d tunable caps move any invocation at all. %d move nothing measurable.** That is the'
% (len(sweep), len(tun), len(tun) - len(sweep)))
Expand Down
Loading
Loading