Split out of #12745. That issue's acceptance criterion "every finding shows caller file:line and a resolved HTTP method" is still undelivered, and #12853 (the backend-scan half) explicitly parked it: "the loss is upstream in the frontend call scanner or the API serialization layer — worth confirming which before fixing."
This issue records which, so the next person does not re-walk the dead ends.
Two hypotheses, both DISPROVED
Stating these because they are the obvious guesses and both are wrong:
- Not backend/frontend field-name drift.
CodebaseApiEndpointsPanel.vue:126 and :167 read ep.file_path / ep.line_number, which match EndpointMismatchItem exactly. The contract agrees on both sides.
- Not a drop in
_match_calls_to_endpoints. api_endpoint_scanner.py:1247 passes file_path=call.file_path straight through, and _scan_file populates it from file_path.relative_to(self.project_root).
Root cause of method: UNKNOWN (99%, 2382/2400)
api_endpoint_scanner.py:1033-1048 — after the structured _API_CALL_PATTERNS are tried, an unconditional fallback appends a call item for any line containing /api/ that the patterns did not match:
if "/api/" in line:
for path_match in _API_PATH_RE.finditer(line):
...
calls.append(FrontendAPICallItem(method="UNKNOWN", ...))
The structured patterns (api_endpoint_scanner.py:49-63) all require the method and the path literal on the same physical line — api.get('/x'), fetch('/api/x', {method:...}), useApi().get('/x').
Two properties of this codebase defeat that:
- The canonical client is
useApi().get() / ApiClient.X with the path built from getApiBase() or a constant, so the literal is frequently not adjacent to the verb.
- Prettier wraps long calls, putting the path on its own line.
Every such site falls through to the fallback and is recorded as UNKNOWN. That is consistent with the observed 99% and with _match_calls_to_endpoints (:1219) then skipping method comparison entirely for those calls — so matching degrades to path-only, which is also why method drift cannot be detected today.
Scope
- Make the scanner resolve the HTTP verb across the statement rather than the line — parse the call expression (the repo already has an AST path for the Python side; the frontend side is line-regex only).
- Keep the fallback, but mark such entries as low-confidence rather than emitting them as equal-weight findings with a fabricated
UNKNOWN method.
- Confirm the
file: "?" symptom separately: it is not reproducible from source reading. EndpointMismatchItem.file_path is always populated. The "?" default that does exist is endpoints/report.py:97 (problem.get("line_number") or problem.get("line", "?")), which is the report generator, a different surface from the findings API where the symptom was observed. Needs a live capture of the findings payload to pin down.
Done when
- The UNKNOWN ratio on a scan of this repo is a small minority, not 99%.
_match_calls_to_endpoints can compare methods for the majority of calls, so genuine method drift is detectable.
- The origin of
file: "?" is either reproduced and fixed, or shown not to occur on the current code with the evidence recorded here.
Verification note
The scanner takes an explicit project_root, so the UNKNOWN ratio can be measured against this repository directly — this does not need the live instance, unlike the #12853 backend-scan half.
Refs #12745, #12853
Split out of #12745. That issue's acceptance criterion "every finding shows caller
file:lineand a resolved HTTP method" is still undelivered, and #12853 (the backend-scan half) explicitly parked it: "the loss is upstream in the frontend call scanner or the API serialization layer — worth confirming which before fixing."This issue records which, so the next person does not re-walk the dead ends.
Two hypotheses, both DISPROVED
Stating these because they are the obvious guesses and both are wrong:
CodebaseApiEndpointsPanel.vue:126and:167readep.file_path/ep.line_number, which matchEndpointMismatchItemexactly. The contract agrees on both sides._match_calls_to_endpoints.api_endpoint_scanner.py:1247passesfile_path=call.file_pathstraight through, and_scan_filepopulates it fromfile_path.relative_to(self.project_root).Root cause of
method: UNKNOWN(99%, 2382/2400)api_endpoint_scanner.py:1033-1048— after the structured_API_CALL_PATTERNSare tried, an unconditional fallback appends a call item for any line containing/api/that the patterns did not match:The structured patterns (
api_endpoint_scanner.py:49-63) all require the method and the path literal on the same physical line —api.get('/x'),fetch('/api/x', {method:...}),useApi().get('/x').Two properties of this codebase defeat that:
useApi().get()/ApiClient.Xwith the path built fromgetApiBase()or a constant, so the literal is frequently not adjacent to the verb.Every such site falls through to the fallback and is recorded as
UNKNOWN. That is consistent with the observed 99% and with_match_calls_to_endpoints(:1219) then skipping method comparison entirely for those calls — so matching degrades to path-only, which is also why method drift cannot be detected today.Scope
UNKNOWNmethod.file: "?"symptom separately: it is not reproducible from source reading.EndpointMismatchItem.file_pathis always populated. The"?"default that does exist isendpoints/report.py:97(problem.get("line_number") or problem.get("line", "?")), which is the report generator, a different surface from the findings API where the symptom was observed. Needs a live capture of the findings payload to pin down.Done when
_match_calls_to_endpointscan compare methods for the majority of calls, so genuine method drift is detectable.file: "?"is either reproduced and fixed, or shown not to occur on the current code with the evidence recorded here.Verification note
The scanner takes an explicit
project_root, so the UNKNOWN ratio can be measured against this repository directly — this does not need the live instance, unlike the #12853 backend-scan half.Refs #12745, #12853