Skip to content

M07 remediation — accuracy-first cross-stack resolution ​

Status: INTERNAL. The original M07 result is unchanged and remains the record of what the accepted engine did: m07-report.md. This document reports what the remediation changed and what it cost. Neither set of numbers is for publication.

Baseline 5cdafca on feature/m07-real-validation, itself based on cartograph-m06 (ef26ae2). Same seven repositories, same commits, same ground truth, same denominators.


The question ​

M07's validation was complete and its product question was not passed. It found six chains, none beginning at a frontend file, and named three causes: frontends call through client wrappers, router prefixes are composed at registration, and model names were resolved without consulting imports.

This remediation addresses all three. It was measured against one criterion written from source before any code changed (benchmarks/remediation/acceptance-target.json): recover the chain from Airflow's pool creation, and lose nothing.

The answer ​

Three complete chains now run from a React component to a database table, each verified line by line against source.

text
ParseDagButton.tsx:30      ──call──►  useDagParsing.ts:30
  ──call──►  queries.ts:2546          ──call──►  services.gen.ts:4538
  ──http-call──►  dag_parsing.py:32 (reparse_dag_file)
  ──orm-access──►  DagPriorityParsingRequest (dagbag.py:267)
  ──queries──►  dag_priority_parsing_request
OriginHandlerModelTable
ParseDagButton.tsx:30reparse_dag_fileDagPriorityParsingRequestdag_priority_parsing_request
AddConnectionButton.tsx:29post_connectionConnectionconnection
AddPoolButton.tsx:28post_poolPoolslot_pool

All three begin in airflow-core/src/airflow/ui/src/ — hand-written React components, not tests, not generated code, not a Python client. Every edge carries confidence, provenance, evidence and a source location; every node is the declaration it names; the path traverses through shared node ids.

Counting each component and its hook separately, six chains have a hand-written frontend origin, out of 24 complete chains in the corpus.


What the baseline actually was ​

Five of M07's six chains were false positives. They ran through providers/edge3/.../worker_api/routes/ui.py:135, where Job(...) constructs a response datamodel imported from a FastAPI models module — not airflow.jobs.job.Job, the SQLAlchemy model of the same name. Import resolution removed them.

The honest baseline is therefore one chain, not six: Onyx's create_group building a UserGroup imported from onyx.db.models. It is real, it survives, and it still does not begin at a frontend.

This correction makes M07's negative finding stronger, not weaker.


Results ​

M07 baselineRemediation
Route declarations — TP / FP / FN101 / 0 / 0101 / 0 / 0
Route precision / recall1.000 / 1.0001.000 / 1.000
Queries — TP / FP / FN48 / 0 / 048 / 0 / 0
Queries produced / source-confirmed290 / 290290 / 290
OrmAccess edges11,84311,464
…unconfirmable against source286126
HttpCall edges1,4581,737
Call edges (new)—1,445
Complete chains6 (5 false)24
…from a frontend file06
must_not_produce violations00
Edges missing an attribute00

379 OrmAccess edges were removed, every sample checked being a symbol that merely shared a model's name. That is the point: this remediation subtracted more relationships than most of its slices added.

What is still unmeasured ​

HttpCall recall, OrmAccess recall and chain recall remain without an independently established denominator, exactly as M07 reported. Nothing here changes that, and the precision figures above must not be read as if it did. Establishing those denominators is future work FW-10, still open.


What changed, and why ​

1. Import resolution ​

A name at a use site is walked through the file's own imports to a declaration — import, module, exported symbol, declaration — instead of being matched against a project-wide map. Unresolvable modules, monorepo path collisions and re-export cycles refuse with a reason. Documented in ../resolver/imports.md.

Found by: 286 unconfirmable accesses at M07. Costs: 379 accesses removed, all sampled ones verified wrong.

2. Configuration-object requests ​

__request(OpenAPI, {method, url}) and client.get({url}) are read. url and endpoint only — never path, which is how every router in the corpus describes a screen.

Found by: twelve TypeScript clients among 1,458 edges.

3. Router prefix composition ​

