Skip to content

Release 0.1.2: promote dev to main - #1368

Merged
swapnilpaliwal-sd merged 18 commits into
mainfrom
dev
Sep 26, 2026
Merged

swapnilpaliwal-sd merged 18 commits into
mainfrom
dev

Conversation

@swapnilpaliwal-sd

Copy link
Copy Markdown
Contributor

Promote dev to main: release 0.1.2

Everything on dev since v0.1.1:

On merge, CI builds every engine on all five platforms and runs the e2e on each. build-engines.yml changed since the v0.1.1 build, so this build compiles fresh instead of restoring the cache. When it is green, release.yml tags v0.1.2 and opens a draft release as axiomcode-bot. Publishing the draft ships to npm.

No rule or .dl changes since v0.1.1: every engine id and query-binary id is unchanged.

swapnilpaliwal-sd and others added 18 commits September 24, 2026 20:46
…d tests match the code (#1321)

* parser/readme: C# and TypeScript are documented, and usage, output and tests match the code
* parser/readme: nested type names and the spellings a query still meets
* parser: a description that does not list languages, so it cannot go stale
* index: use python3's sqlite3 module, not the sqlite3 CLI (#1329)

axiomcode-build shelled out to the sqlite3 command-line program in four
places. Stock Ubuntu and Git for Windows do not ship it, so index exited
127 after building the graph and, under set -e, skipped the fact export
and rule warm-up, which then crashed context on a missing edge.facts.
python3 is already a stated requirement and carries sqlite3 itself.

* release: 0.1.1 (#1329); the vendored parser keeps 0.1.0
…3 that runs it (#1331) (#1334)

* windows: find Python the way the MCP launcher does, and give bash a python3 that runs it (#1331)

A python.org install on Windows has python.exe and py.exe and no python3, and on a
desktop Windows python3 is the Store placeholder. Every CLI verb and every hook
called python3, so all of them failed although Python was installed.

- mcp/find-python.js: AXIOMCODE_PYTHON, python3, python, py -3; the first that runs,
  by its sys.executable. launch.js now takes its list from here.
- bin/axiomcode.js and launch.js put scripts/pyshim/python3 first on bash's PATH,
  pointing at that interpreter (always on Windows, and elsewhere when python3 is not
  the one chosen).
- hooks: every command is node hooks/run.js <hook>.py, which passes stdin, stdout,
  stderr and the exit status through unchanged, and exits 0 when there is no Python
  so a hook never blocks a tool.
- scripts/axiomcode makes the same choice when run straight from a shell, as the
  skill says to without MCP tools.
- Python-started builds use AXIOMCODE_BASH when set, as server.py already does.
- tests/python_names.py: 7 checks, all red on dev; manifests.py requires the runner.
- version 0.1.1.

* ax_fresh: Git Bash's fd 9 is not a descriptor in native python.exe, so the build lock looped forever (#1331)

tests/README.md is left as it was.

* windows: run Python in UTF-8 mode, so a piped answer with → does not raise UnicodeEncodeError (#1331)

* graph: run axiomcode-build through bash; Windows cannot exec a script (#1331)

* parser stays at 0.1.0

* version.mjs: the vendored parser keeps its own version
… covers another (#1336) (#1337)

The chip thresholds compared the absolute zoom, and a small disc is fitted
at a zoom that already passed all of them, so every node was labelled on
open. They now count from the zoom fitAll chose. chip() skips a box that
overlaps one already drawn; the hover and the selection always draw. Only
the ambient chips give way to the rim package labels, so a selection's
callers and callees stay labelled. The pass that labelled every node when
fewer than 500 were shown is removed.

readme: an image of the graph under "Why AxiomCode Graph?", one method
selected with its resolved calls drawn across the codebase.
* README: replace the Spring graph screenshot with the test-impact graph

* README: transparent corners on the impact graph image
… found (#1341) (#1342)

Through a cycle the partial routes multiply with every hop, and when fewer than
max_paths short routes exist the heap grew until the machine ran out of memory.
The search now stops after AXIOMCODE_EVERY_BUDGET partial routes (200,000) and
says so, with how to narrow an endpoint.
… --in find their scopes (#1340) (#1343)

rel() built the index's paths with os.path.relpath, which uses the platform
separator, while every reader of the index splits on '/'. On Windows context
found no scope and exited 2. The indexer, the hooks and the verbs now build
'/'-separated keys, and --in accepts a path written with backslashes.
… Soufflé (#1330) (#1335)

build-engines compiles impact.dl, path.dl, path-opt.dl and path-every.dl on all five
platforms next to the language engines, keyed by dl_program.py's own rules id, and checks
each platform's binaries against the interpreter on a small fixture. The engine package
ships them under queries/, dl_program.py uses the one built from these rules before it
tries a local compile, and publish refuses a platform that lacks them. The nightly e2e now
indexes, runs impact and path from the installed tarballs with no souffle on PATH.

A local dl/.cache no longer reaches the npm package.
…to 1000 (#1341) (#1344)

#1342 stopped the enumeration after a fixed amount of work, which kept memory
bounded but cut the route list short: on a cyclic subgraph it printed one route
where far more exist. Yen's algorithm finds each next-shortest simple route with
a bounded number of breadth-first searches, so the work grows with the routes
asked for and the ones printed are exactly the shortest. --every counts routes
up to 1000, prints the shortest --paths N (20), and past 1000 says so and asks
for a narrower endpoint.
… checks every verb's answer (#1347)

* ci: the way into main installs the packages on all five platforms and runs every verb

A pull request into main now packs @axiomcode/code-graph once, and on linux-x64,
linux-arm64, win32-x64, darwin-arm64 and darwin-x64 installs it with that
platform's engine package, with no Soufflé, and for every language indexes a
case and runs impact, test-impact, path (SQL and Datalog), path --every (both)
and context. The required CI check fails unless every platform passes, so a
release is never cut from a main that has not done this.

* e2e: every query verb must return the answer a tiny project makes true, in all five languages

.github/e2e/<language> is entry() -> helper() -> leaf() and one test calling
entry(). e2e-queries.sh indexes it and requires: impact leaf lists helper as a
resolved caller; path entry leaf reaches it through helper, from SQL and from
the shipped Datalog programs; --every lists a route through helper; context
names leaf; and after a real edit to leaf, changed names it and test-impact
selects the test through the graph, not by its name. A non-zero exit or a
traceback fails. Removing the call, or the test's call, turns it red.
…t refused (#1345) (#1346)

The scope menu is built from the directories above the indexed files, so a
repository whose files sit at its root had nothing to offer, and context refused
with an empty list of suggestions. The root is the one scope such a tree has:
context now uses it and says so, the way a single-package tree is handled.
…ds it on main, and publish that build (#1350) (#1351)

No pull request builds the platform engines any more. A push to main whose
version has no tag yet builds every platform and runs the five-platform e2e;
release.yml tags and drafts only after that run is green; publish-npm ships
that run's engines and code-graph tarball instead of compiling again. A pull
request into main may not reuse a released version, and a change that keeps
main's version (CI, docs) builds nothing when it lands.
A squash promotion lands on main as a new commit, so GitHub keeps listing
dev's originals as ahead of main even once they are released. With merge
commits main shares dev's history and the count is only unreleased work.

protect-main.sh: protect-main allows only merge commits and no longer
requires linear history; the repository allows merge commits, since that
setting cannot differ per branch. Work into dev stays squash.
* cli: axiomcode --version prints the installed version (#1352)

The Node launcher answers it before looking for bash, so it works on a machine
where bash cannot be found; bin/axiomcode answers it the same way from a
checkout. With other arguments --version stays the build option.

--help now prints the header up to its closing rule rather than a fixed line
range, which the new lines would have cut short.

* version 0.1.2
… hardening, AxiomCode bot (#1367)

* query engines: pick the engine package from the hardware, not the interpreter (#1359)

An Intel python3 on an Apple Silicon Mac runs under Rosetta and reports
x86_64, so dl_program looked for engine-darwin-x64 while npm had
installed engine-darwin-arm64, and impact failed without Souffle. A bash
started from it inherited the translation, and run-souffle.sh's uname -m
made the same choice for the rebuild.

On macOS, hw.optional.arm64 now decides (sysctl by full path, since
/usr/sbin is often not on a hook's PATH). Both lookups also fall back to
the same OS's other architecture, because npm installs exactly one
engine package per machine.

* query targets: one dotted spelling for every language, the written one first (#1360)

Each front end spells a qualified name its own way (Java/C# pkg.Owner.m,
Python pkg.module.f, TypeScript src/util#square, JavaScript src/util.square),
so `util.square` resolved in Python and failed in TypeScript, where the
lowercase dotted shape was then handed to the config-key path and died with
"looks like a configuration key".

- The resolvers (path, and impact's types/fields, which path's methods
  lookup serves) now also compare names with / # :: $ read as `.`, on both
  sides. This runs only after the name as written matched nothing, so every
  spelling that resolved before resolves to the same declaration now.
- A dotted name that fits declarations differing only in separators
  (a/b#c, a.b#c, a#b.c) is refused, listing each exact spelling; each of
  those still resolves to its own declaration.
- A lowercase dotted name goes to the config-key path only when the graph
  has configuration facts or its last segment names nothing the code
  declares; otherwise a miss names what is close.
- A qualified name whose last segment the client declares no longer falls
  through to "type used by name", which answered zzz.square with every
  reference to `square`.
- changed hands hooks the full dotted name of the edited declaration
  (target), keeping the short name in the printed hint (shown_target), so a
  hook's impact no longer answers for every declaration of the same name.
- The dotted comparison is prefiltered with LIKE on the last segment, which
  keeps a miss on a million-symbol table at ~0.06 s instead of ~2.3 s.

Cases: typescript/dotted-target, typescript/separator-collision,
javascript/dotted-target. Help and SKILL.md say separators are
interchangeable.

* javascript index: a function, a class or a require binding is not a variable (#1361)

all-javascript-variables.csv carries a binding for every declaration: a
`function f` (FUNCTION_DECLARATION_HOISTED), a `class C` (CLASS_TDZ, twice)
and `const { C } = require('./m')` (CONST_BLOCK_TDZ with an importLinkHash).
The index kept all of them as variable/const rows beside the function or
class itself, so `impact C` on nearly every imported class refused as
"declared as more than one kind", and `changed` reported an edited function
as a field, which sent the edit hook's impact to the wrong declaration.

Only IMPORT_BINDING was excluded. Function and class bindings and bindings
with an importLinkHash now are too, as Python's index already excludes
FUNCTION_DEF, CLASS_DEF and IMPORT. Case: javascript/imported-class.

* windows: impact without symlinks, programs from PATH only, quieter refresh (#1363, #1332, #1364)

impact, and path's Datalog backend, staged fact files with os.symlink,
which an unelevated Windows user may not call (WinError 1314): every
impact, test-impact and changed --impact crashed for an ordinary user,
while CI's elevated runner passed (#1363). Facts are now staged by
symlink, else hard link, else copy. tests/no_symlink.py denies
os.symlink on any OS and fails on the old code.

Windows starts a program named without a path from the current
directory before PATH, and so does Node's spawn. The launcher's
`git --exec-path` and python probes therefore ran a git.exe / py.exe
lying in the working directory: an installer that waited for ever,
which was the pipeline "stall" (#1332), and a planted binary in a
repository would have run. Bare names are resolved over PATH only
(mcp/which.js), the probes time out, and the launcher sets
NoDefaultCurrentDirectoryInExePath=1 for everything below it.

#1364:
- dl_program also looks next to npm's axiomcode.cmd wrapper for the
  engine package, which a plugin installed outside the npm tree needs.
- The background refresher starts bash with CREATE_NO_WINDOW.
- The hooks' relpath no longer raises across drives.
- The dispatcher exports AXIOMCODE_BASH from the Git Bash running it.
- Baseline library roots are split on commas only.

Verified on Windows Server 2022 as a non-admin user: all five languages
index, impact, path, context and changed --impact; CRLF sources and a
path with a space; the pipeline from a directory holding git.exe and
py.exe; MCP initialize and tools/list; 24/24 expected edges.

* ci: write releases, tags, sync merges and bot issues as the AxiomCode app (#1365)

GITHUB_TOKEN always acts as github-actions[bot], so v0.1.1 reads
"github-actions released this" and its tag's tagger is github-actions.
An org-owned GitHub App's token acts as <app-slug>[bot] with the app's
avatar.

.github/actions/bot mints that token per run from the repository
variable AXIOMCODE_BOT_APP_ID and the secret AXIOMCODE_BOT_PRIVATE_KEY,
and returns it with the matching git name and noreply email. Until they
exist it returns github.token and the github-actions identity, so this
can merge before the app is set up.

Used by: release.yml's tag-and-draft step and the main -> dev sync
merge with its conflict issue; the nightly's failure issue; main-guard's
direct-push issue. Pushes go to an explicit x-access-token URL so the
checkout's persisted GITHUB_TOKEN header is not what authenticates them.

A push with an app token starts workflows. Nothing runs on a tag push
and a draft release does not trigger publish-npm, so the one new run
is CI on dev after the sync merge.

* ci: the release e2e asks every CLI verb, in every target spelling (#1366)

The five-platform e2e installs the release binaries with no Souffle and
answered index, impact, path (both backends), path --every, context,
changed and test-impact. It now also asks, on the same fixtures:

- impact by the graph's own qualified spelling, by the dotted form every
  language accepts, and by file:line (read from the built graph, not
  written into the fixture), so #1360 cannot come back unnoticed;
- impact --json, which must parse and name the caller;
- path '*' <leaf>;
- impact and the Datalog path with os.symlink refused, as for an
  unelevated Windows user; the runner is elevated and never saw #1363;
- on Windows, impact run from a directory holding git.exe, python.exe,
  python3.exe and py.exe (copies of cmd.exe), for #1332;
- changed --impact, graph --out, help impact, and --version against the
  installed package's version.

Run locally on darwin-arm64 against the packaged engines: all five
languages pass; against the 0.1.1 tarball the TypeScript dotted target
and the no-symlink impact fail, as they should.
@swapnilpaliwal-sd
swapnilpaliwal-sd enabled auto-merge (squash) September 26, 2026 08:50
@swapnilpaliwal-sd
swapnilpaliwal-sd merged commit 32ed9b1 into main Sep 26, 2026
25 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