The resolver
cartograph-resolver turns the parser's per-file observations into evidenced, cross-language edges: a TypeScript client call to the Python handler that serves it, that handler to the model it uses, and the model to its table. It is the subsystem the project exists for. The technical writeup explains its design in full, alongside what was measured about it.
Pipeline
TypeScript ─► HTTP call observations ─► symbolic URL values ─┐
├─► canonical route ─► match ─► HttpCall
Python ────► route declarations ─► served path ──────────────┘
Python ────► model classes ─► table names ─► model uses ─► OrmAccess, Queries
TypeScript ─► component ─► hook ─► client function ─► CallEvery stage is deterministic. No language model participates (ADR-0007), and the resolver never guesses a URL, symbol, route, table or dependency (RULE 010): a value it cannot determine stays unknown (RULE 009), and evidence that is not enough produces no edge.
What is implemented
| Stage | Module | Design |
|---|---|---|
| Canonical routes | canonical.rs, normalize.rs | canonical-routes.md |
| Dynamic URL evaluation | symbolic.rs, evaluator.rs, dynamic.rs | dynamic-urls.md |
Cross-language matching, and the HttpCall edge | matching.rs, edge.rs | matching.md |
| Router prefix composition | routers.rs | routers.md |
| Import resolution | imports.rs | imports.md |
ORM resolution: OrmAccess and Queries | orm.rs | orm.md |
Client wrapper tracing: Call edges | client_trace.rs | client-tracing.md |
| Per-file cache for ORM access resolution, keyed on what each resolution read | access_cache.rs, dependencies.rs | ADR-0014 |
What the resolver reads
The parser (parser architecture) supplies the observations. On the client side, in TypeScript and TSX:
fetch(...);.get,.postand the other method calls on an object namedaxios,client,httporapi;- a request configuration object whose
urlorendpointkey holds a path — the shape a generated OpenAPI client uses. Apathkey is deliberately not read, because frontend router tables use it.
An Express- or Hono-style registration such as app.get('/x', handler) is recognised only so that it is not mistaken for a client request; it is not extracted as a route.
On the server side, in Python:
- FastAPI and Flask decorators with a literal path —
@app.get("/x"),@router.post(...),@bp.route("/x", methods=[...]); - Django URL-conf entries,
path(...)andre_path(...); APIRouter(prefix=...)declarations andinclude_router(child, prefix=...)mountings, which compose the path a route is actually served on.
URLs are constructed, not written
const BASE = '/api/v1';
const url = `${BASE}/orders/${orderId}`; // → /api/v1/orders/{unknown}A restricted abstract interpretation evaluates each client URL into a symbolic value, propagating constants through module scope and across files. A segment it cannot determine stays {unknown}: aligned against a route parameter it can still match, at a lower prior than a fully static match, and a client whose leading prefix is unknown is refused rather than aligned by guesswork. An environment variable is named and never read.
Canonical routes, and matching
Server parameter dialects — /orders/:id, /orders/{id}, /orders/<int:id> — share the shape /orders/{*}, and a path converter becomes a wildcard. A client URL never declares a parameter. Matching compares a client's canonical route against an index of served routes, requires at least one shared static segment, and produces no edge when the evidence is ambiguous.
Confidence
Each edge carries a confidence that is an uncalibrated prior selected by evidence class, not a probability. The values, what selects them, and what M08 measured about them are set out in the writeup's confidence section and calibration section.
Example output
One complete chain from the M07 corpus — Apache Airflow at its pinned commit 9b43d6abc0fc — from a generated client function to a database table:
enqueueConnectionTest client function in the generated client
│ HttpCall 0.98 route-matcher at …/openapi-gen/requests/services.gen.ts:834
▼
enqueue_connection_test handler
│ OrmAccess 0.80 orm-resolution at …/core_api/routes/public/connections.py:427
▼
ConnectionTestRequest model
│ Queries 0.80 orm-resolution at …/airflow/models/connection_test.py:82
▼
connection_test_request tableEvery edge, confidence and location above is a record in benchmarks/m08/results/dataset.json, produced by the accepted engine (cartograph-m07, 2514f77) and labelled true by M08's independent reader. The same chain, with the same confidences, is recorded in benchmarks/acceptance/m07-acceptance-evaluation.json.
Known limitations
- TypeScript, TSX and Python only. No other language is parsed.
- No JavaScript backends. Express, Hono and similar route registrations are not extracted as routes.
- No OpenAPI confirmation. A specification is not read as evidence, though the domain model has a provenance and prior reserved for it.
- ORM: SQLAlchemy and Django only, and only direct accesses inside a handler —
Model(...)andModel.objects.…. An access made through a service or repository layer is not followed;session.query(Model)andselect(Model)are not recognised; raw SQL is not linked to models; SQLModel and other ORMs are not recognised (orm.md). - Django
re_pathpatterns are recorded but not normalised, so they do not match. - Prefix composition follows named routers only. An
include_router(items.router)attribute, or a prefix applied by a project-local helper, does not compose. - Class-based views and DRF routers are not modelled, nor are deployment-time URL rewrites.
- Bounded traversal. Call following stops after four hops (
MAX_CALL_HOPS), and re-export following after four (MAX_REEXPORT_HOPS). - A client URL with an absolute host never matches, even when it addresses this service, and two services in one repository declaring the same route are reported as ambiguous rather than told apart.
- Resolution is syntactic and import-based. No language server is consulted.
The findings measured on real repositories, including those still unsolved, are in the technical writeup and m07-remediation.md.