@pools_router.post("") on a router carrying /pools, mounted under one carrying /api/v2, serves /api/v2/pools. Any constructor whose name ends in Router counts. Documented in ../resolver/routers.md.

Found by: OUT-5 in M07's supported subset.

4. The client wrapper as a node ​

An HttpCall edge is sourced at the function issuing the request, and calls reaching that function become Call edges resolved through imports. Decided in ADR-0012.


Costs, stated plainly ​

175 Airflow HttpCall edges were lost and 151 gained. The lost ones were Python tests calling test_client.get("/assets/aliases"), where the client is built with base_url=f"{BASE_URL}{get_api_path(request)}". Before composition those matched because the route was also stored as a fragment; a fragment matching a fragment. The path is relative to a runtime-configured base, which is M07's declared SR-1 refusal category, so declining them is the behaviour the subset already specified.

A matcher rule was tried and reverted. Three Superset edges join a frontend unit test mocking /test to a backend unit test serving /test — one generic segment, no method on either side. A rule refusing that also refused /orders with no declared method, which M04 correctly accepts. Losing correct edges to remove wrong ones is not a trade this remediation will make; the three remain a known false-positive class.

Analysis is 11% slower — 68.4 s to 75.8 s across 58,225 files — and peak RSS rose from 825 MB to 965 MB on PostHog. Import resolution is memoised per (file, name); call resolution runs once over TypeScript files. Neither is optimised, and the memory trend remains future work FW-9.


Found by the acceptance audit ​

A corpus-wide false-positive audit after the remediation merged found one false positive in 1,737 HttpCall edges: PostHog's nodejs/src/common/api/router.ts:45 writes app.get('/_metrics', getMetrics) — an Express registration with a named handler — and it was matched to Django's metrics_view. The remediation had refused registrations with inline handlers and documented the named-handler case as still indistinguishable.

It is now refused too. A registration passes handlers; a request passes options. The rule applies only to a receiver that is not a known HTTP client, because Node's own http.get(url, callback) passes a function and is a real request.

The family took three passes to close, one shape at a time. The remediation refused handlers written inline. The first acceptance audit found app.get('/_metrics', getMetrics) — a named reference — and refused that. The second acceptance audit found app.get('/_health', buildGetHealth(services)) — a factory call — and halted rather than accept M07 with it standing. All three are now one rule, and one fixture asserts all three together so the family cannot be closed a third of the way again:

ShapeExampleFound by
inline functionapp.get('/x', (c) => …)M07 remediation
named referenceapp.get('/x', getMetrics)acceptance audit 1
factory callapp.get('/x', buildHandler(deps))acceptance audit 2

Each time, exactly one corpus edge matched the newly-refused shape and no true positive was lost. Zulip's four hand-written frontend requests through channel.get({url}), and a known client's http.get(url, makeCallback()), are untouched — the receiver check, not the argument shape, is what keeps the rule contextual.

Still not solved ​

#Finding
FW-1Onyx's frontend calls /api/manage/... while its backend composes /manage/...: the /api comes from a Next.js rewrites() mapping, a deployment-time mechanism
FW-2Onyx's global prefix is applied by a project-local helper, not include_router, so it does not compose
FW-3Django chains still break: the HttpCall target is a Function in the URL-conf file, the OrmAccess source a Function in the view file
FW-11Airflow's generated client declares two operations twice under different service names; ADR-0011 collapses them into one node. Both declarations are byte-identical in method and URL, so nothing is misattributed, but the alias is lost
FW-5126 OrmAccess edges remain unconfirmable — names bound by star or conditional imports, which are permitted rather than refused
FW-6Class-based views and DRF routers still carry most of PostHog's and Superset's API
FW-7session.query(Model) and select(Model) remain unsupported
FW-10HttpCall, OrmAccess and chain recall still have no independent denominator

full-stack-fastapi still produces zero edges: its ORM is SQLModel, out of scope, and its frontend's URLs are now read but its backend routes compose through include_router(items.router) — an attribute, not a plain name — which router resolution does not follow.

Released under the Apache-2.0 licence.