Skip to content

Release 0.1.5 - #1667

Merged
swapnilpaliwal-sd merged 59 commits into
devfrom
0.1.5
Sep 28, 2026
Merged

swapnilpaliwal-sd merged 59 commits into
devfrom
0.1.5

Conversation

@swapnilpaliwal-sd

Copy link
Copy Markdown
Contributor

Promotes 0.1.5 to dev: 23 fixes merged into 0.1.5.

Work continues on the next patch branch, cut from 0.1.5.

swapnilpaliwal-sd and others added 30 commits September 28, 2026 01:57
… keeps its own version)

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…1537, #1539, #1527, #1528, #1529, #1531, #1512)

Fixture visibility now follows the runner's own rules instead of a name match.

Engine (framework-behavior/dispatch.dl, knobs.dl):
- a fixture belongs to every module it is an attribute of, so a conftest's
  star import makes it that conftest's fixture (#1537)
- a module named in pytest_plugins registers its fixtures for the session (#1537)
- the registered name is the decorator's name= when it gives one (#1539)
- an argument the test's own parametrize supplies directly requests no
  fixture; an indirect one still does (#1529)
- a fixture a class declares serves that class and its subclasses and
  shadows the module's fixture of the name (#1512)

Impact (axiomcode-impact, dl/impact.dl, graph_sql.py):
- a parameter is joined to a fixture through the engine's fixture_injection
  edge where the engine ran, not by name
- a fixture injected by name is no longer credited to every test of its file
  or class (#1527)
- a usefixtures marker on a class, a module's pytestmark and the ini
  usefixtures option reach the tests they apply to (#1528), including a
  fixture from a plugin module or a star-imported fixtures module
- a declaration decorated @fixture is never a test, and conftest.py is
  never collected (#1531)

Cases: graph/test/python/cases/30-fixture-visibility and
tests/cases/python/fixture-reaches-its-requesters, each with near-miss
controls (a same-named fixture in a sibling directory, a def name that is
not the registered name, an unloaded fixtures module, an indirect argument,
a sibling class).
…sters or is injected with them (#1392, #1396, #1414)

A class Spring Boot registers without a stereotype was not a bean, so every
injection of it read unsatisfied and a change to it or to the class that
registers it looked local.

Engine (config-resolution):
- The parser reads META-INF/spring/<service>.imports and META-INF/spring.factories
  (one descriptor per key, Properties syntax). A class named in an imports file or
  under a configuration key of spring.factories is an auto_configuration bean with
  its constructor reached; any other spring.factories key names a registered type
  whose documented callbacks are entry points, not a bean.
- @EnableConfigurationProperties({T.class}) and @ConfigurationPropertiesScan
  register properties beans; @MapperScan (value, basePackages, basePackageClasses,
  Ant patterns, annotationClass, markerInterface) and MyBatis @Mapper register
  mapper proxy beans. MapStruct's @Mapper is excluded by its import.

Query layer (impact):
- registers(t, by, kind): a bean whose definition site is an annotation on another
  type. impact on the type lists that type as "registers it as a bean".
- A type target lists who is injected with a bean one of its own @bean methods
  defines, so a change to a configuration class no longer reads as local.
- Both rules are in impact.dl and in the SQL port; IMPACT_VERSION 30.

Tests: java case 65-boot-bean-registration (near misses: a comment-only entry, an
unrelated properties value, an unregistered properties class, interfaces outside
every scan, MapStruct's @Mapper, a plain interface), plugin case
bean-registered-or-defined-by-a-class (near misses: an unregistered properties
class of the same shape, a non-@bean method returning an injected type).
…ibility

python: a pytest fixture reaches exactly the tests that receive it (#1537)
…tion

java: Spring Boot registrations make beans, and impact names who registers or is injected with them (#1392, #1396, #1414)
…type fits (#1391)

publishEvent(e) ended at the library boundary, so path from a publisher to
its @eventlistener said the two were independent and impact on a listener
said the change was local.

call-edge-generation/event_dispatch.dl adds an event_dispatch call edge from
the publish site to every listener whose declared event type the published
type is assignable to: the exact type, any ancestor of it, and the catch-all
ApplicationEvent and Object listeners. A listener is an @eventlistener or
@TransactionalEventListener method (the classes named on the annotation win,
else its one parameter) or the onApplicationEvent override of an
ApplicationListener<E> implementation, which also becomes a queue entry point.
Publishers are a knob table (knobs.dl section 17) matched against the
receiver's declared type, so a project class with its own publishEvent stays an
ordinary call. A listener for a subtype does not receive the supertype's
event, and a listener for an unrelated type receives nothing.

Query layer: an event_dispatch caller reads "publishes an event this handles"
and is not reported as a call that bypasses the container proxy.

Case 65-spring-application-events and plugin case event-reaches-its-listeners,
with controls. next-step-reads-the-whole-answer keeps its unmodelled
library-call check on an Object-typed event.
… the rebase

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com
…spatch and the fixture reading does not survive (#1391)
…_view() (#1510)

A Django-style route table dispatched only to an undecorated function named
directly in `urlpatterns`. A view under @login_required, a route in a list
mounted with include(), and a class-based view routed with .as_view() were
not url entry points, so impact called a change to them local. A test that
requested the URL reached the view only when the pattern started with a
slash and the view was imported by name.

Engine (dispatch.dl, knobs.dl):
- a route names the def written under its decorators, not the wrapper
- a list handed to include() (by name, inline, or in a (list, ns) tuple) is
  a route table
- X.as_view() dispatches to X's HTTP-verb methods, found through the MRO
- the url_dispatch edge carries the path the route serves, composed through
  every include() that mounts its table; a regular-expression pattern keeps
  the generic label

Plugin:
- routes are registration keys by the engine's served path, so a test that
  requests /api/v1/status/ reaches a view registered as "status/" under
  include() at "api/v1/"; a typed converter narrows the match and a
  verbatim key wins over a parameterised one
- context answers "what does this URL run" from the served path; a test
  method that requests a URL is never said to serve it, and a pattern in a
  table the engine read is not taken for the whole path
- impact's verification accepts the rules' own decorator-by-name hop

Fixes #1510, #1511, #1513, #1482, #1483
…t type (#1383)

mediator.Send(new GetOrder(id)) runs IRequestHandler<GetOrder, R>.Handle in process,
and no call site names the handler, so path called the two independent and impact on
the handler said only "entry point". framework-behavior/dispatch.dl now adds an
event_dispatch call edge from the site to the handler chosen by the request's TYPE,
beside the site's own row for the library method.

One rule, keyed on cs_med_dispatch(SendMethod, HandlerBase, Fan) in knobs.dl, whose
handler method is the one the cs_framework_callback table (the entry-point table)
already names for that base:
- Send -> IRequestHandler, the exact type, with a generic request's type arguments
  (GetById<Order> does not reach the handler of GetById<Widget>; an open generic
  handler serves any);
- Publish -> every INotificationHandler of the type and of each of its bases
  (the interface is contravariant);
- Send -> IPipelineBehavior, IRequestPreProcessor, IRequestPostProcessor: the step
  for its own request, and an open generic step for every request;
- a request declared as a base (not an interface) fans to the handler of each type
  in its family; one declared as the marker interface links nothing.
The request's type is read by the rules that already read a bus message's
(msg_arg_type_name), and where both ends resolve, the resolved type decides and not
the simple name. A receiver that is a property of another value
(services.Mediator.Send) is now named by the property's declared type, for every
by-name framework receiver. A send whose request is a mock's argument matcher
(Arg.Any<T>(), It.IsAny<T>()) links nothing: it is a stub, not a dispatch.

Case 18-mediator-send with controls (a handler of another request type, a generic
request closed over another type, a sibling notification, a notification Sent rather
than Published, a non-mediator receiver, a mock stub); tools/mediator-dispatch-test.sh asserts the
edges in the package-in-source and package-unstaged shapes.
… waits (#1606)

The compiled query programs (impact.dl, path*.dl) were cached inside the
plugin's own dl/.cache, so every plugin copy recompiled identical rules,
and the build compiled them only after publishing the graph, so the
first query raced the compile and paid it (43-79 s).

- dl_program: cache in ${XDG_CACHE_HOME:-~/.cache}/axiomcode/queries,
  keyed by the rules and the platform; an older dl/.cache binary is still
  used. A query with no binary answers from the Souffle interpreter and
  starts one detached background compile under a lock.
- axiomcode-build starts that compile when the build starts, beside the
  engine, and only reports its state after publishing.
- impact: the untyped-receiver caller sample is ordered by file and line,
  not by symbol id, so two builds of one tree show the same sample.
- tests/query_rules.py and a python case with a control.
…hey cannot analyse

- --range a..b reads from `git merge-base a b`, not a's tip, and says so when
  a has moved: commits the base branch received after the fork were reported
  as the branch's own changes and their tests selected. `a` alone is a..HEAD.
- a clean working tree over committed work answers "no change" and names the
  --range that holds the commits; not on a base branch itself.
- a copy without git is refused with what to pass, instead of a diff against
  an empty text. Named files (`changed <file>`, `test-impact <file>`, MCP
  files=[...]) count whole when there is no edit to diff.
- a file the base does not have is one `added <file>` line, plus each new
  declaration something outside the file already calls. Docstring lines are
  never read as headers, and an added declaration carries its own line.
- an insertion strictly inside a callable is its body change; an insertion
  slides past equal lines so a new type starts after the brace above it;
  package / import / using / namespace lines declare nothing.
- changed files outside every indexed language are named, never dropped;
  test-impact lists the test files whose text names them, and code that is
  also run as a program is looked for by name, with a lower-bound note when no
  test names it. An edited test file is itself a test to run.
- extensionless scripts the graph holds are no longer dropped from the diff;
  the name tier stays in the changed file's language; .cs picks dotnet test;
  --in no longer reaches changed as a file list; an answer with no rows past
  page 1 is not paged; --impact output follows the list it belongs to.
- MCP descriptions, SKILL.md and reference/changed-and-tests.md describe it.
… the index chose

`axiomcode graph` ran axiomcode-build with no flags, which detects every
language in the tree. On a repository indexed with --lang it rebuilt the
whole graph on every call, started JavaScript/TypeScript compiles for
stray files, left a graph for each, and recorded the detected languages
as the index's choice, so every later refresh solved them too.

- graph draws from graph.sqlite when the file table says it is current,
  and otherwise rebuilds with the recorded --lang/--src/--library
  (ax_fresh.rebuild_env, shared with the background refresh)
- axiomcode-build: a build that names no language keeps an explicit
  --lang (and --src) recorded in the file table (ax_fresh.py chosen), so
  the refresh, a broken-graph repair, a bare index and graph never solve a
  language the index left out; detected languages are still detected again
- the page draws every language graph the repository has
- prose counts, the page's absolute path, and `axiomcode index --library`
  as the advice; help, SKILL.md, the MCP description and README agree
- tests/graph_verb.py: 21 checks with a detected-languages control
…asks, not by symbol ids

When several declarations matched the task's words with the same score (a
mapper method, a repository method, a service method and a query method all
named findByX), the first seed and its three siblings were whichever came
first in the graph's symbol ids. Those ids are hashes that change with the
directory a project is indexed in, so the same question on the same code
named the mapper method and its XML in one copy and dropped both in another.

- pick_seeds breaks a tie by: a declaration bound by a text file of the kind
  the question asks about (the mapper method whose XML holds the SQL, for a
  SQL question); then the one whose own name holds more of the matched words;
  then its name and place. A question that asks about no text file walks no
  tree and gets no preference.
- The text-binding ring takes an interface seed's implementation callees at
  their nearer distance. A mapper call one hop inside the implementation was
  skipped whenever the closure walk had already placed it at hop 3.
- context --source on a SQL or config question crashed: the printed code
  replaced the question text before the text-file check read it.

Test: java case context-sql-tie-picks-the-bound-method (four equal matches
that sort before the mapper by name; near misses: another mapper XML with the
same statement id under a different namespace, and the same words without a
SQL question, which lists no XML).
java: a published Spring application event reaches the listeners its type fits (#1391)
…h-r2

python: a route reaches its view through decorators, include() and as_view(), and carries the path it serves (#1510)
csharp: a mediator Send or Publish reaches the handler for the request type (#1383)
plugin: compile the query rules once per machine, never while a query waits (#1606)
…e-base

changed / test-impact: read a branch from its merge-base, name what they cannot analyse (#1504)
…-flags

graph: draw the existing graph, and every rebuild keeps the languages the index chose (#1480)
java: context orders equal-scoring entry points by the text file the question asks about (#1392)
…oints, and impact names who receives a provider (#1109)

The decorator relation already gave Nest and Angular their roots (route handlers,
container-built constructors, lifecycle hooks). What it did not give was the wiring:
in `@Controller() class C { constructor(private svc: OrderService) {} }` nothing in
the repository passes an OrderService to C, the container does, so a change to
OrderService read as if no class received it.

config-resolution/di.dl reads the decorator projection once more:
- bean_def: a class carrying @Injectable is a provider, keyed by its own class.
- inject_point: a constructor parameter of a container-owned class (the same class
  decorators the roots use), typed by its declared class. A parameter carrying
  @Inject(TOKEN), or an integration's @Inject<Name>(...), is wired by token, not by
  type, and is not a slot.
- di_edge: known_bean when a provider of exactly that class exists, unsatisfied
  otherwise. Both containers key by class, so a subtype never satisfies a slot; an
  abstract class bound with `{ provide, useClass }` is reported, not guessed.

inject_point and di_edge are exported under Java's names and column orders, so the
query layer, which already reads ext_inject_point for every language, lists the
receiving constructor as "receives it by dependency injection (ctor_param)" with no
change there. bean_def stays internal: the query layer reads it as a proxying
container, which neither of these is.

Measured:
  case 68 fixture    di_edge 0 -> 4 (3 known_bean, 1 unsatisfied); edges and
                     entries of the existing code unchanged
  a real Nest app    di_edge 0 -> 23 (17 known_bean, 6 unsatisfied: every one an
                     abstract repository bound by useClass in a module); call edges
                     2023 = 2023, entry points 164 = 164; index 7.0s -> 6.2s
  a real Angular app di_edge 0 -> 19 known_bean; call edges 1947 = 1947, entry
                     points 289 = 289; index 5.75s -> 5.68s

Controls: an undecorated class with the same constructor parameter, and an
@Inject(TOKEN) parameter whose declared type is an interface, produce no slot.
Case 68's entries golden now prints di_edge (tools/entry_report.py); with the
inject_point rule removed it fails. The plugin case typescript/injected-provider
checks impact on a provider (fails without the rule), on an @InjectRepository slot
typed by a provider (no injection row; fails without the Inject<Name> match), and
on a plain class passed
to an ordinary constructor (no injection row).

Closes #1109

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…lects the generic overload (#334)

`create<K extends keyof WidgetMap>(kind: K)` declared before `create(kind: string)`:
the compiler takes the generic for `create('video')` and the widening signature for
`create(k)` with `k: string`. Once an interface's overloads shared a
declarationGroupKey, specificity decided both sites for `string`, because the literal
matched `string` concretely and matched nothing in `K`.

The overload rules now evaluate the constraint: a string literal matches a
type-variable parameter constrained by `keyof X` when X declares that key (a field or
a method). Both signatures then match and declaration order decides, as the compiler
does. A general string and a literal that is not a key still fall to `string`.

Case 24's accepted gap is removed. New case 78 covers library fields, library methods
and a client class, with controls for a non-key literal, a string variable and the
widening signature declared first. Every site agrees with the compiler.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…ory, or file:line:col is the declaration at that line (#1600)

A file:line is taken the way editors, stack traces and test runners print it:
a leading ./ is dropped, an absolute path under the repository root is read
relative to it, and a trailing :col is accepted. An absolute path outside the
repository is named as outside instead of 'no callable spans'. A root-relative
/src/... still answers as before.

Closes #1600

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

Answering a question from the graph meant first finding out, with raw
sqlite3, which table holds each fact in each language. reference/schema.md
now says it for Python, Java and C#: decorations, resolved and library base
types, entry points by reason, fields and properties and who writes them,
call-edge tiers, strings and non-source text, tests and rungs, and where each
language's graph lives. Each entry names the verb or the query, and what is
not recorded. Every query was run on a fresh index of one project per
language. SKILL.md links it in one line.

Three schema notes the graph carries were stale and now agree: Python
entry_points is not empty, C# fills fields, and type_use is empty for C#.

The Python field extractor wrote tree-sitter's 0-based row as the field line,
while every other Python relation is 1-based. Every field sat one line above
its write, and a nested class's first field landed on its `class Meta:` line.
Each site now adds 1. A new gate checks six write forms with class and method
lines as controls; the golden had frozen the old values and moves by exactly
one line on each of its 49 field rows.
…ite's argument returns (#695)

A wrapper that returns a closure whose result is the call of the wrapper's
parameter (memoize, once, lazy) now gives each wrapping call site a value of
its own. A call through it takes its value from that site's argument only,
instead of the union of every caller's argument, or nothing once the wrapper
is over the parameter fan cap.

Case 64: two memoized getters of different modules each reach their own
module's member (were ambient_terminal); once/lazy getters resolve to one
target instead of two. Controls (a closure returning its parameter, a closure
calling its parameter but returning something else) are unchanged.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
A multi-language repository could be left with no .axiomcode/out/graph.sqlite
after its first build: the other languages' graphs sat unpublished under
.axiomcode/out/<lang>, every query said "no graph yet — building one", rebuilt
every language, and lost the main graph again.

The build a query starts ran in the query's process group. A caller that stops
the query (a timeout, Ctrl-C, a host that ends the command's process group)
stopped the build with it: before the main graph was published, or while it was
being indexed, which the build took for a failure and answered by deleting the
solved graph. On a repository whose first build outlasts the caller's timeout,
every query repeated it.

- ax_contract.ensure_graph starts the build in a session of its own, as the
  background refresher already is, so it finishes whatever becomes of the query
  that started it; a later query finds it running and waits for it.
- axiomcode-build: a signal that arrives once the main language is solved is
  held until graph.sqlite points at that graph (the indexer runs in its own
  process group); the languages still unsolved are recorded in out/partial, so
  the graph reads as stale and the next build builds them. A signal before the
  main language is solved stops the build at once, as before.
- A query waiting on a refresh no longer stops waiting when the main graph is
  published while the language that holds the edited file is still being
  solved: it answered from that language's previous graph.

Tests: tests/multi_language.py gains the stopped-query and stopped-index checks
(single-language and stopped-before-solve controls); its refresh and upgrade
checks pass again. tests/refresh_races.py waits for the repair build before
holding the lock and accepts the wait message #1555 introduced.

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

A property write through a `str` value or a JSDoc typedef `shape` landed on every
other expression sharing that identity: a string is one value per text, a typedef
one value per typedef. On a real bundler's source these writes were ~73K
member_write rows and the source of its widest value sets (hundreds of parameters
holding the same 552 values).

- shared_identity_kind (ambient, str) guards every member-write rule (plain,
  computed key, keyed and enumerated copy, Object.assign-likes, defineProperty,
  defineProperties, setPrototypeOf); dropped writes are declared as
  member_write_refused(a, "shared_identity").
- A write through a shape keeps only func/ctor values (a handler stored on an
  untracked object is still callable through its typedef) and drops data stores.
- keys_helper atoms reordered so each is bound by the previous one (it was a
  cross product of first params and loop heads, re-run every iteration).
- path_value's two path-text regex tests hoisted into non-recursive helpers.

New golden case 64-shared-identity-writes with controls: object literal and
class-instance writes, typedef-declared members, and a handler written through a
typedef all keep their edges.

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

A store through a JSDoc typedef dropped every value that was not a function or a
class, so `ctx.logger = new Logger()`, `ctx.api = { fetch() {} }`,
`ctx.util = require('./util')` and `ctx.list = [a, b]` no longer reached the calls
made through the same typedef elsewhere (`ctx.logger.log()`, `for (const f of
ctx.list) f()`), which had resolved before. Instances, object literals, module
namespaces, arrays and collections are kept now; string, symbol and other scalar
stores stay dropped, which is where the width came from.

Golden case 64 gains the module and iterated-array shapes and a string-store
control that must stay unresolved.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
…ved (#536)

tsc resolves a call through a variable's ANNOTATION. Where that annotation
needs the checker -- an indexed access through a generic alias such as
`StoreApi<S>['setState']`, a named interface, an alias to an alias -- the
parser fell through to the arrow initialiser and claimed it with
`overloadCandidateCount = 1`, where tsc names one arm of an overload pair.

The row is now left unresolved (count 0) instead. Unannotated arrows and
annotations the parser can read (a function type, a type literal, one alias
hop) resolve exactly as before, including the all-optional signature set that
keeps the initialiser with the full candidate count.

On a real state-management library: 9 call sites move from arrow/1 to
unresolved, exactly the 9 that disagreed with getResolvedSignature; engine
edges 4492 -> 4492. Goldens unchanged.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
swapnilpaliwal-sd and others added 17 commits September 28, 2026 08:47
…ambda parameter takes its type (#1401)

A `var` local whose type does not come from its initializer expression was left a declared
unknown in three shapes, recorded as known-missing in case 61:
- the element variable of `for (var x : xs)`: now the component type of an array, the written
  type argument of a platform collection (or an `? extends` bound), or the Iterable<T> argument
  of a client type, read from what the client wrote when the platform is not staged;
- a try-with-resources resource: its implicit close() is now a synthesised `resource_close`
  edge to the resource type's no-argument close(), declared or `var`;
- a lambda parameter, `var` or implicit, whose target is a platform functional interface: a
  table maps each parameter to the interface's type argument.

The bundle positions a resource_close site at the resource local's declaration.

Case 61 gains the shapes with controls (raw list, `? super`, an unlisted interface, a second
type argument, a var resource of unknown type that gets no close()). Its three known-missing
edges now resolve, so the file is removed; the bytecode oracle agrees 37/37.
javascript: a call through a memoize/once wrapper returns what that site's argument returns (#695)
typescript: a string literal that is a key of a keyof constraint selects the generic overload (#334)
…bstract-test-command

hooks: C# edits get a graph line, the directive speaks once per session and names the verb, test-impact runs concrete test classes (#1504)
java: a var in an enhanced for, a try-with-resources resource and a lambda parameter takes its type (#1401)
typescript: a decorator container's constructor slots are injection points, and impact names who receives a provider (#1109)
…p a cached engine that dies with SIGILL

The engine is compiled with -march=native, and the CI cache key named the CPU
by its model name only. Hosted runners that report the same model name do not
all expose the same instruction set, so a binary compiled on one was restored
onto another and died with an illegal instruction on every TypeScript case.

The key now hashes the model name and the flags line of /proc/cpuinfo. The
driver also removes a locally cached binary that exits with SIGILL, so a bad
entry in a shared cache kills one run, not every run after it.
…hat iterates or indexes it (#967)

`const steps = [a, b]; for (const s of steps) s(node)` left `a` and `b` with no
caller, and everything they reach lost its tests: impact answered "0 tests"
for a helper three hops below a pipeline whose tests were indexed.

The value-flow holder rules now follow a function one step further, into
the elements of an array or object literal a variable, field or parameter
holds (through `as const`/`satisfies`, `push`/`unshift` and a spread). A read
of an element runs what the elements hold: a for-of binding (also over
`Object.values(h)`), `h[i]` / `h[key]`, the element parameter of a
forEach/map/filter/reduce callback, and `h.name` over a literal, which
reaches only the property of that name. Each such call is a `fn_value_call`,
the dispatch rung that impact, path and test selection walk. No call-site
answer moves: for several elements the compiler names one arbitrary member
of the union, so the call-site sites stay pinned as known-missing.

Case 78 covers every shape, with controls that must not change: an array
nothing iterates, a for-in over the table, a direct call, and a written key
that must not reach the other entries.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
ci: key the engine cache on CPU flags, drop a cached engine that dies with SIGILL (#1657)
typescript: a function held in an array or table is run by the call that iterates or indexes it (#967)
…labels worded from the reason (#1389, #1423, #1544)

One row per dependent: impact keyed its must-change and direct rows on
(callable, reason), so a callable with two reasons printed twice and the
section header counted it once per rung. Rows are now keyed on the callable
within each section; the surest reason names the row and the others follow
as "also:". --json keeps every reason. path next: names each caller once,
and the Grep preview prints overloads in one file as one line.

Non-source text: a name matched inside a .sh or .dl file (or an
extensionless shell script) is out of scope and dropped. A plain-word name
(note, export, validate) written with no call, quote or qualifier around it
is prose: counted with the grep that lists it, listed under --page all or
--limit, flagged "prose" in --json, and never counted as a binding.

Labels: entry points are worded from one table in ax_edges.py. A route,
main, RPC, CLI or message surface keeps today's sentence; a framework
callback (an ORM hook, a bean constructor, a lifecycle method) says the
framework calls it back and a change breaks that contract. An event or
request hop says it sends what the handler handles. The tests line names
test-code callables the answer lists but the count does not credit.
…te; the grouping case uses an engine reason token
…nd its entry line credits the publisher rows
impact, path and hooks: each dependent once, prose is not a binding, labels worded from the reason (#1389, #1423, #1544)
swapnilpaliwal-sd and others added 4 commits September 28, 2026 09:23
…alified name says (#1598)

`defines` is derived from line spans, and on an equal span the builder nested an
anonymous callable under the named one beside it. `function first() {…} const xs =
[1].map(function (v) {…});` and `const named = () => f(); const listed = [2].map(() => g());`
each walked a fake hop from the named callable to the anonymous one, so every caller
of `first` or `named` was reported as reaching what the other one calls.

JavaScript's qualified names follow the source's nesting (`keys.outer.<function-expression>`
is written inside `keys.outer`; `keys.<function-expression>` beside it), so on an equal
span an anonymous JavaScript callable now nests only under a callable whose qualified
name its own extends; otherwise the builder looks further down the stack. Java, C# and
TypeScript name a lambda after its type, not its method, and are unchanged.

Both builders (path export and the impact fast path) pass the qualified name to
ax_edges.defines_edges; EXPORT_VERSION 3 and IMPACT_VERSION 33 re-export old caches.

Defines edges: engine JS test graph 777 -> 773 (the two arrows at 15-jsdoc-forms
main.js:8 move from the `onDone` arrow beside them to `main`); a real Express app
with a minified bundle 860 -> 787, edges whose child name extends the definer's
394 -> 548; five other real apps and 13 corpus apps unchanged. Java and C#
span cases unchanged (19/19). Build time unchanged (1.12 s).

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
The vendored parser keeps its own version and moves once per release, only
when parser code changed. dev and main ship it at 0.1.1. On 0.1.4, four
merged changes touched parser/: #1592 (a concise arrow body is owned by the
arrow), #1616 (generated-output detection), #1621 (a package's published API
is rooted from package.json) and #1625 (the Java walk and build directories).
So 0.1.2, one bump for the release.

(cherry picked from commit 51c5e8e)

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
javascript: on one line an anonymous function nests only where its qualified name says (#1598)
Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Merge dev (the 0.1.4 release) into 0.1.5
@swapnilpaliwal-sd
swapnilpaliwal-sd merged commit ba09b31 into dev Sep 28, 2026
12 checks passed
@swapnilpaliwal-sd
swapnilpaliwal-sd deleted the 0.1.5 branch September 28, 2026 20:32
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