Git shows what changed.
Aition shows what breaks.
Change review that shows its evidence. Point it at a diff and it returns the files that could be affected, the chain of evidence reaching each one, the tests that cover them, and an explicit statement of what it did not check. Free and local for Python, TypeScript, Go, Java and C#. No API key, no network call, no account.
$ aition check Most tools tell you what they found. Aition also tells you what it couldn't check, and refuses to call anything confirmed that it can't trace.
The damage happens somewhere else
An engineer adds a required parameter to get_user. The diff is four lines in one file, and it looks fine, because it is fine in that file. Two directories away an API route still calls it the old way. A test still calls it the old way. Neither file appears in the diff, so neither appears in review.
That is the ordinary failure, not an exotic one. The couplings that actually break are usually invisible to the tools we review with: an event name published in one service and subscribed in another with no import between them, a URL route written twice, a Go interface satisfied implicitly by a type that never names it, a config key read by a service and written by a deployment file.
Search finds the string and also finds it in a comment. An import graph misses the coupling entirely because there is no import. The failures that survive review are the quiet ones. A crash tells you where it happened. A background job that drops one record in fifty tells you nothing until someone downstream notices a number is wrong.
What it returns
What changed, what could be affected with the evidence for each edge, which tests cover the affected files, and a scope line naming what was not checked.
$ aition check Aition check: 1 review item(s), 2 affected candidate(s), 1 linked test(s). Coverage: COMPLETE within supported static scope. No tests executed. No model calls. - app/users.py [changed-python-signature]: signature of get_user changed Check: Check call arguments, subclasses and compatibility tests. Affected paths (candidate impact): - app/users.py -> app/api.py [confirmed] - app/users.py -> test_users.py [confirmed] Suggested tests: test_users.py Static reachability is evidence for review, not a guarantee of runtime behavior.
No API key, no network call, no cost. Every confirmation cites its evidence in words: import using 'App' in src/Api.cs:2 resolves to App.IStore in src/IStore.cs. A confirmation you cannot trace is not a confirmation.
Four evidence levels, never promoted
A tool that reports a dependency is making a claim. If that claim turns out to be a filename that happened to match, an engineer stops believing the tool, including the times it was right. One confident wrong answer costs more than ten missing ones.
confirmedA parsed import or reference that resolves, under that language's own rules, to the changed item. Cites file and line.possibleA real signal that can't be proven statically: a same-package reference, an implicit interface implementation, an ambiguous bare name several modules share.reported (unverified)Reached, but nothing establishes the link.not reachedNo structural signal found. Explicitly not the same as safe.
Where several modules share a name, the tool names the candidates and marks the finding possible rather than picking one. Precision is measured per evidence type, which is why the weak tiers are labelled rather than confirmed: import-line hits 89%, interface and inheritance 88%, package vocabulary about 100%, symbol-name guesses 16%, shared-string conventions 7%.
Four commands
- check The default, and the one you use daily. Reviews uncommitted changes, a staged diff, the last commit or a whole branch with
--base main. Review items, affected candidates with evidence per edge, linked tests, owners, coverage gaps, scope line, and a durable run receipt. Free, local, no key. - ask Hypotheticals that need judgment rather than traversal. The structural layer finds candidates; a model classifies each file DISCARD, REWORK, REVIEW or SAFE with a reason, a confidence, and for every REVIEW the one concrete check that would settle it. Views: plan, compare, brief.
- triage For work done by an autonomous agent. Given a session transcript and a constraint that has since changed, it classifies the agent's own output: which files rest on an assumption that is no longer true. Claude Code and Codex sessions parse natively.
- verify Runs your real test command against prior findings in an isolated copy. Removes each DISCARD, runs the build, restores it. Records verification as its own status: passed, failed, inconclusive. The build is evidence, not a safety certificate.
Everything else, including impact views, the assumption ledger, history, planning, setup and diagnostics, lives underneath these four as subcommands. Every pre-collapse command name still runs and prints a one-line relocation notice.
Free where it counts
Free, forever, no account
runs entirely on your machineaition check, the full change review: impact chains, contract findings, linked tests, owners, coverage, receiptscheck impact, blast radius for a file, symbol or branchask view plan, a dependency-ordered migration sequencecheck history, the assumption ledger and timelinecheck doctor,check setup,check hook
No network request, no API key, no telemetry, no account, no hosted service. Because check needs no key, it can run on every push at zero cost.
Bring your own key
optional, bounded, inspectableaition ask, per-file verdicts with reasonsaition triage, agent-session evaluationask view compare,ask view brief
Cost is estimated and displayed before anything is sent, capped per run (default $2, set with --max-cost) and enforced across every provider call including retries. --preview prints the exact outgoing payload and stops. --structural forces the local path even when keys are present. Typical measured cost: a few cents to about a dollar, median around $0.25.
Measured, not estimated
Ground truth is derived from the repository source with grep and manual adjudication. No figure below is a model's judgment of its own output.
| Repository | Symbol | Size | Recall |
|---|---|---|---|
| spf13/cobra | Command | 36 Go | 34/34 |
| docker/compose | Compose | 352 Go | 11/11 |
| terraform-provider-aws | FromContext | 9,555 Go | 295/295 |
| spring-petclinic | BaseEntity | 50 Java | 5/5 |
| commons-lang | Validate | 629 Java | 21/21 |
| apache/fineract | NewCommandSourceHandler | 6,847 Java | 461/461 |
| Newtonsoft.Json | IContractResolver | 945 C# | 7/7 |
| ASP.NET Core | IResult | 11,300 C# | 75/75 |
Each row is a real public repository at a pinned commit, a real public symbol, and the files that genuinely consume it. The confirmed and possible split is reported rather than hidden: Newtonsoft's seven consumers are all possible, ASP.NET Core's seventy-five are twenty confirmed and fifty-five possible.
The structural layer was measured separately across 21 cold open-source repositories spanning Rails, Swift, Spring, .NET, Laravel, Go, Rust and C++, at 1,202 of 1,202 ground-truth files.
A frozen three-scenario benchmark with fifteen hidden invalidated files. Directional on a small benchmark rather than a general accuracy claim, supported by three real agent sessions in which the transcript moved verdicts toward ground truth and never away. The benchmark vector has been re-run after every change since and has never moved.
The suite is about 610 behavioural checks, 44 logic checks and 120 product tests, zero failing. An optional calibrated pre-filter scores the weakest evidence tiers before they reach a model: Brier score 0.109, 74% precision at 91% recall on 543 labelled files, cutting files sent to the model by about a third at roughly 23% lower cost.
Five deep, sixteen scanned
Two tiers, shown honestly. In the deep tier Aition parses the language's own grammar and resolves its own module rules, so store imported from one directory is a different thing from store imported from another.
| Language | Resolution handles | Contract changes detected |
|---|---|---|
| Python | packages, relative imports, aliases, from-import forms, parsed with AST | function and class signatures, base classes |
| TypeScript | named and default exports, re-exports, barrel files, tsconfig paths | export signatures, declarations |
| Go | package-by-directory, go.mod module paths, internal visibility, dot-imports | interface methods, exported signatures, structs, removals |
| Java | package declarations, source roots, single-type, on-demand and static imports | method signatures, interface members, public fields, hierarchy |
| C# | file-scoped and block namespaces, using static, aliased and global usings | method signatures, interface members, properties, hierarchy |
Rust is next, then Kotlin and Swift, then Ruby and PHP. C, C++ and Objective-C last, and likely as explicitly partial. No language is called deep until it has been validated on real repositories with zero false confirmations.
Broad scan. Every language below is scanned for connections and appears in impact chains.
Plus non-code formats, treated as definition sources.
What it follows
An import graph is a partial map. It captures the dependencies a language makes explicit and misses the ones a system creates by convention. Hover for an example.
Contract changes it reports. A function or method signature changed, including parameters, return type, ref, out, params and throws. An interface member added, removed or changed, with implementers surfaced as candidates. A public property, field or struct member changed. A class hierarchy changed. An exported symbol or public type removed. An export signature changed through a re-export or barrel chain. Public and protected changes are high-signal; internal and private changes are labelled lower-signal rather than treated as breaking API.
How it works
Large repositories are handled by selecting rather than by scanning everything. When a change touches a symbol, a cheap pre-filter finds the files that could reference it, and the deep-parse budget is spent only on those. On a 6,847-file Java repository that is the difference between reaching 37 of 461 true consumers and reaching all 461. An on-disk index keyed by file mtime and size makes repeat questions two to five times faster, at 18 MB on an 8,861-file repository.
The honesty posture
The worst outcome for a review tool is not a missed dependency. It is a confident wrong answer, because that is the one that ends the relationship. These are enforced in the build rather than left to discipline.
- Evidence on every findingEach affected file carries how it was reached, in words, with the file and line.
- A scope line on every runEvery command ends by stating what it did not check. Build tags, go.work, vendored deps, cgo and generics on a Go run. Assemblies, NuGet, the BCL, MSBuild and source generators on a C# run. Printed, not buried in documentation.
- No merge-safety claimNo command asserts that a change is safe to merge, ship or deploy. SAFE is defined in the output as not materially affected by this change.
- A verification plan on every REVIEWInstead of stopping at needs review, each REVIEW names the one concrete check that would settle it.
- Coverage reported honestlyIf scan bounds are exhausted the run returns coverage_incomplete with the fraction covered and a non-zero status, never an implication that the rest was checked.
- A standing false-reassurance gateThe build measures affected-files-wrongly-SAFE and needed-files-wrongly-DISCARD separately from excess-warning noise. Inject a provider that answers SAFE to everything and the build fails with a false-SAFE rate of 1.0 that warning noise cannot cancel. Inject a wrong confirmation in any deep-tier language and it is caught.
- Durable run receiptsEvery analysis records the run id, input identity, tool and model versions, settings, what was examined, what was excluded and why, the verdicts and the cost. Yesterday's result can be reopened and compared to today's.
Agents can call it directly
An MCP server exposes check, ask, plan and verify over stdio, so Claude Code, Codex or Cursor can assess blast radius mid-session rather than a human assessing it afterward. An MCP-invoked run gets the same budget, policy and safety guarantees as the CLI.
$ claude mcp add aition -- python3 /path/to/aition_mcp.py aition check setup writes CLAUDE.md and AGENTS.md, instructing the agent to check blast radius before contract-changing edits and to verify afterwards. Ordinary edits are excluded so the directive doesn't fire on everything.
Claude Code sessions and Codex CLI rollouts parse natively into the same internal shape. Each tool call is paired with its result, so a failed patch is never read as a successful edit, and a transcript that parses to nothing is refused rather than silently analysed as empty. Assumptions stated across sessions go to a ledger that outlives the session, with five explicit states and evidence attached to every transition, so an assumption made on Monday and built on Tuesday can be flagged on Wednesday.
CI can tell nothing blocking from couldn't run
--json puts exactly one object on stdout with a schema version, a status, a mode, and either the result or a structured error. All progress and diagnostics go to stderr, so parsing stdout always works, including on failures.
| Exit | Meaning |
|---|---|
0 | Evaluated. No threshold hit. |
2 | Evaluated. A --fail-on threshold was hit. Block the merge. |
1 | Could not evaluate: invalid input, budget refusal, provider error, missing credentials, incomplete coverage. |
130 | Cancelled. |
A gate can never pass unevaluated. --fail-on DISCARD|REWORK|REVIEW requires a completed verdict evaluation. Run it without a key, in structural mode, or with nothing to evaluate, and it returns exit 1 with evaluation_unavailable, never a green gate because credentials were missing.
A slash-command GitHub Action is written and reviewed but has not yet been exercised on a live repository. check hook installs a post-commit hook for review on commit.
Local first, and provable
- Nothing leaves by defaultcheck and every free view run entirely on your machine. No network request, no account, no telemetry, no hosted service. Nothing about your repository leaves unless you run a model-backed command with your own key.
- See what would leave first--preview lists every file that would be sent, its size, its redaction count, the providers that would receive it and the cost estimate, then stops. --preview --json writes the literal outgoing prompt to a local file. Preview makes zero provider requests.
- Redaction is a hard gateAWS and GitHub keys, API keys, database-URL passwords, JWTs, private keys and EVM keys are stripped at every provider boundary. If redaction fails on any outgoing file, the request is refused rather than sent.
- Spend is enforced, not advisoryEvery request reserves its estimate against a per-run ceiling before it is made, including retries and the pre-filter. Exceeding the cap raises before anything is sent.
- Your repository is untrusted inputThe on-disk index is JSON with a schema check and never executable. Symlinks out of the repository are not followed, .gitignore is honoured so ignored files can't become provider inputs, and any filesystem action escaping the project is refused. It never modifies your git index.
- IsolationVerification runs in an isolated copy rather than your working tree, with process-group cancellation so a timeout actually kills child processes. Configuration is request-scoped, so two projects in one process cannot contaminate each other.
What it doesn't do
Stated plainly, because a tool that hides its limits cannot be trusted about anything else.
- It does not prove a change is safe. Static reachability is evidence for review. Runtime behaviour, dynamic dispatch and reflection can escape it entirely.
- It does not typecheck. Implicit interface satisfaction in Go, trait impls in Rust and subclass relationships in Java and C# are reported as candidates. Proving them requires a compiler.
- It does not resolve external dependencies. Types from a JAR, a NuGet package, the JDK, the .NET BCL or an external crate are marked external and unresolved. Nothing is downloaded.
- It does not evaluate build configuration. Conditional compilation, build tags, preprocessor directives, source generators and annotation processing are out of scope.
- Deep analysis covers five languages. The rest get the broad scan, and the output distinguishes the two.
- Coverage on very large repositories can be incomplete. Reported explicitly with the covered fraction and a non-zero exit, never implied as complete.
- Live model judgment is not independently benchmarked. The structural layer is validated against ground truth; verdict quality is gated against regression but has no third-party standard.
- No engineer outside development has used it yet. Everything measured is real. None of it proves the findings change what a working engineer does. That is the next thing to learn, and it can only be learned from use.
Get it
Open core, MIT licensed. Python 3.9 or later on Linux, macOS and Windows. The installer creates its own environment, so the system interpreter and PATH do not matter, and aition doctor prints the exact fix for anything wrong.
$ bash install.sh # from the source folder $ cd your-project $ aition check # free, local, no key required
Bring a repository you'd like to see it fail on. That is the useful thing to send back.