Skip to content

Front page: languages on one line; body folds to a scannable summary - #10

Merged
joyful-ii-V-I merged 2 commits into
mainfrom
docs/readme-languages
Aug 7, 2026
Merged

joyful-ii-V-I merged 2 commits into
mainfrom
docs/readme-languages

Conversation

@joyful-ii-V-I

Copy link
Copy Markdown
Collaborator

Two reader-facing changes, both content-preserving:

  • Languages up top. One scannable line under the hero — all sixteen grammars — linking to the full Languages section, plus the add-a-language invitation in both places: a vendored tree-sitter grammar, its queries, one row in the declarative extension table.
  • The body folds. Below the designed front page (the seam is the README's own nav row), every section now leads with 1–3 claim-carrying sentences — numbers kept in the open — and folds its prose into <details> blocks (21 new, 25 total). Scrolling the README now reads as an executive summary with every deep-dive one click away.

Nothing was deleted or reworded: every non-blank pre-edit line is still present verbatim (verified mechanically); all heading anchors still resolve, including the ones other docs link into. ### In the numbers deliberately stays unfolded — the counterexample list is link-targeted and should never hide.

Gate suite: gates=361 pass=358 skip=3 fail=0 (the three standing skips), deckcheck clean on the new prose.

🤖 Generated with Claude Code

joyful-ii-V-I and others added 2 commits August 7, 2026 13:16
…anguage invitation

The full Languages section at the bottom keeps the per-grammar detail; the front
page now answers 'does it speak my language' without scrolling, and both places
say what adding one costs: a vendored tree-sitter grammar, its queries, one row
in the declarative extension table.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…click for the prose

The front page above the nav-link rule is untouched: hero, languages line, install
one-liner, with/without table, the round-4 retrieval table and its two literary
retellings, the token/quality claims, and the two output examples. That block was
designed as a pitch and still reads as one.

Everything below it now leads with 1-3 sentences that carry the section's actual
claim — numbers kept, never hidden — and folds the expository prose into
<details> blocks with informative summaries, using the pattern the page already
used twice. A scroll from "What it answers" to the licence now reads as an
executive summary with every deep dive one click away.

Nothing was deleted or reworded: every line of the previous README is still in the
file byte-for-byte, verified line-by-line, so the gates that pin sentences and
numbers here (readmedrift, readmeexample, floormark, emittertruth, cuda, metal,
deck, skillinstall, …) see the same text. New sentences were added, not
substituted. Headings all stay outside the folds so every anchor — #languages,
#measured, #in-the-numbers, #what-it-saves-you-in-tokens — still resolves, and the
counterexample list #in-the-numbers points at stays unfolded so the link lands on
visible text. Install/command fences, the documentation table, the with/without
table, the benchmark tables and the Languages section stay in the open.

21 new folds across 10 sections. pargates: 361 gates, 358 pass, 3 standing skips,
0 fail.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Summary by CodeRabbit

  • Documentation
    • Expanded and reorganized the README with language support details, CLI commands, installation guidance, and MCP setup instructions.
    • Added practical command-output examples, benchmark results, profiling data, and prompt usage guidance.
    • Clarified benchmark methodology, limitations, reproducibility considerations, and instructions for extending language support.

Walkthrough

README.md was reorganized with expanded documentation for language support, usage, builds, quality metrics, benchmarks, profiling, MCP, prompts, and language extensions.

Changes

README documentation

Layer / File(s) Summary
Usage and build documentation
README.md
Documents supported languages, common commands, offline builds, vendored dependencies, installation prefixes, and CLI/MCP guidance.
Quality panel and command examples
README.md
Adds quality-panel metrics, evidence-family explanations, command examples, ranked-map details, test gates, and trace ingestion guidance.
Benchmark and profiling documentation
README.md
Updates Round 4 benchmark results and documents retrieval limitations, token measurements, hardware counters, LTO/PGO, and software-counter profiling.
MCP, prompts, and language extensions
README.md
Documents MCP server capabilities, prompt execution, prompt descriptions, and the steps for adding language support through vendored grammars.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Suggested reviewers: claude

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the README changes: a one-line language list and folded body sections.
Description check ✅ Passed The description directly explains the README language-list and collapsible-section changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/readme-languages

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 17-20: Update the top-level language contribution note in
README.md to mention that adding a language also requires its query file,
alongside the vendored tree-sitter grammar and declarative table row. Keep the
existing language list and reference link unchanged.
- Around line 814-817: Update the README section describing the two faster
builds so all prose matches the CMake defaults shown in the table: LTO is
enabled by default, while PGO remains opt-in. Remove or revise any wording that
says both options are disabled by default, including the nested details text.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f70c6cb4-dab1-4d9a-b210-0bc81a4c0323

📥 Commits

Reviewing files that changed from the base of the PR and between 34a4d00 and da6090b.

📒 Files selected for processing (1)
  • README.md

Comment thread README.md
Comment on lines +17 to +20
**Languages:** C · C++ · Objective-C/C++ · Metal · CUDA · Python · TypeScript · JavaScript · Java ·
Ruby · Bash · Go · Rust · Swift · C# · JSON — [sixteen vendored grammars](#languages), and adding
another is a vendored tree-sitter grammar plus one row in a declarative table.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Include the query file in the top-level language contribution note.

The top note requires a vendored grammar and one table row. The full instruction at Lines 1073-1076 also requires a query file. A contributor following only the top note can create an incomplete language integration.

Proposed wording
- another is a vendored tree-sitter grammar plus one row in a declarative table.
+ another is a vendored tree-sitter grammar, its query file, and one row in a declarative table.

As per path instructions: “Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
**Languages:** C · C++ · Objective-C/C++ · Metal · CUDA · Python · TypeScript · JavaScript · Java ·
Ruby · Bash · Go · Rust · Swift · C# · JSON — [sixteen vendored grammars](#languages), and adding
another is a vendored tree-sitter grammar plus one row in a declarative table.
**Languages:** C · C++ · Objective-C/C++ · Metal · CUDA · Python · TypeScript · JavaScript · Java ·
Ruby · Bash · Go · Rust · Swift · C# · JSON — [sixteen vendored grammars](`#languages`), and adding
another is a vendored tree-sitter grammar, its query file, and one row in a declarative table.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 17 - 20, Update the top-level language contribution
note in README.md to mention that adding a language also requires its query
file, alongside the vendored tree-sitter grammar and declarative table row. Keep
the existing language list and reference link unchanged.

Source: Path instructions

Comment thread README.md
Comment on lines +814 to +817
</details>

<details>
<summary>The two opt-in faster builds — LTO on by default, PGO at 14–25% cold, and why the output stays byte-identical</summary>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Resolve the LTO default contradiction.

The summary says LTO is on by default. The nested text says both options are off by default. The table also says the default build has LTO enabled. This gives users conflicting build instructions. State that LTO is on by default and PGO is opt-in, if the table reflects the CMake default.

Proposed wording
-<summary>The two opt-in faster builds — LTO on by default, PGO at 14–25% cold, and why the output stays byte-identical</summary>
+<summary>The faster builds — LTO on by default, PGO at 14–25% cold, and why the output stays byte-identical</summary>

- Two build options answer that, both off by default:
+ LTO is on by default; PGO is opt-in:

As per path instructions: “Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 814 - 817, Update the README section describing the
two faster builds so all prose matches the CMake defaults shown in the table:
LTO is enabled by default, while PGO remains opt-in. Remove or revise any
wording that says both options are disabled by default, including the nested
details text.

Source: Path instructions

@joyful-ii-V-I
joyful-ii-V-I merged commit 8b180c7 into main Aug 7, 2026
12 checks passed
@joyful-ii-V-I
joyful-ii-V-I deleted the docs/readme-languages branch August 11, 2026 14:22
joyful-ii-V-I added a commit that referenced this pull request Sep 5, 2026
…losure's OWN paging decision (jsoncheck #10)

verify-wave1 R2: mcpverbs.h emitted grep's row total under 'no --limit/--offset given' while M2 made
pageDisclosure's quintet (which carries total) ride whenever the listing was CUT, so every bare cut
answer — MCP grep and the CLI --batch grep: CDATA — carried the key twice. isPaging now asks
computePageDisclosure(...).paging, the one decision both halves share. Gate: jsoncheck.sh #10, a
duplicate-key sweep (json.loads + object_pairs_hook) over every CLI --json verb (#1-#7 + the #8b
universe), every tools/list verb's MCP envelope + payload (31 verbs, 11 JSON payloads on an alpha
fixture with a >100-hit grep), and every --batch CDATA payload. RED on e3b52d3 (MCP grep + --batch
'duplicate key total'), GREEN after; total survives on the uncapped bare payload (total=1 shown=1).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
joyful-ii-V-I added a commit that referenced this pull request Sep 5, 2026
…s it cannot honour

