Repository navigation
fix(impact): Python transitive reach — two over-reach sources, Optional class-object params - #1891
Open
swapnilpaliwal-sd wants to merge 3 commits into
Open
swapnilpaliwal-sd wants to merge 3 commits into
swapnilpaliwal-sd wants to merge 3 commits into
Conversation
Measured against runtime traces on the Python oracle corpus, every false
transitive-reach row has a static edge the runtime never made (the scorer
accepts any runtime caller at any depth, so context-insensitivity cannot
produce one). Two of the edge kinds are wrong by construction and are
fixed here; reach precision 0.675 -> 0.724, reach recall -0.1pt (src) /
-0.2pt (test-side), test-file recall -0.1pt, callers and path unchanged.
1. A decoration string naming a PARAMETER of the decorated declaration is
no registration key (ax_registration.decoration_key_strings, shape 4),
nor is a string inside another call in the decoration (shape 5).
`@option("--params", "-p", "params") def main(params)`: the flags are
what a caller writes to reach the command; the parameter name is written
by every function that builds a dict with that key, and the by-key join
made each of them a caller of the command. The flags stay keys.
2. The "decorator by name" hop lands on what the decorator RETURNS, not on
the decorator's body. It used to run decorated -> decorator, making the
decorated function (and everything reaching it: a route's tests) a
caller of whatever the decorator calls. Calling the decorated name never
runs the decorator; it runs what the decorator returned:
- bare `@d`: d's return; written as a call `@d(...)`: the return of
what d returned (decorated_call);
- a wrapper (fn_returns, from the engine's method_returns_method): the
decorated declaration now stands for that wrapper;
- the function handed back unchanged (fn_returns_param, a registering
decorator): no hop at all; the decoration itself runs at import,
which the import walk already follows;
- unknown return: the old hop stays (a route the walk cannot judge).
IMPACT_VERSION 70 (new facts fn_returns, fn_returns_param, decorated_call).
Cases: decorator-by-name-runs-its-wrapper, option-parameter-name-is-no-key
(each fails on the base, with a control that must not change);
decorator-the-project-declares now expects the route it really takes
(`at import`) instead of the by-name hop.
Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
… its class `cls: type[X] | None = None` and `Optional[Type[X]]` are the default-None spelling of a class-object parameter: the body swaps None for a default class and calls `cls(...)`. Only a top-level `type[...]` annotation was read, so the call stayed `callee_is_parameter` and every constructor behind it lost that caller (a decorator factory building its command class in a closure is the common shape). param_class_object_ref now names the `type[...]` subscript an annotation is or one of its union operands is, and both the concrete (expr_type_class_object) and the bounded-TypeVar (param_class_object_bound) rules read it. The existing top-level rules are unchanged. Corpus: one-hop callers src 0.828 -> 0.829, path found 0.727 -> 0.729, test->target path 0.559 -> 0.562; reach and tests unchanged; engine suite and torture unchanged (agree 588 / missing 46 / extra 37); index time flat. Case: optional-class-object-parameter (fails on the base; the bare `type[Command]` control passes on both). Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
swapnilpaliwal-sd
marked this pull request as ready for review
October 10, 2026 06:48
swapnilpaliwal-sd
requested review from
JaredHLZhang,
Whua689 and
suyashpaliwal26
as code owners
October 10, 2026 06:48
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follows #1886 (merged into
apps/integration-0.1.9).Question
Does context-insensitivity explain Python transitive impact's false reach, and what lifts precision without losing recall?
Measurement (16-repo Python oracle corpus, runtime traces + mutation truth)
Every false row was classified by the first static hop the runtime never made (walking from the false node toward the change, through hops the runtime also made). An offline replay of the impact walk from dumped solves reproduces the engine's reach and test sets exactly, so pruning policies could be measured before writing rules.
Transitive reach (52.9k false rows; precision = runtime caller at any depth):
__init__through an unresolvedsuper()into a library base)Context-insensitivity produces no false reach under this metric: a chain of edges the runtime made somewhere is a runtime caller. It matters only for test selection, where it is a minority: of the test files that ran but never ran the change, the hop is a dispatch fan that ran elsewhere 11%, a resolved call that ran elsewhere (branch-dependent, not fixable by context) 12%, a library callback into a CLI runner 24%, by-name 11%.
Changes
type[X] | None/Optional[Type[X]]parameters construct X (concrete and bounded TypeVar).Effect (all 16 repos; base -> this branch)
Tune vs held-out reach precision: 0.657 -> 0.713 / 0.764 -> 0.771. Query latency flat (p50 ~0.45-0.5 s on the two largest repos), index time flat. Case suite 1601/1601, Python engine suite + torture unchanged, literal gate ok.
Measured, not shipped
super()sites no project class can answer as library receivers: precision -> 0.782 but test-side reach recall -1.5pt (coincidental hits: wrong chains that covered true callers the graph misses).F1 0.8 at today's recall needs reach precision ~0.87; test-selection precision is capped near 0.66 by false rows no static rule can see (files that never ran, ran the change without failing, branch-dependent calls).