Skip to content

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
Get early access Open core, MIT. Python 3.9+, Linux, macOS and Windows.

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 machine
  • aition check, the full change review: impact chains, contract findings, linked tests, owners, coverage, receipts
  • check impact, blast radius for a file, symbol or branch
  • ask view plan, a dependency-ordered migration sequence
  • check history, the assumption ledger and timeline
  • check 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, inspectable
  • aition ask, per-file verdicts with reasons
  • aition triage, agent-session evaluation
  • ask 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.

0false confirmations across every validated repository
461 of 461consumers found in a 6,847-file Java repo
1,202 of 1,202ground-truth files across 21 cold repos
10.4send-to-end check on 6,847 files, cold, 241 MiB peak
$0.00what a check costs, every time
15 of 15invalidated files found with an agent transcript, against 4 of 15 from the diff alone
Deep-tier resolution on real repositories at pinned commits
RepositorySymbolSizeRecall
spf13/cobraCommand36 Go34/34
docker/composeCompose352 Go11/11
terraform-provider-awsFromContext9,555 Go295/295
spring-petclinicBaseEntity50 Java5/5
commons-langValidate629 Java21/21
apache/fineractNewCommandSourceHandler6,847 Java461/461
Newtonsoft.JsonIContractResolver945 C#7/7
ASP.NET CoreIResult11,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.

With the transcript15/15
Diff only4/15

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.

The full write-up, with every run.

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.

Deep tier languages
LanguageResolution handlesContract changes detected
Pythonpackages, relative imports, aliases, from-import forms, parsed with ASTfunction and class signatures, base classes
TypeScriptnamed and default exports, re-exports, barrel files, tsconfig pathsexport signatures, declarations
Gopackage-by-directory, go.mod module paths, internal visibility, dot-importsinterface methods, exported signatures, structs, removals
Javapackage declarations, source roots, single-type, on-demand and static importsmethod signatures, interface members, public fields, hierarchy
C#file-scoped and block namespaces, using static, aliased and global usingsmethod 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.

PythonJavaScriptTypeScriptVueSvelteGoRustJavaKotlinCC++Objective-CC#SwiftRubyPHPSolidity

Plus non-code formats, treated as definition sources.

JSONYAMLTOML.envSQL schemasmigrationsProtobufGraphQLAvroThriftROS .msguORB .msgDockerfileCI workflowsMakefileCMakepackage.jsonCargo.tomlgo.modpyproject.tomlrequirements.txtGemfilecomposer.jsonMavenGradle.csprojPackage.swiftPodfile

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.

importssymbolsconvention stringsinterfacespackage vocabularycross-format namesmanifestspath referencesdeletions and renames

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

Structural layer local, free, deterministic Reads the repository, builds the change set from git's own structured output rather than diff text, and traces what could be affected. This layer does all the discovery. It never calls a model.
Reasoning layer optional, your key, capped Used only by ask and triage. The structural layer decides which files are worth asking about; a model decides what each one means. Nothing is sent until the cost is estimated and capped and secrets are redacted.

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 code contract
ExitMeaning
0Evaluated. No threshold hit.
2Evaluated. A --fail-on threshold was hit. Block the merge.
1Could not evaluate: invalid input, budget refusal, provider error, missing credentials, incomplete coverage.
130Cancelled.

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.