Wave-2 verifier finding R2. V1 made --pr-context's est_tokens price the document exactly, and that is
what made this legible: the number is now true, it is visibly larger than the ceiling the caller
stated, and the root said nothing. At fc71640, on a clean tree:

  --max-tokens=500   budget_tokens="500"  est_tokens="2002" trim_level="0" truncated="none"   4.0x
  --max-tokens=1000  budget_tokens="1000" est_tokens="2002" trim_level="0" truncated="none"
  --max-tokens=2000  budget_tokens="2000" est_tokens="2002" trim_level="0" truncated="none"

The NON-empty root already labels the identical condition (pickPrTrimLevel's last rung appends
";budget-floor-exceeded"), so the vocabulary existed; this root never reached it, because its whole
document is the legend and it has no ladder to descend. That is the same rule lane V2 closed on --for
this round ("a stated budget is never a SILENT overshoot"), applied to the root V1 made honestly
priced, and V1's own found-not-fixed #1 predicted it in words.

THE FIX. prEmptyRootPrice decides the price and the label together, because the second changes the
first: price at level 0; if a budget is in force and the price is over it, apply
truncated="budget-floor-exceeded" and RE-PRICE once. One pass is the fixpoint for pickPrTrimLevel's
own reason — the suffix lengthens the root tag, and a document already over budget cannot come back
under by growing — so the printed est_tokens is the delivered document's real price either way.
Verified: at --max-tokens=500 the clean root emits 5,305 B and est_tokens="2122" = round(5305/2.50)
exactly, delta 0. budgetTokens == 0 is UNLIMITED and no ceiling can be exceeded against it.

