Skip to content

v0.6.8

Latest

Choose a tag to compare

@github-actions github-actions released this 19 Oct 23:43
· 214 commits to main since this release
cd11226

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_acks rows and .ripwire_notes pinned to a Go method stop matching: re-pin them (--notes lists an old note with dangling="1"; --note-add with the same target stores the new id).
  • Scripts that read next=: on a cut --callers answer it is now the next page (the --uses follow-up moved to uses_next=); on a capped --callees page the --expand follow-up moved to expand_next=; on a --for answer with a body fold, <sigs next=> names the first skipped body and the signatures-only call moved to sigs_next=.
  • --for with no --token-budget now stops when it is complete (a 7,500 B target, a 30,000 B guard that says when it fires); an explicit --token-budget is a soft target whose cuts stay recoverable through next=.
  • Git: ripwire now pins repository-configured filter and diff programs to nothing for its own git commands (--doctor reports it). Files behind a repository-local filter (git-crypt, a per-repository LFS driver) are read as stored.
  • --quality-delta=A..B under 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. --for stops when it is complete, opens one owner body for a "how does X work" question, appends bodies and names more files; --callers and --callees serve up to 50 rows whole; --impact of 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; --grep names 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.T proves a class only through an in-tree q; x op= v rebinds; Ruby and Python x[k] is a call only where the receiver is proven.
  • Hedged instead of silent. --safe-delete reads risk="hedged" for a method getattr(obj, f"watch_{name}") or this['compile' + x] can reach; a multi-definition answer says which definition each row belongs to.
  • Memory and safety. --quality-delta=A..B holds one commit's index at a time (a 20,356-file repository's HEAD~1..HEAD had 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 marked via="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. Elixir unquote(...) calls are calls again (#357, #380).
  • Faster. --pr-context=HEAD~30 on 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 a git 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 --for answer; 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.