Skip to content

0.1.7 Release - #1764

Merged
swapnilpaliwal-sd merged 67 commits into
mainfrom
dev
Sep 29, 2026
Merged

swapnilpaliwal-sd merged 67 commits into
mainfrom
dev

Conversation

@swapnilpaliwal-sd

Copy link
Copy Markdown
Contributor

Improvements

Bugs

swapnilpaliwal-sd and others added 30 commits September 28, 2026 13:36
… function stored on this, in a module variable, in a tested options member, or through an expression (#1675)

unresolved_value_callee gains five reasons, each an unresolved call whose
callee is a value the graph does not hold:
- module_variable: `let handler = null; ... handler()`
- field: `this.cb()` where cb is a field, not a method
- parameter_member: `opts.onReady()` when the code treats the member as an
  optional function (typeof test, `&&`/`||`/`??` guard, if condition, `?.()`)
- expression: `(c ? a : b)()`, `(0, cb)()`, `make()()`
- dynamic_code: `new Function(s)()`
A bare member call on an untyped parameter (`res.send()`, `s.trim()`) is
not counted: without a type it reads the same as a library method, and
counting it turned 10 of 12 independent pairs on a real Express app into
unknown. No call edge changes.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…iased this field, a fallback field and new this.Ctor() (#1675)

The field reason of unresolved_value_callee now covers:
- a field holding only null until a setter runs (`cb = null`, `this.cb = null`,
  `static cb = null`): the null literal no longer counts as the known value
- a static `this.cb = f` / `this.cb()` pair, known by the assignment, not a field declaration
- `self.cb()` with `const self = this`: any receiver valued as the instance or class
- `new this.Ctor()`
- `this.cb = cb || function () {}`: the call resolves to the fallback, but the
  parameter it falls back from is a function no caller in the graph supplies

A module variable initialised from a call and never reassigned
(`const log = require('debug')('app')`) is no longer a value callee: the
library made it. One handed a function (`memo(Inner)`) stays open.
`Model.tag2()` (a known-missing member of case 49) now names the field.
No call edge changes.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
A word the code only writes (a local `const labelOf = new Map()`, a parameter,
`process.env.Path`, `HttpStatus.OK`, an object key `{ Bucket: … }`) was
answered as "type X (not declared here …)", its mentions counted as exact
callers with a passing verified: line. The undeclared-type fallback now needs
evidence the name is a type: `new X`, a library method of X, a type position,
a reference bound to a type, or an unbound, capitalised, unqualified name
(`Path.Combine(…)`). References the parser binds to a value are dropped.
Without that evidence the answer says nothing is declared and lists the
methods that write the word as [by name] leads, so a multi-graph answer no
longer carries a phantom section beside the graph that does declare it.

Closes #1602

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

The resolver rewrote '#' to '.' for everything but methods, so Vault.#secret
became Vault..secret and #secret became secret: no spelling reached the field.
A '#' name is now looked up verbatim as a field before the rewrite.

JavaScript class fields were also indexed with no owner, so even a public
field's readers were only 'alongside' rows; the field now takes its class from
ownerTypeLinkHash (and its own id), as TypeScript's already do.

Golden cases for JS and TS, with controls: a private method, a public field,
and a same-named private field in another class.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…pact --in answers for the declarations under it

A scope matched any file whose path contained it, so --in tests also took
parser/src/test/*-tests.ts, and impact <name> --in <dir> answered for every
declaration of the name, in every language graph, all outside the scope.
A scope that names a path of the repository now matches by prefix (one that
names no path still matches anywhere); impact narrows its targets to the
declarations under the scope, and the fan-out drops a graph whose
declarations all lie outside it when another graph has one inside.

Closes #1584

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
… keeps its own version)

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…er, so a fixture's Array no longer answers production calls (#1574)

A test fixture that is its own program (its own tsconfig, often noLib with a
hand-written interface Array) declared globals the engine treated as
repository-wide, and the parser keys a declaration group by name, so the
fixture's Array also merged into every other program's Array. A production
xs.map(f) resolved to the fixture's map.

- A client global is visible to a module its tsconfig governs or that lies in
  that tsconfig's directory tree; library globals and globals with no governing
  tsconfig stay repository-wide.
- A declaration joins another's merged entity only when its program sees that
  declaration's file.
- Readers with no module in hand (Object/Function prototype members, typeof a
  global, a namespace callee) read the unscoped globals plus those of a program
  that covers every module.

New golden case 79 (nested fixture program) with a control: the fixture's own
calls keep their edges.

Closes #1574

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
context/impact: --in matches by path prefix and scopes impact's declarations
impact: resolve a private class field (Owner.#name) in JS and TS
javascript: path says unknown, not independent, for a callee stored on this, in a module variable, in a tested options member, or computed by an expression (#1675)
path/impact: a name no declaration carries is not a type (#1602)
typescript: a global declared in one program is not in scope in another (#1574)
Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Merge dev (the 0.1.6 release) into 0.1.7
…ten function

A call through a wrapper's closure took its value from the site's argument
alone, even when the wrapper reassigned that parameter first. The wrapper's
own writes to the parameter now feed the per-site answer: the written
function's result always, the site's argument unless a plain, unbranched
write that does not read the parameter replaced it. Writes inside the closure
or a nested function do not count.

Closes #1706

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

A graph.sqlite that is not a readable database (a disk that filled, a copy cut
short) made every verb, from the shell and through the MCP server, die in a
Python sqlite3 traceback. Each verb's door (axiomcode-path G, axiomcode-graph)
now probes the graph (schema read, quick_check only when that fails or when an
sqlite error escapes mid-answer), says "graph is corrupt", moves it aside with
a marker that stops the build calling unchanged files up to date, rebuilds it,
and answers from the last good (baseline) graph meanwhile. Another language's
corrupt graph is named and left out; the main language still answers.

tests/corrupt_graph.py: garbage, truncated, bad page, marker, MCP and
other-language cases, with controls (sound graph, empty graph, up to date).

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…port let a setter reassigns (#1708)

An ES import is a live binding. A named import or a namespace member call
on a module that exports a let and reassigns it in a setter runs whatever
the setter stored. The engine now marks such an unresolved call as a value
callee (imported_variable) through a named import, a namespace, an export
specifier, a renaming re-export and export star, so path answers unknown
and names the site instead of independent. An export the module never
reassigns, and an exported const that resolves, keep their answers.
No call edge changes.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
javascript: a wrapper that writes its parameter answers with the written function
plugin: a corrupt graph.sqlite is said, moved aside and rebuilt, never a traceback
…sts remote hops

TypeScript had no framework-behavior phase, so no TS bundle held an
ext_remote_edge row and impact/path stopped at the send. destinations.dl
joins an HTTP client (fetch, an axios-style client or its create()d
instance) to the route that serves it (router calls, @Controller/@get),
reading the path from a literal, a template, a constant (local or
imported) or a const config object. Verb must agree; the route with the
fewest differing segments wins. Unjoined halves are reported as
remote_unserved / remote_unsent / remote_undetermined. Framework names
live in config-resolution/knobs.dl.

context now prints ext_remote_edge rows as their own tier (transport,
destination, confidence), never merged into call hops. impact's test
closure walks a remote hop from sender to handler on its own `remote`
rung, so test-impact selects the test that drives the client.

Part of #1293

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
typescript: cross-process remote_edge for HTTP routes; context lists remote hops
javascript: path says unknown when a call goes through an imported export let a setter reassigns (#1708)
Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…goldens

tests: TS cases 68 and 69 expect #1710's remote_unsent section
… and reuse it

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

What was wrong

1. test-impact printed `pytest <files>` for every Python selection. A Django
   project with no pytest in its requirements, config or CI runs
   `python manage.py test <dotted labels>`, and a unittest project runs
   `python -m unittest`; the printed command and the `next:` line built from
   it did not run there. The edit hook's body line reuses the same command.
2. When a changed file outside the index (a fixture) was named by test files
   in their text, the runner line was chosen by the majority extension of
   those files and listed all of them. A Python answer could print
   `pytest` over .cs, .java, .ts and .js fixture files that merely contain
   path words, and a Java answer could add a .py basename to -Dtest=.
3. The by-name tier treated __init__.py as a module name.
4. `impact` and `test-impact` answered "tests: 0" when the graph was built
   with --src and the tests live in a sibling tree, without saying the
   test tree was never read.

The change

- command_for hands each runner only files of its own language.
- A Python selection is grouped by the nearest project directory and run
  with that project's runner, read in the order a project states it:
  a pytest config section, pytest.ini or a conftest.py; the command its
  CI workflow, tox, nox or Makefile runs; a declared pytest dependency;
  the test file's own pytest import; a Django manage.py (dotted labels, run
  from the manage.py directory); TestCase modules with nothing else
  (`python -m unittest`). With nothing to go on it stays pytest. A
  manage.py that is not Django's (a Flask CLI) does not count.
- A selected Python file that declares no test is not handed to a
  runner: one that runs as a program (a __main__ guard, a shebang, a
  top-level sys.exit) gets `python <file>`, and a fixture or helper
  module under a test tree is named apart instead of being put on a
  pytest line that would collect nothing from it.
- The text tier reads only the graph's language's files, before choosing
  the most specific needle, and prefers a test module the runner collects
  over a helper or fixture beside it. Test files of another language that
  name the file are counted in one line, not listed: that language's graph
  answers for them. No cross-language link is made.
- The by-name tier no longer matches a stem every package repeats
  (__init__, __main__, conftest): on a --src graph it listed 15
  __init__.py files as the tests "named after" an edited __init__.py.
- `next:` recognises `python manage.py test` and `python -m unittest`.
- impact prints a NOT SEEN line (and --json `tests_outside_src`) when no
  test is counted and test files of the graph's language lie outside the
  --src it was indexed with; test-impact repeats it under "no test".

Tests

- tests/test_command.py: Django, unittest and pytest runner cases, each
  with a near miss (pytest-django dependency, a [tool.pytest.ini_options]
  section, a conftest.py, a Flask manage.py, a test importing pytest, a
  bare def test_), a script test and a fixture module, __init__ and
  __main__ stems (control: pricing.py still names test_pricing.py), language
  filtering for Python, Java and C#, and the next: line. Each positive
  check fails on the old code; the near misses pass on both.
- tests/changed_range.py: the text tier's command holds only the
  graph's language's collected test modules, with a Java and a C# file
  and a Python helper naming the same fixture.
- tests/cases/python/tests-outside-src: --src app with tests/ outside
  it says NOT SEEN; the near miss (a test inside --src) is counted and
  says nothing. 2 of 3 checks fail on the old code, the control passes.
- Suites: tests/test_command.py 107 ok; tests/fastpath.py 4 of 4;
  tests/changed_range.py all ok (5 of its checks fail on the old code);
  tests/run.py --lang python 149 of 154, the 5 failures all in
  lambda-is-named-by-its-place, a TypeError from the duplicate
  G.enclosing already on the release branch (fixed separately), not
  touched here; tests/run.py abstract-test-base-command 2 of 2;
  tests/enrich_lines.py 36 of 36; tests/hook_languages.py 7 of 7.

Smoke (before is the installed build, after is this change, same graph)

- A Django project (requirements and CI run manage.py test, no
  pytest), body edit of one model method: 126 tests in 23 files both
  times. Before: `pytest <app>/tests/test_x.py ...`; after:
  `python manage.py test <app>.tests.test_x ...`.
- A library whose tox runs `-m unittest discover`, body edit of
  one helper: 102 tests in 7 files both times. Before: `pytest tests/...`;
  after: `python -m unittest tests.test_x ...`, and that command
  resolved all 7 modules and ran 209 tests (104 errored on this machine's
  missing optional dependencies, the same on the unedited tree).
- A Flask project (pytest in its dependencies, a manage.py that is a
  Flask CLI), body edit: 683 tests in 96 files, and the pytest line is
  byte-identical before and after (the control).
- The same project indexed with --src on its package dir, impact of the
  same function: before "tests: 0 of 3" and nothing else; after the same
  count plus "NOT SEEN: ... 104 test file(s) lie outside it". The whole
  repository indexed finds 683 tests for that edit.
- This repository's own tests (run as scripts), test-impact on this
  change: before a pytest line over script tests, fixture modules and
  32 files of other languages; after `pytest` for the pytest modules and
  `python tests/<x>.py` for each script, 3 fixture modules named apart,
  and "32 test file(s) in another language name them too".
- Java and C#: tests/run.py abstract-test-base-command (mvn -Dtest line)
  and tests/hook_languages.py (dotnet test --filter line) pass unchanged.

Not yet measured on the full corpus or on held-out projects; the smoke numbers above are the only corpus numbers.
…y their key, class expressions named by their binding

The JavaScript index adapter dropped a field's owner (`impact Store.items`
answered `field items`), left a computed-key member with an empty name
(`Counter.`), and displayed an anonymous class expression as
`<anonymous-class>` with its variable as a second declaration. Fields now
take their class by ownerTypeLinkHash, computed keys are named
`[<key as written>]` (and impact keeps them one segment), and a class
expression takes the variable, field or property it is bound to, so a
nested member reads `Outer.Inner.ping`. INDEX_VERSION 5 re-indexes older
graphs.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…argument of its own reads the parameter (#695)

The per-site answer for memoize/once/lazy wrappers dropped every value of the
closure's carrying return, not only the parameter's call. `fn() || fallback`,
`override || (cached = make())` and `let result = defaults; result = fn() || result`
lost the fallback, the override and the default. Now only the parameter's call is
answered per site; every other operand keeps its ordinary value (an `undefined`
cache placeholder aside).

Sites that do not pass the parameter themselves fall back instead of answering
nothing: a missing argument reads the parameter's default, a spread argument reads
the merged parameter as before, a wrapped getter passed to a wrapper
(`memoize(memoize(f))`, `once(getAlpha)`) answers what a call through the inner
site returns, and the closure called inside its own wrapper reads that
invocation's parameter.

Case 64 gains these shapes; its first getters and controls are unchanged.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…answers per site (#695)

`function once(fn) { let r; function inner() { if (!r) r = fn(); return r; } return inner; }`
returns its closure by name, and a named function declaration was not recognised as
the wrapper's closure (only a returned function expression or a const bound to one).
So `getter().method()` read the merged parameter: a superset, or nothing once the
wrapper was over the fan cap. A returned binding of a function declared inside the
wrapper is now its closure too.

Case 64 gains declared.js: two instance getters and a wrapped memoized getter over a
27-caller declared wrapper, each reaching its own module; controls (a declared closure
that returns something else, a function declared outside the wrapper) are unchanged.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…on the declared-closure fixture

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
swapnilpaliwal-sd and others added 26 commits September 28, 2026 16:58
…swers unknown

A method calling this.hook() where neither the class, an ancestor nor any
write the graph follows gives the instance a hook runs a function stored
from outside (e.hook = fn, Object.assign(this, opts)). It is now an
unresolved value callee (instance_member), so path says unknown and names
the site instead of independent. Classes with an unresolved or dynamic base
are left out, as are ambient members (hasOwnProperty).

Closes #1709

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
impact, path, context: when the graph has no answer, give the text matches, labelled [text]
…-mislabelled

impact: label each link of a chained route registration with its own verb and path
…ed-key

javascript: a call through an object-literal table with a computed key reaches the table's functions
javascript: this.x() with no declared x answers unknown in path
A call through an object literal resolved only when the receiver was the
bare const in its own file. An imported const (`api.fetch()`), a nested
literal (`api.users.list()`) and a factory returning a literal
(`makeStore().inc()`) all stayed unresolved, for methods and for arrow- or
function-valued properties alike.

expr_objlit follows each of those roads to the literal the receiver
evaluates to; an annotated variable or return type is left to the type it
names. Case 80 covers the shapes, with annotated and non-literal controls.

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

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

`xs.forEach(this.handle, this)`, `el.addEventListener('click', this.onClick)` and
`setTimeout(this.onHover.bind(this))` hand an instance method over without calling it, and the
passer had no row for it, where a free function passed at the same position had one.
method_value reads the member off the receiver's type (or shape) the way an instance call does,
sees through `.bind`, and leaves getters, setters, data fields and `.call`/`.apply` alone.

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

`impact --page 2` was rated not useful in 10 of 11 recorded calls:

- Page 2 was page 1's view cut further along. The default view caps the
  users at 25 rows and 5 per file, and collapses a [by name] flood, so
  page 2 printed the few rows past page 1's budget and then the same
  "... +54 more" and "+9 [by name]" summaries. The rows those summaries
  stood for were on no page, and `--page all` still hid 63 of them.
- A one-page answer asked for page 2 printed page 1 again, with no word
  that there was no page 2.
- A section continued on a later page repeated the whole answer's counts
  in its heading, so the numbers did not match what the page showed. The
  entry-point rows were moved under a "low-certainty rows" heading and
  left their own sub-heading behind, empty.
- The collapsed [by name] rows came with a search for the target's own
  method name, which for a type or a property is a compiler-made name:
  grep "<init>", "<classbody>", "<lambda>".
- Through MCP the footer read "page=3 (MCP) or page=3 (CLI), or all of it
  with page all", and `page` took only an integer, so page="all" could
  not be sent.

The change:

- ax_pages: page 1 is the default view, unchanged. Pages 2 and on hold
  every row page 1 did not print, in the order of the full answer, with
  no summary lines. impact marks the rows its default view hides and the
  lines that summarise them, so the pager builds both views from one run.
  `--page all` prints every row.
- A page past the end of a one-page answer says "page 2 does not exist:
  this answer has 1 page".
- A continued section's heading counts the rows on that page, by rung;
  entry-point rows keep their sub-heading and are headed as entry points.
  A section with no rows on a later page is not printed.
- The [by name] search names what the collapsed rows call, or the
  target's own name (the type, the field, the method); never a name
  starting with "<" or a dunder.
- MCP: `page` takes a number or "all"; `--page all` is written page="all"
  and the footer names one spelling per surface. The SDK-free server
  advertises the union as anyOf. The impact description says tests=True
  is the CLI's --tests-only.
- axiomcode-path: two methods named G.enclosing (one per lambda, one per
  file and line) were merged into one class, and the later one replaced
  the earlier, so every lambda lookup raised a TypeError (test-impact on
  named files, the lambda case). The file-and-line one is enclosing_at.

Tests: a Python case with a near-miss control for each behaviour (page 2
lists the three hidden callers and no summary; a one-page answer has no
page 2, and without --page is printed whole; a type's search names the
type, a method's keeps `.value(`), the Java paging case's footer, and
tests/mcp.py for page="all", page=1 and the footer wording.
The enclosing fix turns 5 of lambda-is-named-by-its-place's 7 failures on
the base green; its other 2 (a Python control, a C# field initializer)
and member-owner-is-its-type's stale pending marker fail on the base too,
with the same output, and are not touched here.

Smoke, same graphs, installed build vs this change:
- Java REST app, impact on an entity type with 138 users, page 2:
  9 new rows and 2 summary lines before; 15 new rows and none after,
  headed "15 row(s): 15 resolved".
  Pages 1 to 7 together print 120 of the 120 rows, none twice.
- One-page answers asked for page 2 (one Java, one Python, one C#
  target): page 1 repeated byte for byte before, 3 of 3; "does not
  exist" after, 3 of 3, and page 1 unchanged.
- Type targets: the search was grep "<classbody>" (Python) and a
  compiler-made name (C#); now grep -rnw on the type's own name, both.
  A C# property: the names of unrelated methods before, the property's
  own name after; a method keeps its `.name(` search.

Not yet measured on the full corpus or on held-out projects; the smoke
numbers above are the only corpus numbers.
… Record/index-signature elements

A call's result was untyped when its type came from:
- a function-typed parameter with its own type parameter, bound from the
  argument: `pick: <T>(x: T) => T; pick(u).save()`. Function-type parameters
  never linked their declared type, so T had nothing to bind from;
- a signature's own `<T>` (function type, method/call/construct signature in a
  type literal), which was not in scope for its return, so `T` referenced nothing;
- a `Record<K, V>` or inline `{ [k: string]: V }` read by key, including a value
  that is itself a function type: `factories[kind](n).area()`.

Case 80 covers each shape plus controls that must stay unresolved
(`get<any>()`, an index signature over `any`).

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

A method returning `new this.constructor(...)`, directly or through a const it
initialises, was read as one merged value: every instantiated subclass of the
declaring class, so each call on a clone fanned to all their overrides. Like a
`this`-return, it is now substituted per site from the call's receiver. A `let`
local and `new this()` in a static keep their merged value.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…s length (#1601)

Line 0, or a line past the last, spans no callable and fell through to the
module node, so it was answered as the file's top-level code as if that line
existed. Both verbs resolve file:line in G._resolve; it now refuses a line
below 1 and, reading the file on disk, a line past its end.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…ler reports

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

A same-named file under a test directory is not a test: fixture inputs
(test/fixtures/*/index.js), another language's test and the changed file
itself were offered as the tests of src/index.js, with a runner command.
A by-name candidate is now a test only by its ecosystem's runner default
(x.test.ts, test_x.py, XTest.java, XTests.cs, or directly in a mocha
test/ or __tests__/ dir) in the changed file's language family; any other
same-named file is counted on a 'not a test' line with no command.

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

typescript: type receivers from a generic signature's T and from Record/index-signature elements
… its own command

A test-tree file run as a program with no framework (a runner calling runCase at
top level, a tests/check.py with a main guard) was walked to and labelled [test],
then counted "0 of 0", and test-impact said no test reaches the change. Its module
is now in test_method, from one rule (graph_sql.script_tests) that the exporter and
the hooks' fast path both read, so impact counts it and test-impact selects it.
The command is the one the project runs it by: its package script, its usage line,
else the interpreter; never a guessed framework. Helpers that export, setup and
config files, and test data (cases/, fixture projects under src/, tools/) are not
scripts. The impact facts version moves to 40 so a cached export is rebuilt.

Also: axiomcode-path declared G.enclosing twice, the second shadowing the first,
so `changed <file>` without git crashed on a top-level lambda. The file:line form
is now G.enclosing_at.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…ds its own entry

An unguarded script test that no package script or usage line names was handed
to node or npx tsx, which undid #1570 (a file nothing runs is said so). It still
counts and is selected; only a file with require.main === module, import.meta.main
or a __main__ guard gets its interpreter as the command.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
impact, test-impact: a script-style test counts as a test and runs by its own command
…-callback

typescript: a method passed by reference registers with the function that passes it
…embers

typescript: resolve calls on members of an unannotated object literal
javascript: new this.constructor() builds the receiver's class at each site
path / impact: a file:line outside the file is refused with the file's length
…matches

A file:line whose partial path (a basename, or dir/file) matches several
indexed files was refused before --in was applied, so the scope could not
pick among them. G.line_files now narrows the matches by --in: one file
under the scope is the answer, labelled with its full path; two or more are
the refusal's list; none keeps the whole list and says so.

impact's field lookup for file:line matched the line in every file the path
ends, so test.js:6 among 17 test.js answered for one file's const; it now
resolves the file the same way and refuses when the path is ambiguous.

Closes #1666

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
impact / path: --in chooses among the files a partial file:line path matches
test-impact: by-name fallback offers only real tests of the changed file's language
@swapnilpaliwal-sd
swapnilpaliwal-sd merged commit a4c2e70 into main Sep 29, 2026
24 checks passed
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