Evidence-driven code review and project understanding. Point it at a repository or a pull request; get a project you can walk — a measured dependency map, a page per module, editor-grade source with the measured symbols marked, call-flow diagrams that cannot invent an arrow, adversarially-validated review findings, and a question box whose every answer is citation-checked. Anything that cannot be traced to a pinned revision, an extractor receipt, or a validated finding says so instead of pretending.
Everything in that screenshot is either measured (the counts, the flow arrows, the fan-in rankings — each backed by an extractor receipt) or cited (the narrative's 36 statements each carry a citation that was validated against this revision; the statements that failed validation were deleted and the deletion is disclosed).
The files tab is the git tree at the analyzed revision — not just the modules the graph measured. Bright files carry measured symbol counts and open their module page; docs, workflows and scripts are dim but open as pinned source. Generated files are labeled, which is also why they carry no findings.
Source renders with VS Code's own grammars (Shiki, Tokyo Night). Two colour channels,
deliberately distinct: text colour is the grammar of the file's extension — never
content-guessed — while the coloured left border and the name ← N badge mean a symbol
the graph measured, spanning its whole definition. An unmarked identifier has no graph
node, and the panel says so rather than implying full coverage.
Click any module named anywhere in the app — a citation, a matrix row, a cycle member, a
finding's location — and land on its page: what it defines (types first, ranked by fan-in,
large groups collapsed, pub items marked), who uses each definition, what it imports,
the flows passing through it, its place in the suggested reading order, and, on a
pull-request run, exactly what the change did here. The header's interface m/n badge
is the measured module depth — how much of the module is surface versus implementation,
read from the signatures rust-analyzer rendered (pub(crate) counts as internal) — and
changed N× is the file's churn, counted from git history in one receipted pass (the
overview ranks the most-changed modules, and the map draws churn as border thickness).
Every run also exports its understanding as an
llms.txt-shaped artifact (/api/runs/{id}/artifact/llms-txt)
so other agents can consume what was measured without knowing this tool.
On a pull request the map says which of it you touched: a count chip, a halo on changed nodes, markers on the matrix rows, and a focus facet that narrows to one side of the diff.
The full graph is never rendered — node-link views stop being readable around 25 nodes, so a server-side readability gate refuses hairballs and says why. What you get instead: packages first, one package's modules levelized (only cycle edges drawn — the ones worth seeing), and the dependency matrix for the whole project, where a cell above the diagonal is a cycle.
Path-finding answers "how does A reach B" — the one node-link task that works at any size. Endpoints are offered only if they can participate, every hop is labelled with its measured edge kind, and "nothing connects A to B" is an answer, not an empty canvas.
Ctrl-K from any tab searches everything the graph measured — files land on their module
page, symbols land with their definition expanded. Arrows and enter work; so does "nothing
in this run's graph matches".
Ask anything about the module on screen. The agent answers from that module's source and
graph slice, and every sentence is citation-checked against the revision: claims whose
citations do not resolve are deleted and the deletions disclosed. Answers are cached
content-addressed — the same question at the same revision is free forever — and every
previously-asked question is listed for one-click reopening. Each definition also has an
explain? button that asks for you.
From the same diff: which entities this change added, removed, renamed or moved,
cross-referenced against every tracked document. A reference page still naming a
removed function, docs that missed a rename, a path that moved, a new public
function nobody documented — each named with its file and line. Advisory only:
nothing here edits a document or blocks a review, because documentation is
somebody's writing and the pipeline's job is to say what it noticed. Matching is
word-bounded and exact, so compute_output is never a mention of put; the
Diátaxis classification names the signal it decided on, so you can disagree with
it; and every generated diagram now carries the source lines it was drawn from —
plus a readability verdict, because rendering and being legible are different
claims and only the first one used to be checked. An over-budget diagram is drawn
anyway with its numbers stated: shortening labels until they fit is how
bitflags 1.3.2 and bitflags 2.11.0 become the same box.
A pull-request run analyzes both revisions and shows the change three ways, none of
them a text diff: the structural delta (symbols and relationships added, removed, moved —
"storage now imports api" appears in the diff of neither file), the public-API delta
measured by cargo public-api with breaking-change severity from cargo-semver-checks,
and the bounded impact set with its precision caveat attached to the artifact itself.
Reviewers propose; a validator that never saw the proposal re-examines each finding in a fresh context, with the real build/test toolchain at hand. The review tab shows the whole funnel — including what did not survive, because a table of survivors looks identical whether the check rejected eleven candidates or none. Every verdict carries a written rationale and a confidence, both visible in the funnel. A coverage panel shows what each reviewer actually read — measured by the engine from the tool stream, never the model's own claim, with "unknown" honestly distinct from read and unread. A reviewer that fails with invalid output or a timeout gets exactly one retry (the validation errors quoted back), and both attempts stay visible in the agent ledger.
Rejections are also remembered across runs (ADR-0016): a finding that recurs at
byte-identical code is suppressed instead of re-validated, shown with the original run
and reason — so re-running on the same repository gets cheaper and quieter, and
codeatlas compare still calls the two runs reproducible. Any edit to the file re-opens
its questions.
Before the reviewers run, a threats tab lays out what an attacker could do here:
trust boundaries with the data that crosses them, assets with the properties that
actually matter, and — given equal space — what the attacker provably cannot do,
because that is what keeps severity honest. Abuse paths are numbered TM-nnn; each names
its existing controls and says whether their evidence checked out. A repository with no
meaningful attack surface says so, rather than inventing a boundary to fill the page.
The model is built once per repository and reused — it describes what a system is,
which changes far more slowly than its code — so a second review on the same repo pays
nothing to know where to look. Its focus paths feed forward into the reviewers, which
weight them and calibrate severity to what "high" means for this repository. A model
reused from an earlier revision says which one; --refresh-threat-model rebuilds it.
A validated security finding then carries its own attack-path receipt: how attacker-influenced data reaches the flaw (source → sink → outcome), who can drive it from where, impact and likelihood each with a reason — and what the analysis could not establish, because a receipt that hides its gaps is a confidence trick.
A C4 container view derived from the measured graph (every box names the node it came
from; nothing is drawn without evidence), exportable as Structurizr DSL — and an audit of
the repository's own ADRs against the code: conformed, drifting (stated, not
softened), or unverifiable, which is never allowed to look like conformance. ADRs can
carry machine-addressable claims (IMP-001 (layering): api -> cache -> storage) — the
audit then checks the decision's own declared order with its text as the verbatim
assertion, and verdicts bind to stable claim ids; an ADR without claims gets the inferred
audit, labeled as the guess it is. The page also shows the decision mesh both ways:
which code violates a decision (graph evidence) and which code cites one (a receipted
scan), with dangling references — in either direction — called out as broken history.
Most projects speak no protocol — and forcing a sequence diagram onto a batch tool would be the most convincing wrong artifact this tool could produce. When the model says null, the page says why, with the reasoning cited to source.
Every inference in every run is produced by one of twelve registered skills, and each one
carries a governance card a reviewer can act on without opening the skill source:
owner, permission envelope (enforced by the engine, not by convention), credential needs
with an honest "not specified" vocabulary, risks with mitigations, output contract, and
eval coverage. The card is bound to the trusted registry by build-breaking checks — a
skill version bump without card re-review fails — and behavior-critical skills carry
activation evals including negative cases: on a deliberately sound crate, the recorded
reviewers report nothing, the protocol modeler refuses, and the threat modeler returns an
honest empty. Scope compliance is graded from measured engine telemetry (reads watched
from the tool stream, denials from the permission hook) — the audit over the recorded
cassettes caught two skills probing off-allowlist commands live, and the hook denying
both. Same catalog on the CLI (codeatlas skills), in docs/skill-cards.md, and in the
dashboard.
The run detail page opens the manifest — toolchain versions, which model answered at what token cost, the run's own degradation notes ("verification tools unavailable: …"), every output by role, each row openable. Below it: the agent invocation ledger and a receipt for every deterministic tool invocation. This is the page you open when you want to know whether to believe the other pages.
Everything above needs a server, a database and a browser build. The person you are
asking to review a change has none of those. codeatlas walkthrough <run-id> writes one
self-contained HTML file per module topic — an inlined diagram, and every module expanding
to its real source read from the revision that was measured. No script tag, no CDN, no
network: the file opens on a laptop with nothing installed, which is the whole point, and
a browser test fails the build if the page requests anything at all.
It says what it is. The revision is on the page, the diagram carries its own readability verdict rather than implying the picture is fine, and when the renderer is missing it ships the diagram source and says so. Syntax colouring and cross-links stay in the dashboard — this file trades them for opening anywhere.
The pipeline runs in shadow mode: it prepares the exact review payload and posts
nothing. Publishing requires, simultaneously: an explicit request-approval, a human
approve recorded with name and time, CODEATLAS_PUBLISH_ENABLED=1 in the environment
(default off), an unset CODEATLAS_KILL_SWITCH, a clean secret scan, and no prior
publication — every one re-checked at post time, because reaching a code path is not
evidence of permission. The review tab reads the publication ledger rather than asserting
an outcome.
A published finding can carry a one-click committable fix — but the commit button is
earned, not assumed. Reviewers may propose a fix only when replacing exactly the cited
lines resolves the issue entirely (the completeness rule), and the deterministic payload
builder renders a GitHub suggestion block only when the comment's anchor provably
covers that full span; anything less ships as visible, non-committable text. The
approval preview shows the fence the human is approving. Two more levers are explicit
opt-ins: --attack-paths-for-rejected buys receipts for dismissed security candidates
(why no path exists, and exactly where it breaks), and --model skill=model routes any
skill to a cheaper tier — with scripts/tier_experiment.py as the recall-fixture
measurement that decision should wait on, and the invocation ledger recording who
actually answered.
| What | Why | Notes |
|---|---|---|
| Python 3.12 + uv | the backend | uv sync installs everything |
| Node 20+ | the dashboard | npm install in frontend/ |
| PostgreSQL 17 | the evidence store | infra/ has init scripts; credentials live in the OS keyring |
Rust toolchain + rust-analyzer |
extraction | rustup, plus rust-analyzer on PATH |
cargo-public-api, cargo-semver-checks |
the API delta | PR runs degrade gracefully without them |
Structurizr CLI, mmdc (optional) |
diagram validation | absence is noted in the manifest, never silent |
claude CLI, logged in (optional) |
narration, review, ask | everything deterministic works without it |
uv run poe verify-env prints the full tool matrix — what is installed versus required.
uv sync # Python deps into .venv
uv run poe verify-env # check the tool matrix
# initialize PostgreSQL (see docs/runbooks/setup.md), then:
uv run python -c "from codeatlas.db.migrate import upgrade_head; from codeatlas.db.session import migrator_engine; e=migrator_engine(); upgrade_head(e); e.dispose()"
npm --prefix frontend install
uv run poe check # ruff + mypy --strict + pytest: everything green?# Local path or any URL git can clone. Deterministic half only — no agent, no cost.
uv run codeatlas run --repo https://github.com/sharkdp/fd --repository-id sharkdp/fd --workdir var
# Add the narrated explanation (agent quota; --review adds the reviewers too):
uv run codeatlas run --repo <path-or-url> --repository-id owner/name --narrate [--review] [--replay] [--max-tokens N]
# Opt-ins that cost extra quota, on both `run` and `review-pr`:
# --refresh-threat-model rebuild this repository's cached threat model
# --attack-paths-for-rejected receipt dismissed security candidates too
# --model skill_id=model_id route one skill to another model (repeatable)The skill catalog is readable without a database or a run:
uv run codeatlas skills # every skill: version, trust, evals, owner
uv run codeatlas skills reviewer-security # one full governance card
uv run codeatlas skills --check # governance gate: nonzero if anything drifteduv run codeatlas serve --workdir var --port 8137 --ask # --ask enables the question box
$env:CODEATLAS_API = "http://127.0.0.1:8137"
npm --prefix frontend run preview # then open http://localhost:4173The server is read-only until told otherwise. --allow opts in to mutating capabilities,
a closed vocabulary where a typo is a startup error (ADR-0023):
uv run codeatlas serve --workdir var --port 8137 --allow ask,analyze,resume
# + agent-runs: launched runs may review/narrate (spends real quota)
# + review-pr: PR reviews of allowlisted slugs — requires --github-repo <owner/name>
# per slug, and refuses to start on a non-loopback --hostWith analyze on, the dashboard's + analyze button re-runs any repository this
workdir already knows — never a free-text clone URL: analysis executes the target's
build scripts, so adding a new repository stays a CLI act on the machine that will run
the code. Runs execute in a subprocess (restarting serve does not kill a twenty-minute
analysis), progress streams into the run detail page, and the cancel button works even
for runs started from a terminal. One run at a time, globally: the LangGraph checkpointer
is a single SQLite file, and the limit is physics until that moves to Postgres.
Approvals never grew buttons. Deciding records a human name, the API has no
authentication, and a button would record decidedBy as whatever an anonymous client
claimed — so the review tab prints copyable commands for approve, reject and publish
instead, each with its preconditions.
uv run codeatlas walkthrough <run-id> # into the run's artifacts directory
uv run codeatlas walkthrough <run-id> --out .\share # or wherever you want themOne self-contained HTML per module topic. On demand rather than on every run: most runs are never walked through, and writing files nobody asked for is not a side effect an analysis should have.
uv run codeatlas review-pr owner/repo 42 # analyzes base AND head, prepares the payload
uv run codeatlas status <run-id>
uv run codeatlas compare <run-id> <run-id> # exits nonzero if two runs are not reproducible
# the same verdict lives at #/compare in the dashboard, differences apart from notesuv run codeatlas request-approval <run-id>
uv run codeatlas show-approval <approval-id> # read the exact payload; prints the --payload value
$env:CODEATLAS_PUBLISH_ENABLED = "1" # default off — publication is an explicit act
uv run codeatlas approve <approval-id> --by "<you>" --payload <12-char-sha> [--note "..."] --publish
# or the two-step flow:
uv run codeatlas approve <approval-id> --by "<you>" --payload <12-char-sha>
uv run codeatlas publish <approval-id>--payload is proof of reading: the value only exists in show-approval output or the
dashboard, so you cannot approve bytes you have not looked at. What posts is a real PR
review — inline comments anchored on lines the diff added (findings outside the diff fold
into the body with permalinks), every byte carrying the AI-provenance marker the gate
enforces, findings already posted by an earlier run deduplicated by that same marker. The
posting path itself is live-tested (-m github_live, see docs/runbooks/github-access.md).
CODEATLAS_KILL_SWITCH=1 stops every agent invocation and every mutation — publication,
API-launched runs, resumes — everywhere, immediately. The one exception is cancel: a stop
button the stop switch disables would be absurd (ADR-0023). Database overrides: CODEATLAS_DB_URL / CODEATLAS_DB_HOST /
CODEATLAS_DB_PORT (and CODEATLAS_TEST_DB_URL for the test database).
flowchart LR
A["🔬 <b>analyze</b><br/>codeatlas run · + analyze"] --> W["📡 <b>watch</b><br/>live stage bar"]
W --> X["🗺️ <b>explore</b><br/>eleven tabs · Ctrl-K"]
X --> ASK["💬 <b>ask</b><br/>cited answers"]
X --> WT["📦 <b>walkthrough</b><br/>one file to hand over"]
X --> PR{"pull-request<br/>run?"}
PR -- yes --> PAY["🧾 <b>payload</b><br/>shadow mode · nothing posted"]
PAY --> APR["🔏 <b>approve</b><br/>CLI · a named human"]
APR --> PUB["🚀 <b>publish</b><br/>the gate re-checks everything"]
Everything below exists in two places: a CLI command and (except approvals) a dashboard surface.
Note
Exit codes are consistent everywhere: 0 success · 1 the operation ran and
refused or failed · 2 your arguments named something that does not exist.
| Feature | CLI | Dashboard | Needs | |
|---|---|---|---|---|
| 🔬 | Analyze a repository | codeatlas run |
+ analyze (sidebar) → repository mode |
--allow analyze (+ agent-runs for review/narrate) |
| 🔀 | Review a pull request | codeatlas review-pr |
+ analyze → pull request mode |
--allow review-pr + --github-repo, PAT in keyring |
| 📡 | Watch progress / status | codeatlas status |
run header badge + run detail tab (live, 1.5 s poll) | — |
| ⏹️ | Cancel a run | (no CLI verb — see below) | cancel in the run header | --allow analyze |
| Resume a failed run | codeatlas resume |
resume (no agents) in the run header | --allow resume |
|
| ⚖️ | Compare two runs | codeatlas compare |
compare (sidebar header) → #/compare |
— |
| 📦 | Export walkthroughs | codeatlas walkthrough |
walkthrough export panel on the run detail tab | — |
| 💬 | Ask about code | — | ask panel on any module page | --ask (alias --allow ask), claude CLI |
| 🛡️ | Skill governance | codeatlas skills |
skills (sidebar header) → #/skills |
— |
| 🧾 | Request approval | codeatlas request-approval |
(CLI only — the UI prints the command) | a PR run with a payload |
| 🔏 | Approve / reject | codeatlas approve / reject |
(CLI only — copy buttons on the review tab) | --payload proof of reading |
| 🚀 | Publish | codeatlas publish |
(CLI only — copy button on the review tab) | CODEATLAS_PUBLISH_ENABLED=1, PAT |
CLI. codeatlas run --repo <path-or-url> --repository-id <owner/name> mirrors the
source, pins a revision, and runs the deterministic half: extraction, graph, overview,
architecture, diagrams, exports.
| Flag | Does | Costs |
|---|---|---|
--ref <ref> |
analyze a branch, tag or sha instead of HEAD |
— |
--review |
reviewers, validator, threat model, attack paths | agent quota |
--narrate / --no-narrate |
the cited project explanation — tri-state: unset follows --review, so --narrate alone describes without reviewing |
agent quota |
--replay |
answer every agent call from recorded cassettes; refuses --model (cassettes are model-blind) |
free |
--max-tokens N |
the run-wide agent budget (default 2,000,000) | — |
--refresh-threat-model |
rebuild the repository's cached threat model | agent quota |
--attack-paths-for-rejected |
receipt dismissed security candidates too (why no path exists) | one call per dismissal |
--model skill=model |
route one skill to another model (repeatable); the ledger records who answered | varies |
Dashboard. + analyze at the top of the run list opens #/new. Pick a repository
this workdir already knows, set the ref, toggle review, pick narrate from the
tri-state. Submitting navigates straight to the new run; the overview shows the live
stage bar until there is an overview to show. The run executes in a subprocess —
restarting serve does not kill it.
Warning
The form has no URL field, deliberately. Analyzing a repository executes its
build scripts (rust-analyzer and cargo run in the checkout, build.rs and all),
so introducing a new repository is a CLI act on the machine that will run the code.
The dashboard only re-analyzes what you already chose to trust (ADR-0023).
The red cancel in the run header, while a run is created or running. It records
a durable cancel_requested event first and terminates the executor's whole tool tree
second — so it also stops runs started from a terminal (they check the event at every
stage boundary), and it still works if the server restarted and lost the process
handle. There is deliberately no CLI cancel verb: in a terminal, Ctrl+C
is the cancel, and the reaper records what happened.
codeatlas resume <run-id>, or the header button on a failed or cancelled run.
Resumes from the last LangGraph checkpoint — the interrupted stage re-runs from
scratch, finished stages do not. Both paths rebuild without an agent engine, which is
why the button says no agents: a resumed run re-runs its deterministic stages only.
codeatlas status <run-id> prints the status word. The dashboard does better: the
run detail tab shows a 15-row pipeline bar — every stage pending, running,
succeeded or failed with its duration and, on failure, the actual error — derived
from the same event rows the pipeline commits as it goes, polled at 1.5 s while the run
is active. A run whose server stopped responding says so instead of spinning.
stateDiagram-v2
[*] --> created: codeatlas run · POST /api/runs
created --> running: source_lock pins a revision
running --> succeeded
running --> succeeded_with_gaps: a reviewer failed — the status says so
running --> failed
running --> cancelled: a human asked, the event is honored
running --> paused_for_approval: a payload awaits a human
failed --> running: resume (checkpoint, no agents)
cancelled --> running: resume (checkpoint, no agents)
paused_for_approval --> succeeded: published
CLI. codeatlas review-pr owner/repo 42 fetches the PR (read-only, PAT from the
OS keyring), refuses drafts and closed PRs unless --force, analyzes both
revisions, runs the full review, and stops at a payload: shadow mode, nothing posted.
All the run cost flags apply; --narrate defaults on.
Dashboard. + analyze → pull request mode: slug + PR number, cost stated
before you click. The server only accepts slugs named in its --github-repo
allowlist — the refusal names what is allowed — and there is no force option: forcing
a draft review stays a deliberate CLI act. The base and head shas are resolved when you
submit, so the shas you saw are the shas analyzed.
Open any run from the sidebar; tabs left to right:
| Tab | What it answers | |
|---|---|---|
| 🧭 | overview | What is this project? Counts, packages, levels, cycles, most-changed modules, the cited narrative, call flows, where to start reading |
| 🗂️ | files | The full git tree at the revision; measured files open module pages, everything else opens as pinned source |
| 🗺️ | map | Packages → one package's levelized modules → the dependency matrix → path-finding ("how does A reach B"). On a PR run: changed-node halos, matrix markers, a diff-side focus |
| 🏛️ | architecture | C4 context/containers derived from the graph, plus the Structurizr DSL |
| 📜 | decisions | The repo's ADRs audited against the code: conformed / drifting / unverifiable, claims checked verbatim, the citation mesh both ways |
| 🛡️ | threats | Trust boundaries, assets, attacker capabilities and non-capabilities, numbered abuse paths with their controls re-measured |
| 📻 | protocol | What the project speaks, usually an evidenced "nothing" |
| 🔀 | change | PR runs: the structural delta, the public-API delta with semver severity, the bounded impact set, docs drift |
| 🐛 | findings | Validated findings with rationale, confidence, attack paths, source drill-down |
| ⚖️ | review | The candidate → validated funnel including what died, reviewer coverage (what was actually read), the payload preview, and the approval/publication panel |
| 🧾 | run detail | The manifest (openable per output), the live pipeline bar, walkthrough downloads, the agent ledger with per-call cost, every extractor receipt |
Not a tab but everywhere: click any module name to land on its page (definitions ranked by fan-in, usage, imports, flows, its slice of the change) — and Ctrl+K searches everything the graph measured from any tab.
On any module page, the ask panel takes one question about the module on screen; every
sentence of the answer is citation-checked against the revision and the dropped claims
are disclosed. Answers are cached by (revision, module, question) — repeats are free,
and prior questions reopen with one click. Each definition also carries an explain?
button that asks for you. Needs the server started with --ask and a logged-in
claude CLI; the 403 you get otherwise tells you exactly that.
codeatlas compare <left> <right> or the compare link in the sidebar header
(#/compare — linkable, and the run detail page prefills it via compare with…). Two
runs at the same revision must produce the same graph hash and finding ledger; the page
preselects the two most recent runs sharing a revision. Differences decide the
REPRODUCIBLE / NOT REPRODUCIBLE verdict; token and cost drift render in a separate
notes panel because they deliberately do not.
codeatlas walkthrough <run-id> [--out DIR] writes one self-contained HTML file per
package — or download the same bytes (a test pins the equality) from the walkthrough
export panel on the run detail tab. Attach it to a PR; it opens with no server, no
network, no build step. If the server has no diagram renderer, the panel says so up
front rather than letting you discover it after saving.
uv run codeatlas request-approval <run-id> # opens the approval, pauses the run
uv run codeatlas show-approval <approval-id> # the exact bytes + the --payload value
uv run codeatlas approve <approval-id> --by "<you>" --payload <12-char-sha> [--note "…"]
uv run codeatlas reject <approval-id> --by "<you>" [--note "…"]
$env:CODEATLAS_PUBLISH_ENABLED = "1"
uv run codeatlas publish <approval-id> # or: approve … --publish in one stepThe review tab shows the whole state — undecided / approved / rejected / published, with the payload preview — and offers copy buttons for all three commands, each carrying its preconditions.
Important
The dashboard will never offer buttons that decide. The API has no authentication,
so a button would record decidedBy as whatever an anonymous client claimed — and
the audit trail's entire value is the name in it. --payload is proof of reading:
the 12-character value exists only where the payload is displayed, so you cannot
approve bytes you have not looked at. A decided approval cannot be flipped.
Publishing re-checks everything at post time, because reaching a code path is not evidence of permission:
flowchart TD
P["codeatlas publish <id>"] --> K{"kill switch unset?"}
K -- no --> R["⛔ refused — the attempt itself is recorded"]
K -- yes --> O{"not already published?"}
O -- no --> I["↩️ idempotent: the existing record returns, nothing reposts"]
O -- yes --> D{"decision is approved?"}
D -- no --> R
D -- yes --> E{"CODEATLAS_PUBLISH_ENABLED=1?"}
E -- no --> R
E -- yes --> S{"secret scan clean?"}
S -- no --> R
S -- yes --> V{"AI-provenance marker on every byte?"}
V -- no --> R
V -- yes --> GH["🚀 exactly one PR review posts to GitHub"]
codeatlas skills lists the twelve governed skills (no database needed);
codeatlas skills <id> prints one full card; codeatlas skills --check is the
build-breaking governance gate. The same catalog renders at #/skills — owner,
permission envelope, credentials, risks, eval coverage — with any governance problems
listed first, because a partial catalog must say it is partial.
codeatlas serve is read-only by default. Everything mutating is a launch-time opt-in
with a closed vocabulary — a typo is a startup error, never a silently absent
capability:
serve --allow … |
Unlocks | Spends |
|---|---|---|
| (nothing) | every read: all tabs, compare, walkthrough downloads | nothing |
ask (alias --ask) |
💬 the question box | agent quota per question |
analyze |
🔬 + analyze + ⏹️ cancel |
CPU only |
resume |
CPU only | |
agent-runs |
review / narrate toggles on launched runs | agent quota |
review-pr |
🔀 pull-request mode — also needs --github-repo <owner/name> per slug, and refuses to start on a non-loopback host |
PAT (read-only) + agent quota |
The API self-documents at /api/docs, and every run's understanding is exportable as
llms.txt (/api/runs/{id}/artifact/llms-txt) for other tools. One hidden CLI verb
exists — execute-run, what the dashboard's launcher spawns; humans use run.
Important
CODEATLAS_KILL_SWITCH=1 stops every agent invocation and every mutation —
publication, API-launched runs, resumes — immediately, everywhere. The one exception
is cancel: a stop button the stop switch disables would be absurd (ADR-0023).
flowchart LR
subgraph acquire["📥 acquire"]
source_lock --> extract --> build_graph --> base_revision
end
subgraph change["🔀 change (PR runs; no-ops otherwise)"]
api_change --> graph_diff --> change_impact --> docs_drift
end
subgraph comprehend["🧭 comprehend"]
project_overview --> architecture --> narrate
end
subgraph deliver["📤 deliver"]
export_llms --> export_cytoscape --> review --> finalize
end
base_revision --> api_change
docs_drift --> project_overview
narrate --> export_llms
(Fifteen nodes — over this project's own ten-node diagram budget, and drawn anyway with its numbers stated, which is exactly what the budget rule says to do.)
(api_change runs before graph_diff on purpose: the diff's interface labels need to know
which symbols the public-API delta named, and "changed but not exported" is only
expressible once that delta exists. docs_drift is last of the change stages: it consumes
the diff and the API delta and nothing consumes it, which is what advisory means
structurally. project_overview, architecture and narrate are the deterministic half
of the comprehension features — the overview, files, map, architecture, decisions, protocol
and flows views all come from them. The threats tab comes from the review half, where the
threat model is built before the reviewers so its focus paths can aim them. The four change
stages return immediately on a whole-repository run: there is no diff, so they publish
nothing rather than publishing something empty.)
source_lock pins the revisions under analysis and, in pull-request mode, resolves the base
and derives the changed-path and added-line sets from the mirror. base_revision analyzes
the revision the change is measured against; it is a no-op for a whole-repository run, and
reuses an already-analyzed base when the extractor toolchain that produced it still matches
(ADR-0013). Each graph is stored as a snapshot with an explicit role of base or head —
"the run's graph" is not a well-formed question once a run holds two.
graph_diff compares the two graphs: which symbols and which relationships the change
added, removed or moved. That is the signal a text diff cannot give — storage now imports
api appears in the diff of neither file. Comparison runs on identities with the package
version stripped out, because a symbol id embeds it and a release bump would otherwise read
as the whole crate being rewritten; the version change is reported as its own fact.
api_change answers "what did this crate expose before, and what does it expose now"
without a model: cargo public-api renders the public surface of each revision and the
difference is arithmetic, then cargo-semver-checks classifies the severity. A package that
could not be measured on both sides is listed in skipped with the reason, never reported as
unchanged — "no API change" and "nothing was measured" must not look alike.
change_impact walks the dependency edges backwards from the symbols the change modified or
removed. One hop by default and two at the most: static change-impact precision is around
38–50%, so an unbounded closure reaches nearly everything while being right about nearly
none of it. Results are ranked (public API, crate-crossing, internal, test-only), the surplus
past the report limit is counted rather than dropped, and the precision caveat is a field of
the artifact so nothing can render the list without it.
The review stage then explains the change before reviewing it, because a reviewer needs to
know what a change does before being told what might be wrong with it. The explanation is
the one artifact a model writes, and what makes it usable is not the model's care but the
validator behind it: each claim must cite something this run measured, and a claim whose
citations do not resolve is removed and listed in droppedClaims. A softened false claim is
still a false claim, and it keeps the authority of appearing in the report.
The stage also builds (or reuses) the repository threat model first, so its focus paths can aim the reviewers, and traces an attack path for each validated security finding after validation — both cached and fail-open, so a review is never blocked on them (ADR-0017).
docs_drift needs no model at all: it takes the entities the structural diff already
named, reads every tracked document at head, and reports where the two disagree — advisory
findings a human decides on, never an edit and never a gate (ADR-0021).
Normalized, schema-validated evidence artifacts are the interfaces between pipeline stages:
facts (extractor receipts) → agent inferences (candidate findings) → validated findings →
presentation artifacts. No stage may launder inference into fact — the JSON Schemas in
schemas/ are the source of truth for every contract.
| Path | Purpose |
|---|---|
schemas/ |
Versioned JSON Schemas (Draft 2020-12) — canonical contracts |
src/codeatlas/ |
Python backend (extractors, pipeline, validation, API, CLI) |
frontend/ |
React read-only dashboard (Vite + TypeScript) |
src/codeatlas/walkthrough/ |
The single-file HTML export — no server, no network, no build |
.agents/skills/ |
Trusted, pinned skill registry + skill definitions |
.agents/cards/ |
Per-skill governance cards (skill-card.v1), rendered to docs/skill-cards.md |
docs/adr/ |
Architecture decision records (MADR) |
docs/runbooks/ |
Setup, operations, rollback |
docs/screenshots/ |
The images in this README |
fixtures/ |
Deliberately-flawed and clean Rust fixture crates for evaluation |
tests/ |
unit / integration / e2e / security / regression + cassettes + golden files |
scripts/ |
verify_env.py tool-matrix probe, live-integration validators, dev helpers |
infra/ |
Install/validate scripts, receipts, DB init |
uv run poe check is the fast gate (ruff + mypy --strict + pytest); uv run poe check-all
is the release gate — both halves including the Playwright e2e suites. Markers gate what
needs external capability: subproc (git/cargo/rust-analyzer), pg (local PostgreSQL),
agent_live (logged-in claude CLI). The live Playwright suite additionally needs a served
run: CODEATLAS_RUN=<id> npm run e2e. One spec deliberately never touches the server:
walkthrough.spec.ts opens the exported HTML over file:// and fails if the page requests
anything at all, because "works with nothing running" is a claim only a browser can check.
External integrations are also validated directly against the live service before anything depends on them, since a fixture cannot prove that authentication, ref fetching or caching work against the real thing:
uv run python scripts/validate_github.py # GitHub read paths and refusals
uv run python scripts/validate_two_revisions.py owner/repo N # both revisions, live PR
uv run python scripts/check_real_project.py --repo <url> # every analysis, real crateThe last one is the important one for anything graph-shaped: a levelization that collapses,
an impact set that reaches everything, or a view that is a hairball only show up at real
size. scripts/show_overview.py and scripts/show_views.py print the results for a human
to look at — and the standing rule here is that a green suite is not evidence the
presentation is right; serving a real project and looking is.


















