Skip to content

Cartograph CLI v0.1.0 ​

Milestone M09 · the first public developer-facing release.

Cartograph derives a symbol-level, cross-language graph from source code and shows the evidence behind every edge. v0.1.0 turns the validated engine into a CLI an external developer can install and use.

This is 0.1.0, not 1.0. The scope is one supported subset of one problem, measured and documented rather than claimed.


What it does ​

console
$ cartograph trace enqueueConnectionTest --path ~/airflow

enqueueConnectionTest  (function)  airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts:834
  │
  ↓ HTTP-CALL   confidence 0.98   provenance route-matcher
  │   evidence   POST /api/v2/connections/enqueue-test matched POST /api/v2/connections/enqueue-test
  │              at .../routes/public/connections.py:383 (enqueue_connection_test); exact HTTP method;
  │              every path segment static and equal
  │   observed   airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts:834
  │
enqueue_connection_test  (function)  airflow-core/src/airflow/api_fastapi/core_api/routes/public/connections.py:383
  │
  ↓ ORM-ACCESS   confidence 0.80   provenance orm-resolution
  │   evidence   ConnectionTestRequest(...) in enqueue_connection_test at .../connections.py:427
  │
ConnectionTestRequest  (class)  airflow-core/src/airflow/models/connection_test.py:82
  │
  ↓ QUERIES   confidence 0.80   provenance orm-resolution
  │   evidence   ConnectionTestRequest maps to table "connection_test_request"; __tablename__ = "connection_test_request"
  │
connection_test_request  (table)

This path crosses the HTTP boundary and reaches a database table.

Four artefacts, three languages, no shared symbol table. Every hop is a computed relationship carrying its own evidence and source locations, and each one in the example above was verified by hand against Apache Airflow at commit 9b43d6a.

(Directory paths are shortened and the per-hop observed line is omitted so the block fits the page; everything else is verbatim.)


Commands ​

CommandWhat it answers
cartograph .Can I analyse this repository, and what did it find?
cartograph trace <symbol>Why does Cartograph believe this relationship exists?
cartograph parseWhat do the files say? (syntactic facts)
cartograph normalizeWhat is each observation's canonical form?
cartograph matchWhat was decided, and what was refused?
cartograph versionWhich build is this?

--json on any of them emits a versioned document. Full reference: docs/development/cli.md.


Installation ​

From source — works everywhere the toolchain does ​

bash
cargo install --git https://github.com/Rexy-5097/cartograph cartograph-cli

or, in a clone:

bash
cargo build --release -p cartograph-cli
./target/release/cartograph .

Rust 1.97.1, pinned in rust-toolchain.toml; rustup selects it automatically.

npm — a launcher, not a reimplementation ​

bash
npx cartograph-cli .

The npm/ package finds the native binary and forwards argv, stdio and the exit code. No analysis happens in JavaScript. It never touches the network at run time. See npm/README.md.

Prebuilt binaries ​

.github/workflows/release.yml builds a native binary for each target below on that target's own operating system, smoke-tests it there — version, a real analysis, JSON conformance, trace, and exit codes 2/3/5 — and attaches it to the GitHub release.

PlatformTarget triple
macOS Apple Siliconaarch64-apple-darwin
macOS Intelx86_64-apple-darwin
Linux x64x86_64-unknown-linux-gnu
Windows x64x86_64-pc-windows-msvc

Verification status at the time of writing. aarch64-apple-darwin is built and fully exercised locally, and the release binary is built and smoke-tested on ubuntu-latest and macos-14 by CI on every pull request. The Intel-macOS and Windows targets are configured but not yet produced — the release workflow runs on tag push, and no v* tag exists. Until it does, this project does not claim those binaries exist. Build from source there.


Exit codes ​

CodeMeaning
0Success. Diagnostics may still have been reported.
2Usage error.
3Path missing, unreadable, or holding no supported source.
4Analysis failed.
5trace: symbol not found.
6trace: symbol ambiguous; nothing was chosen.
7Partial analysis, with --strict.

1 is unused, so an unexpected 1 is always a defect.


Performance ​

Release build, Apple M-series, cold page cache, single run.

