Skip to content

Python recall (operators, library bases, context) and --library auto for every language - #1886

Merged
swapnilpaliwal-sd merged 15 commits into
apps/integration-0.1.9from
fix/py-recall-3
Oct 10, 2026
Merged

swapnilpaliwal-sd merged 15 commits into
apps/integration-0.1.9from
fix/py-recall-3

Conversation

@swapnilpaliwal-sd

@swapnilpaliwal-sd swapnilpaliwal-sd commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Python recall, round 3: four commits, each with a case that fails on the base and passes here, plus a control. Stacked on #1885.

commit what tests recall (tuning / held-out) other verbs
operator / truth protocol a == b, a | b, x in c, -a, if x:, not x, x or y run the operand's dunder (OPERATOR_PROTOCOL, TRUTH_PROTOCOL edges) +1 / +4 files, 9 extra, 0 lost callers src recall 0.707 -> 0.714, test->target path 0.528 -> 0.541
library class through a dotted base, a star re-export or a generic alias class C(pkg.mod.Base), collections.abc.X -> _collections_abc.X, Mapping = _alias(abc.Mapping, 2) +1 / +536 files (a CLI subject subclassing its library by dotted name) test->target path 0.541 -> 0.559
a library-callback site keeps its name match unless a hand-back from it is summarised restores 2 failing files the previous commit's new library site had dropped +2 / 0, 17 extra
context: a headline that names no code chooses the scope from the body 6 issue-shaped tasks answered NOTHING (a refusal) context recall@5 0.846 -> 0.891, @10 0.881 -> 0.933, MRR 0.703 -> 0.744

