Repository navigation
ripwire 0.6.8: more complete first answers, and fewer false edges. This release collects four batches of reviewed changes (trains 28 to 31). --for stops when its answer is
complete, --callers and --callees serve more rows whole, --impact of a type finds the code that uses it, and the call graph proves more receivers and hedges more
guesses, with the guesses labelled. The numbers below say what was measured, on what, and what each one does not show.
Read this first
- Caches are rebuilt once. Every ingest cache and quality snapshot an earlier build wrote is refused and re-indexed on first run (parser version 206, cache version 36, snapshot scheme 22).
- Go: a method's id is now
file::Type::method. Quality-baseline rows,.ripwire_quality_acksrows and.ripwire_notespinned to a Go method stop matching: re-pin them (--noteslists an old note withdangling="1";--note-addwith the same target stores the new id). - Scripts that read
next=: on a cut--callersanswer it is now the next page (the--usesfollow-up moved touses_next=); on a capped--calleespage the--expandfollow-up moved toexpand_next=; on a--foranswer with a body fold,<sigs next=>names the first skipped body and the signatures-only call moved tosigs_next=. --forwith no--token-budgetnow stops when it is complete (a 7,500 B target, a 30,000 B guard that says when it fires); an explicit--token-budgetis a soft target whose cuts stay recoverable throughnext=.- Git: ripwire now pins repository-configured filter and diff programs to nothing for its own git commands (
--doctorreports it). Files behind a repository-local filter (git-crypt, a per-repository LFS driver) are read as stored. --quality-delta=A..Bunder a memory limit can answer with the kinds it can decide and exit 6 (new) when that partial answer has no gating row;--quality-kinds(new) runs only the kinds you name.
Measured: the same 173 questions, 0.6.5 then 0.6.7 then 0.6.8
Each answer is graded two ways: a stricter method (a rubric item counts only when the answer names the item's primary symbol, or at least two of its listed terms) and a more lenient one (any one listed term). A question counts as answered completely when its first answer covers the question's pre-written must-mention list. Only the first answer to each question counts.
| first answer complete, of 173 | 0.6.5 | 0.6.7 | 0.6.8* |
|---|---|---|---|
| stricter method | 50 (about 29%) | 83 (about 48%) | 98 (about 57%) |
| more lenient method | 78 | 109 | 127 |
| the 10 questions from one repository we set aside before any build for this release ran (stricter) | 2 | 5 | 6 |
| answers that claimed a wrong target (mechanical check against pre-listed wrong targets) | 3 | 0 | 0 |
* The 0.6.8 column was measured on the train-28 build of 0.6.8, the first of the four batches. The later changes in this release (trains 29 to 31) were measured only on their own target questions, in the next section, and are not in these counts. A re-run on the final 0.6.8 build of the 29 questions the train-28 build had answered completely only by the more lenient method: 9 are now complete by the stricter method too, and none of the 29 lost a matched item (the other 144 questions were not re-run). Grading is mechanical and the same machine ran all three builds. A hand read for false claims covered a sample of the 0.6.7 and 0.6.8 answers; the 0.6.5 answers were barely read, so their false-claim count is a lower bound.
- Since 0.6.7, 15 questions got better and none got worse (stricter method), and the mechanical wrong-target check found no wrong-target answer in either build.
- By language (stricter method), 0.6.5 then 0.6.7 then 0.6.8: C 5, 12, 13; C++ 6, 9, 10; JavaScript/TypeScript 13, 20, 23; Python 12, 20, 21; Go 4, 6, 8; Ruby 3, 6, 10; Rust 5, 5, 7; Java 2, 5, 6.
- What it costs: the first answers to the 60 development questions total 356,443 B with 0.6.7 and 427,247 B with the 0.6.8 build measured here (+19.9%).
- Other tools: on the 120 of these questions that five other code-intelligence tools also answered (unnamed here; each at its version as of 2026-10-08; one of them covered 108 of the 120), those five answered 13 to 35 completely with the stricter method and 23 to 44 with the more lenient one, against ripwire's 66 and 89 on the same 120 (ripwire at the build described above). On the final 0.6.8 build, 7 of the 23 questions in this set that ripwire answered completely only by the more lenient method are now complete by the stricter one too, and one (fzf-02, in the table below) went the other way; the other 97 were not re-run. A named, complete comparison is planned for 0.7.0.
- How to read it. On an earlier 163-question round a hand read of the grader's output fell between the two methods (more lenient 99, stricter 75, hand read about 81 to 85), so treat the truth as between them. This is one round on our own questions, and it does not include trains 29 to 31.
Later changes, measured on their own target rows only
Measured per change, on that change's target rows, not a full table. The rows differ per change and overlap, the base builds differ, so do not add these up. "Stricter" and "more lenient" are the two methods above.
| batch | change | rows measured | before, then after (items matched, or complete / not) |
|---|---|---|---|
| 29 | callers served whole up to 50 rows | ninja-06 --callers=Stat |
stricter 2 of 6 items, then 6 of 6 (+270 B) |
| 29 | --impact of a type starts from the symbols that name it |
tmux-10 --impact=grid_cell; hono-10 |
0 of 32 files, then 32 of 32 (1,341 to 6,141 B); 12 of 14, then 13 of 14 |
| 29 | minimum arity: an overload a call cannot reach is not listed | ninja --callees=ParseDefault |
the unreachable second candidate is gone; stricter 8 of 8 before and after |
| 29 | Go receiver scope (#403); Rust standard-type member gate (#405) | fzf --path with qualified names; mdBook --callers=iter |
"endpoint not found", then a 5-hop chain; 6 false callers removed (stricter 2 of 7 before and after) |
| 30 | f.call() and f.apply() are calls |
fastify-14 | incomplete, then complete (both methods) |
| 30 | function-local Go/Rust/GDScript declarations are not symbols | 28 fzf and nfpm cells; 574 symbols removed on fzf, gotestsum, nfpm | stricter 12 before and 12 after: fzf-12 incomplete to complete, fzf-02 complete to incomplete (its old credit rested on a call edge to a function-local var) |
| 30 | --for tail rows name their file's top symbols |
120 --for cells |
stricter 41, then 43 (fastify-13, starlette-11); more lenient 88, then 89; none lower; +1.2 KB per answer on average |
| 30 | --path hops read Owner.method |
16 --path cells |
none lower; flake8-14 matched 1 of its items, then 4 |
| 30 | a call's result type types the next receiver (Go, Rust, Java, Kotlin, TypeScript) | 8 target cells (nfpm, mdBook); a census on nushell and envoy | no cell moved; the binding of 880 call sites on nushell (Rust) and 76 on envoy (Go, Rust, Kotlin) changed, 16 nushell sites checked by hand, 0 false |
| 31 | --for appends the bodies of its top ranked rows |
53 --for rows |
stricter +4 (hono-02, hono-11, koa-02, textual-02), none lost; the whole answer grows by 3.4 KB median |
| 31 | a callers list is served whole while its rows fit 16,000 B | tmux-07 --callers=options_get_number |
40 of 121 callers, then 121 of 121, in one call (4,604 to 11,434 B) |
| 31 | a qualified annotation proves a class only through an in-tree qualifier | 62,750 call sites on six repositories | 0 change: an honesty fix that no measured row needed |
| 31 | --for's first eight rows name eight files; a .d.ts ranks below its implementation |
44 rows | stricter 27, then 29 (fastify-11, fastify-13, from the .d.ts rule); more lenient 39 before and after; files named in the first 8 rows 3.96, then 8.00 on average |
| 31 | --path names what an unreached path stopped at and offers a hedged bridge; --callees lists closures |
the 54 hardest rows | stricter 1, then 3 (htop-14, nunjucks-21); more lenient 24, then 26; none lower; 0 wrong hits (textual-11 from refused to partial) |
What got better, in words
- Complete answers.
--forstops when it is complete, opens one owner body for a "how does X work" question, appends bodies and names more files;--callersand--calleesserve up to 50 rows whole;--impactof a type reaches the files that use it; the calls the resolver declined are listed as hedged<dc>rows instead of being counted and dropped;--grepnames who reads a constant. - Fewer false edges. Go methods carry their receiver type; a local bound to a call takes the callee's declared result type; a Rust call named like a standard collection member binds no in-tree namesake; a TypeScript or Python annotation
q.Tproves a class only through an in-treeq;x op= vrebinds; Ruby and Pythonx[k]is a call only where the receiver is proven. - Hedged instead of silent.
--safe-deletereadsrisk="hedged"for a methodgetattr(obj, f"watch_{name}")orthis['compile' + x]can reach; a multi-definition answer says which definition each row belongs to. - Memory and safety.
--quality-delta=A..Bholds one commit's index at a time (a 20,356-file repository'sHEAD~1..HEADhad been killed at an 8 GiB limit, #387); on Linux the guard counts the cgroup's real charge, not just the resident set. - Markdown. A link
[text](other.md#slug)edges to that heading instead of the file, and a link no call shares is markedvia="link"(@muayyad-alsadi, #409, #414). - Languages. Clojure (
.clj,.cljs,.cljc,.bb; #349) parsed 4,930 of 4,933 files from four public checkouts; that is parser completion, not call-resolution accuracy. Elixirunquote(...)calls are calls again (#357, #380). - Faster.
--pr-context=HEAD~30on this repository: 49.45 s and 45.79 s became 8.66 s and 7.67 s (two interleaved runs each, output byte-identical). The startup git check no longer spawns agit rev-parse(269 to 351 ms in our four-repository measurement).
What did not move, and what it cost
- Answers are larger: +19.9% on the 60 development questions above; the body fold adds 3.4 KB median to a
--foranswer; in the callers-band change's sweep, 89 of 158 answers changed, by a median of +3,357 B. - One change aimed at five rows and flipped one (rake-02; the bar was four). Two of the three rows built for the owner body flipped (fastify-12, ninja-11); koa-12 did not.
- C++ and Objective-C call results are not typed yet: a macro can declare a name no reader sees. Rust receivers written as an in-tree type are declined and counted, not bound (5 true mdBook edges declined, 72 false claims removed).
Thanks
Code: @LaimaWu (#380 Elixir unquote, #382 Python imports on shared captures), @mpapis (#396, #401, #412: Ruby mixins and the Rails/DSL work on the shared path; slice 4a, #400), @tonibergholm-codento (#387 report, #391 and #392: the memory fixes) and @tag-und-nacht (#349, Clojure).
Ideas and reports: @muayyad-alsadi (#409, markdown as a graph; code in #414, links that bind the exact heading), whose work also inspired the HTML help-wanted issue #417 (slices you can claim). Other help-wanted slices are listed in CONTRIBUTING.md, which now says where Rust, C++ and Go need the most help.
Open pull requests we have not merged yet: #397 (@pt-act), #402, #410 and #411 (@lennix1337). Thank you.
Versions and caches
Parser version 206, cache version 36, quality-snapshot scheme 22; the legend dictionary is dictv=30173b0aa2d06473 entries=908. Caches written by 0.6.7 or any earlier build are rebuilt once.
Full notes: CHANGELOG.md, section 0.6.8.