RepositoryFilesWallPeak RSS
startup (version)—1.9 ms—
fixture corpus194.2 ms—
full-stack-fastapi1560.12 s9 MB
zulip1,5393.20 s79 MB
autogpt3,7314.96 s106 MB
onyx5,7476.70 s121 MB
superset6,0637.63 s147 MB
airflow8,65414.37 s209 MB
posthog32,33564.42 s920 MB

Memory grows with repository size because the whole analysis is held in memory at once — dynamic URL resolution needs every module's exported constants before any URL can be resolved. This is the known scaling limit, and M09 did not address it. Incremental analysis is M10's goal; recording the baseline here is what makes that milestone measurable.

Correction (M17 traceability review). The file counts above match every committed M07 run of the same pinned corpus. The startup, wall-time and peak-memory figures come from the single run described above, and no record of that run was committed, so they cannot be traced to a benchmark run. The committed M07 runs in benchmarks/results/, benchmarks/remediation/ and benchmarks/acceptance/ give PostHog between 30.0 s and 49.9 s and between 825 MB and 965 MB, depending on the pass. The table is kept as it was published.


Supported subset ​

Cartograph recovers relationships it can compute. Outside this subset it refuses rather than guesses, and a refusal is reported, not hidden.

Frontend — fetch, axios and generated OpenAPI clients with a literal or statically resolvable URL; template literals partially evaluated with unknown segments preserved as unknown. Backend — FastAPI, Flask and Django route declarations, including router prefixes composed through include_router chains. ORM — SQLAlchemy and Django models resolving to a table through __tablename__, db_table, or a shared declarative base.

benchmarks/supported-subset.json is the authoritative list, and every exclusion in it has an identifier that appears in the refusal reasons.


Accuracy, stated honestly ​

From the accepted M08 measurement over seven pinned production repositories (14,932 edges):

  • 1 false positive among 11,221 independently verified edges.
  • HttpCall recall 0.907 (117/129), OrmAccess 0.744 (1401/1883), end-to-end chain 3 of 5 — each within a scope the result names.

Correction (M17 traceability review). All three recall figures were measured in Airflow only — its generated client, its import-bound model uses, and its independently readable chains — not across the seven repositories the sentence above introduces. See the Recall section of the M08 report.

  • Confidence is an uncalibrated prior, ECE 0.18 in the direction of under-confidence.
  • 24.9% of edges could not be verified from source, concentrated where the evidence is weakest, so every accuracy figure above is an upper bound.

Full report: docs/benchmarks/m08-report.md.


Known limitations ​

  1. Memory scales with repository size. 920 MB on a 32,000-file repository (an untraced figure — see the correction under Performance). No incremental mode, no file watching, no caching between runs. (M10.)
  2. Only TypeScript, TSX and Python. No Go, Java, Ruby, C#, Rust or PHP.
  3. Only the subset above. Class-based route registration (Flask-AppBuilder @expose), SQLModel, and clients that pass an options object rather than a URL are refused by design.
  4. trace walks outward only. There is no reverse traversal ("what reaches this table?") and no blast-radius query. (M12+.)
  5. Node ids are stable within a run, not across runs. Identity is (kind, name, file).
  6. Confidence must not be thresholded as a probability. It is not calibrated; see the policy document.
  7. --strict fires only on files that could not be parsed at all, not on syntax errors, which tree-sitter recovers from.
  8. No desktop UI, no MCP server, no GitHub app, no ask, no diff. Those are later milestones and are deliberately absent rather than stubbed.

Security ​

The analyser reads source files and nothing else. It does not upload source, execute repository code, connect to a database, read environment values as part of analysis, call a language model, or make any network request. Evidence is structural — file, line, canonical route, grammar fact — never source text, and no absolute filesystem path appears in any serialised output.

cargo build runs git rev-parse once to record the commit in version; that is the only subprocess this project spawns, it is build-time rather than analysis-time, and it degrades to unknown when git is absent.

Verified by 26 adversarial cases including source containing credential-shaped text, 400 unparseable files, Unicode paths, closed pipes and maximum logging verbosity.

Released under the Apache-2.0 licence.