Every verb, base (#1885) vs this branch

Sixteen repositories (ten tuning, six held-out), mutation / runtime-trace / issue-fix oracles.

verb metric tuning held-out all
impact --tests recall 0.884 -> 0.884 0.713 -> 0.826 0.786 -> 0.851
precision 0.420 -> 0.420 0.442 -> 0.414 0.431 -> 0.417
empty answers 51 -> 50 45 -> 30 96 -> 80
tests (the verb, on the mutant edit itself) recall (all / first tier) 0.884 / 0.634 -> 0.884 / 0.639 0.713 / 0.510 -> 0.826 / 0.590 0.786 / 0.563 -> 0.851 / 0.611
impact (one-hop callers) src recall 0.726 -> 0.736 0.656 -> 0.656 0.707 -> 0.714
precision (ran) 0.952 -> 0.950 0.930 -> 0.925 0.946 -> 0.943
impact (transitive reach, <= 3 runtime hops) src / test recall 0.790 / 0.858 -> 0.798 / 0.860 0.515 / 0.800 -> 0.517 / 0.802 0.733 / 0.846 -> 0.740 / 0.848
precision (ran) 0.660 -> 0.657 0.720 -> 0.764 0.668 -> 0.675
path (src pairs, both argument orders) found 0.784 -> 0.784 0.621 -> 0.628 0.724 -> 0.727
path (failing test -> change) found 0.591 -> 0.599 0.420 -> 0.490 0.528 -> 0.559
context recall@5 / MRR 0.799 / 0.632 -> 0.837 / 0.678 0.894 / 0.776 -> 0.946 / 0.812 0.846 / 0.703 -> 0.891 / 0.744

No repository lost a failing test file, a path pair or a context gold file. Index time flat warm (libsum recomputes once when its own source changes); edges +0.8%.

Validation

  • tests/run.py every language: 1584 of 1584 checks (316 cases), with AXIOMCODE_ENGINE set to this checkout.
  • Python engine suite incl. torture: 43 passed, 0 failed; goldens unchanged.
  • literal gate ok. No parser change.

Open

  • held-out spawns that import a module chosen by a parameter (one repository, ~370 files), a library-typed self parameter whose library method calls back client overrides (one repository), a converter dispatch table (one repository): single-repository shapes, not tuned on.
  • runtime-only misses (a test file failing without executing the change: shared state, generated code run at import) bound recall at about 1.1%.
  • impact's library-callback callers are listed as framework-mediated direct rows naming the client function that called the library, not the nearest client frame; a one-hop scorer that counts only calls it rows does not see them.

Libraries: --library auto in every language

Libraries stay opt-in: a graph is built without them unless --library is given once, and then every rebuild keeps them.

  • --library auto stages what the project depends on: Python packages its source imports (virtual environment), JavaScript / TypeScript packages it imports (node_modules), Java dependencies pom.xml / build.gradle declare (their -sources.jar, or the class jar decompiled with Vineflower), and NuGet packages a .csproj references (decompiled with ilspycmd). What it cannot find is named with the command that fetches it.
  • Compiled once: every library (discovered or named as a source directory) is parsed once into ~/.cache/axiomcode/libir/ and reused; before this a named source directory was re-parsed on every refresh after an edit. A library unused for 30 days is pruned.
  • Dependency changes rebuild the graph: with libraries on, a manifest or lockfile edit, pip install -U, npm install, dotnet restore, or fetching a Java dependency that was missing (mvn dependency:resolve) marks the graph stale like a source edit does.
  • path names a TypeScript library method by its member (index#Greeting.shout was not found as Greeting.shout).
  • The skill explains when and how to compile and enable libraries, what they buy, and how to turn them off.
  • CI installs both decompilers for the Java and C# legs.

Validated with a case per language (each fails without the change): Python, TypeScript, JavaScript, Java (sources jar, decompiled class jar, absent dependency) and C# (decompiled package, unrestored package). Query cases: python 386, typescript 299, javascript 321, java 346, csharp 243; front door 21/21. Measured by hand: a dependency upgrade with no project edit triggers one refresh and the graph is fresh after it; an in-use library is never pruned.

Closes #1887.
Closes #1889.

swapnilpaliwal-sd and others added 4 commits October 9, 2026 19:18
`a == b`, `a | b`, `x in c`, `-a`, `if x:`, `not x`, `x or y` compile to no call, so the
client __eq__, __or__, __contains__, __bool__ or __len__ they run had no caller: impact listed
nothing that compares, composes or truth-tests the object, and a test doing exactly that was
not selected. Two new protocol edges on the existing channel, each its own kind:

  OPERATOR_PROTOCOL  the left operand's slot and the right operand's reflected one (a
                     comparison's mirror, `a < b` -> b.__gt__), `!=` falling back to __eq__,
                     `in` running the right operand's __contains__ (or __iter__), and the
                     unary -, +, ~. A chained comparison matches no catalogue row and emits
                     nothing.
  TRUTH_PROTOCOL     if / while / assert conditions, a conditional or comprehension
                     condition, `not x`, and the operands of `and`/`or` -- the right one only
                     when the whole expression is tested (`return a or b` hands b back
                     untested). __bool__, or __len__ when the class has none.

Operators and dunders are catalogued in builtins.dl (the data model's tables).

Mutation oracle, sixteen repositories: +5 failing test files selected (one tuning, four
held-out), 9 extra, none lost; empty answers 96 -> 92; impact's one-hop callers src recall
0.707 -> 0.714 (tuning 0.726 -> 0.736); a failing test's path to the change found 0.528 ->
0.541. Edges +0.8%, index time flat. Case operator-runs-the-dunder fails 6 of 8 on the base;
controls: a class with its own __bool__ is never tested through __len__, and the untested
right operand of `or` reaches nothing.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…xport or a generic alias is that class

A client class whose base is a library class was recognised only when the base was a bare
name or a subscript. Three spellings of the same base left it a plain unresolved class, so the
library's own methods never called back the overrides it defines:

  * a DOTTED base, `class Command(clikit.core.Command)` / `class Store(abc.Mapping)`: the
    base list's DOTTED_NAME slot is read like NAME (138 such classes across the sixteen
    repositories, every one of them);
  * a name a library re-exports with `from M import *` (`collections.abc.MutableMapping` is
    `_collections_abc.MutableMapping`): libsum now loads every module first and expands star
    imports (M's __all__ when it declares one, else its public names) before analysing any
    body, so the alias table has the row;
  * a module-level generic alias built around a class, `Mapping = _alias(collections.abc.
    Mapping, 2)`: followed only when the first argument resolves to a library CLASS, so a
    `partial(f, x)` wrapper around a function is not taken for f.

Mutation oracle, sixteen repositories: tests recall 0.786 -> 0.851 (tuning 0.884 flat, +1
file; held-out 0.713 -> 0.826, +536 files on a CLI subject whose commands subclass the
library's by dotted name), precision 0.431 -> 0.417, empty answers 92 -> 83; a failing
test's path to the change found 0.541 -> 0.559. Two tuning files lost to a by-name match
the new library site suppressed are restored by the next commit. Case library-star-reexport
fails 4 of 5 on the base; control: a library base whose method hands nothing back never
reaches the client's __getitem__.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…s a hand-back from it is summarised

A call the engine knows enters a summarised library callable (ext_lib_callback_site) was made a
library receiver outright, which drops its by-name callers. Where the summary does say what the
library hands back from that call (a lib_callback_edge from the same caller through the same
callable), that is right: the hand-back is the answer and a project method merely named like
the call is not. Where it says nothing -- the library reaches the project through a method's
return, a registry, anything the summary cannot follow -- knowing the call enters a library is
no evidence that nothing in the project runs behind it, and the name match it had before the
library was read is kept.

Found when naming more library bases (previous commit) turned a test client's
`super().__init__(...)` into such a site and two failing test files lost the only route the
graph had to them. Mutation oracle, sixteen repositories: both restored, +17 extra files, 0
lost; empty answers 83 -> 80. IMPACT_VERSION 69 and path EXPORT_VERSION 5, since the staged
facts change. Case library-site-without-summary: the base fails the name-match check; control:
a call whose hand-back is summarised still reaches no project method merely named like it.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
… from the body

A scope-less context call ranks directories by where the HEADLINE's words land, and refuses
with a menu when they land nowhere. A report titled in prose ("Release 8.4.2", "Disable when
developing?", "Make unimported utilities private") lands nowhere by its headline while its body
names the code outright (`@retry(wait=wait_fixed(1))`, `LazyFile`, `make_short_help`), and the
refusal answered NOTHING. The whole report's words now rank the directories when the headline's
have no evidence; the pick stays an offered scope (it neither filters nor weights), and a report
whose words land nowhere at all still refuses.

Issue-to-fix oracle (92 tasks, sixteen repositories): empty answers 6 -> 0 (three repositories),
recall@5 0.846 -> 0.891, recall@10 0.881 -> 0.933, MRR 0.703 -> 0.744; tuning 0.799 -> 0.837
recall@5, held-out 0.894 -> 0.946. No task lost a gold file. Case context-body-names-the-code:
the base refuses ("--in <path> is required"); control: a headline that lands still chooses the
place by itself.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
@swapnilpaliwal-sd
swapnilpaliwal-sd changed the base branch from fix/py-recall-combined to apps/integration-0.1.9 October 10, 2026 03:48
@swapnilpaliwal-sd
swapnilpaliwal-sd marked this pull request as ready for review October 10, 2026 03:49
swapnilpaliwal-sd and others added 11 commits October 9, 2026 20:54
…ated, and what staging buys

Closes #1887.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…es), made once per dependency

The entry the skill led with was a dependency's source directory, which index re-parses; the reusable
form is the CSV IR `axiomcode parser <source> <ir dir> --library` writes. Lead with that, keep the
source directory as the accepted fallback.

Refs #1887.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…comma, unresolved vs external

Every claim in the --library section was exercised: two IR entries stage two roots; a space after a
comma is refused as "not a directory"; a relative entry resolves against the repository; a source
directory is parsed under .axiomcode/out/.intermediate/lib/; AXIOMCODE_LIBRARY works the same; a bare
index keeps the library graph. A `~` after a comma is refused (a leading one is the shell's), and
without libraries a dependency call is unresolved or an external boundary, not always unknown.

Refs #1887.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…orts; a source entry is compiled once

Libraries are opt-in: the graph is built without them unless --library is given. `--library auto`
finds the packages the project's source imports, in its virtual environment or under node_modules,
compiles each once into ~/.cache/axiomcode/libir with `bin/axiomcode parser --library`, and stages the
compiled tables. A named source directory goes through the same cache, so a rebuild the refresher starts
after an edit no longer parses it again (a large dependency was re-parsed on every edit). A library IR
entry passes through unchanged.

The skill says when to do this: for better coverage, compile the dependencies and enable them.

Refs #1887.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…ases for `--library auto` in JS and TS

TypeScript names a library method module#Member (`index#Greeting.shout`); the library endpoint lookup
matched only a `.`-separated tail, so `path welcome Greeting.shout` said nothing of that name was
declared although the call resolved into the library. Cases cover discovery from node_modules and a
call typed through the package in both languages; each fails without the build change.

Refs #1887.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…ough their sources jars

auto reads the dependencies pom.xml (a ${property} version resolved) and build.gradle declare, finds
each one's -sources.jar in the Maven repository (.mvn/maven.config's maven.repo.local, else ~/.m2) or
Gradle's module cache, unpacks it once and compiles it like any source library. A dependency with no
sources jar is named, with the command that fetches it.

Refs #1887.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…by decompiling them

NuGet packages ship assemblies, not source. auto reads the PackageReferences each .csproj declares
(a version Directory.Packages.props sets centrally too), finds the package in the folder NUGET_PACKAGES,
nuget.config's globalPackagesFolder or ~/.nuget/packages names, decompiles its assembly once with
ilspycmd and compiles the C# like any source library. A package never restored is named, and a
missing ilspycmd is reported with the command that installs it; CI installs it for the C# leg.

With this, auto covers all five languages, and the skill says so.

Closes #1887.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
… no sources jar

A dependency with only a class jar was reported and left out. It is now decompiled with Vineflower
(found in the Maven repository, or AXIOMCODE_JAVA_DECOMPILER) once into the cache and compiled like a
source library; with no decompiler the dependency is named with the commands that fetch sources or the
decompiler. CI fetches Vineflower for the Java leg.

Refs #1887.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…y changes; old compiled libraries are pruned

Only source files were compared, so a dependency added, removed or upgraded without a source edit
(`pip install -U`, `npm install`, `dotnet restore`, a manifest or lockfile edit) left the graph on the
libraries it was built with. The file table of a graph built with --library now also records the
manifests and lockfiles and what is installed (a virtual environment's dist-info names, npm's install
record, .NET's restore output); a difference marks the graph stale and the usual background rebuild
runs, which re-discovers `auto` and compiles only what changed. A graph built without libraries is
unaffected and never gains them by itself.

A compiled library no build has used for 30 days is removed from the cache, with what was unpacked or
decompiled for it; one in use is never removed.

The skill states both, and how to turn libraries off again.

Refs #1887.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…brary graph stale

Fetching a missing Java dependency (`mvn dependency:resolve`, a Gradle build) writes only to ~/.m2 or
Gradle's cache, which no file in the project records, so the graph stayed without that library until
the next source or pom edit. The dependency fingerprint now includes which declared dependencies are
present in the Maven repository or Gradle's cache; one appearing marks the graph stale, and the
refresh stages it.

Refs #1887.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
@swapnilpaliwal-sd swapnilpaliwal-sd changed the title Python recall round 3: operator/truth protocol, library bases by dotted name / star re-export, context scope from the body Python recall (operators, library bases, context) and --library auto for every language Oct 10, 2026
@swapnilpaliwal-sd
swapnilpaliwal-sd merged commit 473bbb3 into apps/integration-0.1.9 Oct 10, 2026
11 checks passed
@swapnilpaliwal-sd
swapnilpaliwal-sd deleted the fix/py-recall-3 branch October 10, 2026 05:39
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