The legend now DEFINES the token it emits. The BUDGET clause described the floor condition but never
spelled "budget-floor-exceeded", so the attribute value was undefined prose on the non-empty root too;
one sentence names it, and names the clean-tree case explicitly.

GATE FIRST: test/prbudgetcheck.sh gains arm #10. The verifier's point was that F4's empty-root budget
arm (:175) only ever read the DEFAULT-budget root, where 2002 <= 8000 passes and the floor is never
reached. #10 states the property over BOTH root emitters and sweeps eight explicit --max-tokens
values across three roots (dirty working tree, clean working tree, =BASE) = 24 rungs, in both
directions:
    est_tokens > budget_tokens  => truncated= carries budget-floor-exceeded   (no silent overshoot)
    est_tokens <= budget_tokens => it does not                                (no false label)
plus: every rung echoes the budget it was given, and the labelled root ships the legend clause that
defines the term. It refuses to pass vacuously — at least one rung of the sweep must actually go over
and one must be inside, asserted as its own arm.

  RED at fc71640:  FAIL (#10) a --pr-context root exceeded its stated --max-tokens with nothing on
                    the root saying so   [empty-diff root, budgets 100/500/1000/2000: est=2027,
                    truncated="none", up to a SILENT 20.2x overshoot]
                    FAIL (#10) --pr-context --max-tokens=500 on a clean tree carries no
                    budget-floor-exceeded label
  GREEN after:      prbudgetcheck: ALL PASS, rc=0 (all four #10 arms)

Determinism x2 + xmllint on six roots (clean and =HEAD~1, each with no budget / --max-tokens=500 /
--max-tokens=100000): all DET, all well-formed, and est_tokens recounts the emitted bytes at
2.50 B/tok with delta 0 on every one — the label does not break the pricing fixpoint.

Gates green: prcontextcheck prbudgetcheck estchargecheck attrvocabcheck legendcoveragecheck
legenddriftcheck floormarkcheck jsonparitycheck pagingsweepcheck formatgatecheck deckcheck
readmedriftcheck.

--edit-check=prEmptyRootTail: status="contract-change" params 4 -> 5, callers=1, incompatible=0 —
deliberate, it is what carries the label. --quality-delta regressions=0 gating=0 (5 acked by exact
kind with a reason). Extracting prEmptyRootPrice rather than inlining the branch leaves
writePrContext's own complexity (154) and LOC (376) unchanged by this commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
joyful-ii-V-I added a commit that referenced this pull request Sep 6, 2026
--eval-retrieval built its gold set by walking symbol ids from 0 and stopping at the
first 150 doc-commented symbols. Ids are assigned in crawl order and the crawl is sorted
by path, so the gold set was "whichever 150 doc-commented symbols sort earliest by path".
bench/ sorts before src/. A contributor who documented a benchmark harness -- touching no
ranking code at all -- moved a published number. Every retrieval figure this project
publishes was drawn from that sample.

Reproduced as a controlled experiment at db6a416: one identical 60-symbol doc-commented
probe file, one otherwise byte-identical corpus, only the probe's path varied.

    aaa_probe/probe.h (early)   subtoken/name 0.834   subtoken/doc-phrase 0.620
    zzz_probe/probe.h (late)    subtoken/name 0.729   subtoken/doc-phrase 0.976

0.105 and 0.356 MRR from path spelling alone.

A second instance of the same defect sat in the metric: rankOfSymbol broke score ties by
`i < gold`, i.e. by id, i.e. by path. Invisible where the ranker has signal and total
where it has none -- on name-exact/doc-phrase nearly everything ties at zero, so the rank
was "how many files sort before mine" and that row's recall@1 was luck.

Gate first. test/knownitemcheck.sh gains three arms, all red on db6a416:
  #8  order-independence -- the same probe at an early vs late path must give identical
      numbers (the defect, reproduced as a test; mirror case included so it cannot pass
      by accident)
  #9  sample self-disclosure -- population=/scored=/rule= must be emitted, population >= scored
  #10 population re-derivation -- adding N doc-commented symbols must raise population by
      exactly N, derived from a controlled input rather than by reimplementing
      docPhraseFirstLine (which would clone the bug)

Fix: the population is every qualifying symbol and the eval is exhaustive over it
(rule=exhaustive; a disclosed path-free cut by smallest fnv1a64(scope::name) is the safety
valve above kMaxScored, cutting on the key so an identity is never split). Ties use the
midrank 1 + #better + #tied/2, which no ordering can move.

PUBLISHED NUMBERS MOVED. Not a ranking change -- the ranker is byte-identical. Repository
root, decomposed one fix at a time:

                                              subtoken/name  name-exact/name  ne/name r@1
    path-ordered 150-slice, id ties (old)         0.598          0.829           75.3%
    census population, id ties                    0.724          0.939           90.8%
    census population, midrank ties (now)         0.724          0.922           85.4%

On THIS root the sampler fix does nearly all of it and moves numbers UP. That direction is
NOT a property of head-slicing: the slice reports whichever corner of the tree sorts first,
and the two published roots disagree -- at the root it UNDERSTATED (0.598 vs a true 0.724),
in src/ it OVERSTATED slightly (name-exact/name 0.977 vs 0.974). The honest word is
ARBITRARY, and an earlier draft of this message calling it "systematically harder" was the
same overreach the entry exists to record.

The consistent downward movement in src/ is therefore the MIDRANK fix, not the sampler:
on src/ the sampler moved name-exact/name MRR by 0.003, the midrank by 0.014. Most of that
is tie-luck removed, but part is the convention -- midrank is deliberately pessimistic,
scoring a two-way tie at the top as 1.5, taking no recall@1 credit where id-tiebreak gave
rank 1 whenever gold's id was lowest. Not all of it is luck.

README's src/ headline is re-pinned 98.0% -> 91.3% recall@1. NOT all instrument: the 98.0%
was last measured on an older corpus, and no clean decomposition of corpus-drift vs
instrument is available for that figure, so it is re-pinned rather than explained.

The two defects COMPOUND, which is why neither was visible alone: a sample biased toward
early-path symbols, graded by a metric that rewards early-path symbols.

Re-pinned in the same commit: README.md, docs/EVALS.md S4, bench/ANSWERQUALITY.md,
docs/COMMANDS.md (regenerated through docs_commands_build.py from an updated capture).
docs/EVALS.md S7 gains the dated record so the discontinuity is not misread later as a
ranking regression. Two dated records are deliberately NOT rewritten -- the Terminality
round A pre-registration and the 2026-08-08 fa4639e A/B -- because retroactively editing a
pre-registration is the thing pre-registration exists to prevent.

NOT done: the fixture was not touched. Stripping the docstrings or excluding bench/ would
have restored 0.600 while leaving the defect in place, would need redoing every time an
early-sorting file gained a comment, and would teach the next contributor that documenting
code breaks the build.

Cost: exhaustive scoring is ~23x the old slice (each scored symbol drives eight
whole-corpus rankings, two of them full PageRank power iterations). It buys nothing for
users -- the ranker is unchanged -- and nobody pays it in a normal workflow: not an MCP
verb, classed a maintainer harness in skills/ripwire-router/SKILL.md, and the only
automated caller is knownitemcheck on src/. It buys honest published numbers.

Follow-up not taken here: ~45% of the eval's CPU is system time from allocating and
freeing N-sized vectors inside the per-query loop. Hoisting those would roughly halve the
runtime without changing a digit of output.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
joyful-ii-V-I added a commit that referenced this pull request Sep 11, 2026
CodeRabbit #127 finding 3985249704 (src/mcpverbs.h:1269, +1313) — VALID.

`blast_radius` and `forgotten` are both windowed by pageWindow with the same limit/offset,
and the payload carried no shown count, no total, no has_more and no next_offset for
either. A caller could not tell that rows had been omitted and could not construct a next
request. The CLI twin has disclosed its blast-radius cut in prose since C1 F-10 (situ.h's
situShowingNote: "showing 8 of N files — shown=8 total=N capped=1"), so this was the two
dialects saying different things about the same run.

THE SHAPE IS --test-gate's, not a new one (§B7.1, situ.h::writeTestGateReportJson): two
INDEPENDENT listings in one report is exactly the case pageview.h rule 6 answers with
rule 1's noun-prefixed exception, because a bare `shown` next to two arrays is ambiguous
in a way `shown_blast_radius` cannot be. So the payload now carries
  shown_blast_radius / blast_radius_capped   — the pair for listing one
  shown_forgotten / forgotten_capped / forgotten_total — listing two, with its own
      row population (no other key carried it)
  total / has_more / next_offset / offset / limit — pageview.h's ONE disclosure under the
      JSON syntax row, describing the PRIMARY listing (blast_radius, the array it
      precedes). Its `total` IS blast_radius's total; a second spelling would be the
      duplicate key jsoncheck #10 pins.
Both _capped bits and both shown counts are DERIVED from the rows the document actually
emits. pagingDisclosure emits nothing when no window applied, so a bare call (this verb's
default is unbounded) gains only the four derived counts.

Live, on this repo:
  bare     shown_blast_radius=5 blast_radius_capped=false shown_forgotten=80
           forgotten_capped=false forgotten_total=80
  limit=2  shown_blast_radius=2 blast_radius_capped=true  shown_forgotten=2
           forgotten_capped=true forgotten_total=80
           total=5 has_more=true next_offset=2 offset=0 limit=2

GATE ARM, test/mcpcontractcheck.sh §G, beside the three live paging probes already there:
eleven checks over a bare run and a limit=2 run, every one of them DERIVED (shown must
equal the rows served; capped must equal shown < total), so a hand-written constant cannot
satisfy it.
CAN GO RED, observed against the pre-fix binary: all eleven fail with
  shown_blast_radius=None … total=None has_more=None next_offset=None offset=None limit=None

Green: mcpcontractcheck ALL PASS, mcpattrparitycheck ALL PASS, mcpclidiffcheck ALL PASS,
jsoncheck ALL PASS (incl. the --json determinism gate).
joyful-ii-V-I added a commit that referenced this pull request Sep 11, 2026
…— the default comparator aborted every Linux G1 ingest

The asan (ubuntu-24.04) job on #135 (run 34583440115, head fed22de) failed at its first gate after the
fixture: EVERY ripwire invocation in packtaskcheck died rc=134 with empty stderr, the --pack-task refusal
arms included. Reproduced in an ubuntu:24.04 container (clang 18 + libstdc++ 13) with CI's exact
ASAN/UBSAN/LSAN options and log_path dropped so the report reaches stderr:

  include/c++/13/string_view:576:38: runtime error: unsigned integer overflow: 8 - 13 cannot be
  represented in type 'size_type' (aka 'unsigned long')
    #0  std::basic_string_view<char>::_S_compare            string_view:576
    #8  std::sort<vector<string_view>::iterator>
    #9  rw::extent::resetExtentScratch                      src/extentsuspect.h:185
    #10 rw::extent::classifyFileExtents                     src/extentsuspect.h:221
    #11 rw::(anonymous)::markExtentSuspects                 src/ingest_model.h:403
    #12 rw::ingest                                          src/ingest.cpp:320

Not a leak, not an ODR violation, not init order: UBSan's `integer` group on a libstdc++-internal wrap.
libstdc++'s string_view::compare settles a prefix pair by computing `n1 - n2` in size_type; G1 runs
-fsanitize=integer under -fno-sanitize-recover=all, so the first comparison of two same-file C++ class
names where one prefixes the other kills the process. libc++ never subtracts, which is why both macOS
legs (macOS ASan included) and every local run were clean, and why no ignorelist entry covers it — the
three libstdc++ entries name bits/string_view.tcc and basic_string, not <string_view> itself. It fires
inside ingest, before verb dispatch: hence the empty stderr on the refusal arms. test/fixture holds no
prefix pair, so the fixture step passed; src/ holds one, so every run over it aborted. main 096e354,
built and run the same way, is clean (--for rc=0 with no report; bare --pack-task refuses rc=1).

The house rule already existed — src/infra/sortutil.h: order string_views through svLess, never
operator< (main went red the same way at 69a17f9). The extent detector's std::sort over
ExtentScratch::cppClassNames and std::binary_search over its span both took the default comparator. Both
now pass rw::sortutil::svLess, the SAME total order operator< defines, so the sorted table and every R2
answer are byte-identical: no kParserVer or kCacheVersion bump, no capture regeneration.

WHY THE EXISTING GUARD MISSED IT, AND THE ARM THAT NOW SEES IT. portablebuildcheck #6 greps for
binary_search|lower_bound|upper_bound|equal_range on lines that SPELL string_view. Neither call names its
element type (a member declared 67 lines up, a span parameter), and std::sort was never matched at all.
New arm #6b takes every ordered algorithm called at default arity, resolves the container its first
argument ranges over through the nearest preceding declaration in the same file, and exempts
static_assert; planted positive and negative controls run every time, so a scanner that goes quiet fails
the arm. Red-first: on fed22de it fails with exactly src/extentsuspect.h:159 and :185 (exit 1); green on
this fix and on main 766913d (exit 0); about 0.7 s over src/. Stated blind spots: a container declared in
another file, an auto-typed container, a comparator lambda that applies < to string_views itself.

Dynamic twin: extentsuspect_unit gains an R2 case whose class names prefix one another (Tree, TreeGuard,
TreeGuardScope). extentcheck (U) builds the driver with the binary's own flags, so under the Linux ASan
build the unfixed header aborts it (`9 - 14` from resetExtentScratch, rc=134, (U) FAIL); with the fix it
passes and pins the answers on every platform.

VERIFIED in an ubuntu:24.04 aarch64 container (clang 18, libstdc++ 13) under the asan job's exact env,
log_path=/tmp/asanlog included: ripwire_asan_fixture rc=0; packtaskcheck ALL PASS (39, both refusal arms
print again) with /tmp/asanlog* empty; tracecheck, qualitycheck, mergescoutcheck, cachefuzzcheck,
extentcheck and macroreparsecheck ALL PASS; det-gate and xmllint pass; --for over src/ rc=0; a bare
--pack-task refuses rc=1.

editcheckcheck and cppqualcheck went red in that container for a reason that is neither this lane's nor
CI's: the vendored tree-sitter-yaml scanner returns SCN_FAIL (-1) from `static char scn_pln_cnt`
(third_party/deps/yaml/src/scanner.c:828), char is UNSIGNED on aarch64 Linux, and G1's
implicit-integer-sign-change aborts every whole-repo ingest that meets a YAML file. x86-64 and Apple arm64
have signed char, so neither CI leg can see it; main 096e354 fails both gates identically in the same
container. With that one file exempted by a verification-only ignorelist (not part of this commit),
whole-repo --for, editcheckcheck (48 PASS / 0 FAIL), cppqualcheck (66 / 0) and --doc-drift all exit 0 with
/tmp/asanlog* empty — so no ripwire code, #135's extent and re-parse passes over the whole repo included,
trips a sanitizer there.

macOS plain build --clean-first: portablebuildcheck, extentcheck ((U) 20 rule cases), macroreparsecheck,
packtaskcheck, gateexitcheck, binoverridecheck, worktreeleakcheck, manifestcheck, ripwirepubliccheck,
docscommandscheck and showcasecapturecheck pass; --quality-delta gating=0 (two minor rows: the unit
driver's main() grew by its new case).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
qinghuanandejiangshi pushed a commit to qinghuanandejiangshi/ripwire that referenced this pull request Sep 17, 2026
… row

A C++ out-of-line member of a class template whose template-argument list
spans source lines has a scope holding that line break verbatim, and
--pin-census wrote ids raw. One C row became a 6-field line plus a
continuation line starting with neither C/S/O nor #, and the symbol's S row
broke the same way (seen once on a large private C++ corpus). The map never
had the defect: escapeXml writes the same scope as &redhat-et#10;. Five more
spellings reproduce on the pre-fix binary: a TAB or a form feed in the
argument list, a backslash line splice, CRLF source, and a | inside an id,
which is the targets separator.

The fix: every id and callee field goes through appendCensusField (a
Byteset256 + appendCleanRun escaper, the escapeXml shape): \ -> \\, TAB/LF/CR
-> \t \n \r, every other C0 byte and | -> \xHH. Columns are unchanged and an
id without those bytes is byte-identical to v2; the header line says v3 and
documents the escape. bench/scip_match_diag.py decodes the fields (it opens
files by an id's path); scip_pin_precision.py joins ids as opaque keys.

Measured, pre-fix vs fixed binary over a clean `git archive a55b118`
export: 99 of 51,821 rows change (18 C, 81 S; 98 hold |, 1 a backslash) and
all 99 decode back to their pre-fix bytes; the 18 C rows are Markdown-heading
targets such as `--token-budget=N[K|M|G]` that a |-split read as 2-6 targets
(18 misread before, 0 after). Every other row is byte-identical.

Gate: test/pincensuscheck.sh arm (L), over a generated fixture (a pipe|dir
directory, CRLF and TAB bytes are never committed). Every non-comment line
is a C/S/O row with its field count (also over the (B) and (G) censuses);
each awkward caller id decodes to its source bytes, re-encodes identically
and is an S id verbatim; the | target splits to one id; dispositions and
summary counts match what a line reader parses; a control un-escapes one C
row and the same shape check sees it. (B) now expects v3.
The \xHH digits come from a constexpr table, not a format call into a
char[] (test/fixedbufsweep.sh refuses an unclassified fixed buffer), and a
form-feed case holds the zero-padded low-byte path to the documented rule.
Red on the pre-fix binary: 12 FAIL (a line reader parsed 2 of 6 C rows and
12 of 16 S ids). Green: 52 PASS, also under ./asan/ripwire, which reports
nothing over the census fixtures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
s0undt3ch pushed a commit to s0undt3ch/ripwire that referenced this pull request Sep 20, 2026
…led like the appended token

On a clean tree --pr-context's whole truncated= value is "budget-floor-exceeded" (no trim level to append
to), so the settle that removes ";budget-floor-exceeded" from an answer that fits once compacted missed it:
est_tokens=374 inside --max-tokens=500..2000 still claimed the floor was exceeded (prbudgetcheck redhat-et#10). The
whole attribute is removed there, and its compact reading with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant