Skip to content

docs(research): measure --slice line recall on an issue-derived Python corpus - #318

Draft
joyful-ii-V-I wants to merge 8 commits into
mainfrom
lane/research-arise-slice
Draft

joyful-ii-V-I wants to merge 8 commits into
mainfrom
lane/research-arise-slice

Conversation

@joyful-ii-V-I

Copy link
Copy Markdown
Collaborator

This runs ripwire's --slice primitive against the question the paper it came from actually asks. --slice is our bounded first rung of the statement-level def-use primitive in ARISE (arXiv:2605.03117), which measured such a primitive at +17 pp Function Recall@1 and argued the binding constraint was the graph's granularity floor rather than its ranking. Its docs/LINEAGE.md row is at L87. docs/EVALS.md has carried the registered contract and, since August, results on four C/C++ corpora; this round asks the harder question on Python, on issue-derived fix patches, with gold taken at the pre-fix tree — the setting an agent is actually in. Full write-up: docs/research/slice-line-recall.md.

The protocol is committed before the numbers exist (results section deliberately empty in the first commit), and the three things the original registration left open — which tree, what counts as gold for a pure insertion, what "surfaces" means when the rows are unordered — are closed as dated amendments rather than decided afterwards. 560 dataset rows, 173 scored, 498 (instance, variable) pairs, deterministic, re-run identical. No src/ changes. (Base note: measured against main at 755f9026, before 0.6.2; the diff still merges cleanly onto today's main, 15a20855, tag 0.6.2.)

What it says, including the parts that do not flatter us.

  • The ceiling dominates. 70.8% of the corpus's gold lines are in fixes that span functions, which an intra-procedural slice cannot address at all. Of the reachable remainder, 56.7% of gold lines name a sliceable local — that is what accuracy multiplies against, reported on its own line rather than folded in.
  • Accuracy is high and the misses belong to the oracle. Per-variable line recall 0.932 under the registered word-regex relevance oracle, 0.995 under a tokenizer-decided one. All 100 misses were classified rather than sampled; 97 are a name inside a comment, docstring or string. The three that survive are one construct: a keyword-argument name colliding with a local.
  • Finer granularity does change retrieval quality, not only presentation — by about 10 points, mostly through the filter rather than the ranking. Given the correct function at a 512-byte budget, whole-function delivery surfaces 17.6% of gold lines, def-use-filtered lines 27.6%, ranked line-level 32.1%. But Recall@1 is 0.048 against a 0.042 chance rate — the primitive re-ranks a shortlist, it does not name the line.
  • Our rung 2 is redundant where we unseed it. Unioned over a function's whole inventory, --slice-flow=both reaches no line the flat rows do not — a flow row at depth ≥1 is by construction a line in some other variable's own slice.

This is a first pass with a lot further to push, and the write-up states plainly where we are weakest against the original: name-based occurrences instead of true def-use edges, source lines instead of AST statement nodes, no alias analysis, no lexical scope separation in Python, no inter-procedural tier at all. We would like correction on, first, whether the paper's own gain came from coverage or from precision — our construct loses almost nothing in coverage and pays heavily in precision, and the answer decides whether name-based def-use is worth extending or worth replacing — plus four more questions in the doc's closing section.

Our working premise across this line of investigations: algorithmic, deterministic checks applied while an AI writes are the practical way to keep code sound at the speed AI now writes it — no label, no vendor claim, a check that either fires or it doesn't.

🤖 Generated with Claude Code

barefootski and others added 4 commits September 20, 2026 14:45
…e-derived Python corpus

The 2026-08-28 registration in docs/EVALS.md has been run twice, both times on
cpp corpora mined from git history, scoring ADDED lines at the POST-fix tree.
This registers the py-family, issue-derived, PRE-fix-tree reading of the same
shape, and three things the original registration left open (which tree, what
is gold for a pure insertion, what "surfaces" means when rows are unordered),
plus the rank, reachability, cost and granularity-vs-presentation questions the
ARISE paper (arXiv:2605.03117) actually raises.

No number produced by the harness exists at this commit; that is the point of
committing the protocol first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ix corpus

Harness (bench/slice/): locbench_gold.py builds pre-image line-level gold from
a LocBench-shaped dataset and local checkouts without invoking ripwire, so the
gold cannot move when the binary does; run_slice_linerecall.py measures set
recall, rank against a random-order control, the reachability cascade, cost in
bytes and wall time, and a byte-budgeted file/symbol/line granularity
comparison; inspect_slice_misses.py classifies every miss with Python's own
tokenizer; probe_wholerepo_selector.py prices the one-file-tree deviation.

560 dataset rows, 182 carried, 173 scored, 498 (instance, variable) pairs. The
run is deterministic: two end-to-end runs compared identical row for row.

Three findings worth a reader's time. Seven in ten of the corpus's gold lines
live in fixes that span functions, which an intra-procedural primitive cannot
address at all — the ceiling, stated apart from the accuracy. Under a
tokenizer-decided relevance oracle the per-variable line recall is 0.995 and
the three residual misses are all a keyword-argument name colliding with a
local. Unioned over a function's whole inventory the flow rows reach no line
the flat rows do not, because a flow row at depth >= 1 is by construction a
line in some other variable's own slice.

docs/EVALS.md gets a cross-reference, not a number: this corpus scores
pre-image lines at the base commit where the four cpp corpora score added
lines at the post-commit tree, so the two are not comparable and are never
averaged. docs/README.md indexes the new directory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…mains

--quality-delta named two clone pairs the new py-round harnesses created against
the existing cpp one: my git() against its sh(), my text_of() against its
line_text(). Both now live in bench/slice/_common.py, together with the strict
NAME-token oracle the runner and the inspector both use, and run_slicerecall.py
imports them rather than keeping its copies. The extraction is textual - the
bodies are unchanged - and the proof is that the gold file and every measured
row still compare identical after it.

The two oversized new mains lose their per-row bodies to measure_instance() and
carry_row(), which also makes every early exit a counted skip in one place.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…easurement loop

measure_instance was 126 lines at complexity 49 with nine parameters. The two
scoring blocks it ends with are independent of everything above them, so they
become rank_scores() and budget_scores(), and the four accumulators that outlive
a row travel as one sink. Textual extraction again: the measured rows and the
summary compare identical to the run before it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 21, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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

barefootski and others added 4 commits September 21, 2026 11:52
Eight research lanes each added their own wording for the same `docs/README.md`
table line, which is eight conflicts on one line the moment two of them land.
This is the shared text, byte-identical everywhere, so the same addition on two
branches merges clean. The entry count above the table is corrected with it:
the table has held twenty rows for some time while the sentence still said
sixteen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…lti-function ceiling

--slice is intra-procedural by declaration, and §R3 of slice-line-recall.md already measured that
70.8% of the LocBench corpus's gold lines (6,809/9,615) sit in fixes touching more than one function
-- out of reach by construction. Before extending --slice across call boundaries, measure whether the
call graph the tool already builds would actually recover them: for each gold line outside the seed
function, --at names its true enclosing symbol and --path reports the directed hop count from the
seed, deduped per distinct symbol and cached per row.

This commit adds the protocol and the pre-registered decision bands (extend_2 = (hop1+hop2)/6809;
>=50% builds it, <20% kills it) before any number exists -- §10.4/10.5/10.6 are headings only, same
discipline the 2026-09-20 round's own pre-registration commit (3d994cd) used. A follow-up commit
fills in the results once bench/slice/run_cross_fn_reach.py finishes.

archive_tree() moves into _common.py (probe_wholerepo_selector.py's own git-archive materialization,
now shared with the new script) rather than growing a second copy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…hin 2 hops -- kill it

bench/slice/run_cross_fn_reach.py against the 122 measurable multi-function rows (2,913 of the
6,809-gold-line ceiling; 80 rows lack a local checkout, 2 have no base_commit, 4 refused the
full-path seed selector): extend_2 = (hop1+hop2)/6809 = 6.14%, well under the 20% kill band fixed
in 2173712 before this ran. hop1=4.3%, hop2=1.9%, hop3plus=0.9%, unreachable=22.9%,
no_enclosing_symbol=4.5%, hop0 (already reachable today)=8.4%. At the instance level, extending
reach to depth 3 still leaves 95% of measured rows with at least one gold line out of reach.

The one open thread: 83.8% of unreachable gold lines sit behind a symbol that has SOME graph edge,
just not on a directed path from the seed -- consistent with sibling functions a patch touches
together via a shared caller, which --path cannot see and --connect might. Recorded in §10.6 as
future work this round does not answer; the 6.14% verdict is scoped to directed extension only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…lds either way

extend_2 = 6.14% was reported only against the full 6,809-line ceiling, of which 57.2% was never
measured (no checkout/commit/seed refusal). Scoring the unmeasured share as non-extendable is the
conservative choice, but a reader can reasonably ask for the figure over the 2,913-line measured
subset alone: (hop1+hop2)/2913 = 418/2913 = 14.35%, verified independently from
run_cross_fn_reach.py's own results.json rather than assumed. Both stay under the pre-registered
20% kill band, so the verdict does not depend on which denominator is used.

Also: names --connect=SEED,TARGET (or an equivalent undirected/bidirectional reach) over the 83.8%
of unreachable gold lines that have graph edges but no directed path from the seed as the specific
follow-up measurement that would need to change before this verdict does, rather than leaving that
as a vague caveat; and states the verdict's scope explicitly (LocBench Python multi-function
fixes, edit_functions[0]-seeded, directed reach only) so it is not read as a general claim about
slicing.

Co-Authored-By: Claude Sonnet 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.

3 participants