Repository navigation
fix(analytics): scan registry-mounted router modules outside api/ (#12946) - #12947
Merged
Merged
Conversation
…2946) The scan walked backend_dir/'api' only, but the registries also mount routers from sibling packages -- services.advanced_workflow.routes, routers.* and friends. Their routes were never discovered, so every frontend call to one was reported as a missing endpoint. The prefix has to be carried explicitly: _get_module_prefix() resolves a file via its path relative to project_root and then py_file.stem, and the stem of services/advanced_workflow/routes.py is 'routes', which matches nothing. Feeding these files into the existing loop alone would have found the routes under /api instead of /api/advanced-workflow. Limited to module files. A registry entry naming a package cannot have its prefix applied to the modules inside it: llc registers as ('llc.api', '', ...) and its real /api/llc/* paths come from include_router calls in the package's own __init__.py. Applying that empty prefix per-submodule produced /api/costs/... instead of /api/llc/costs/..., inventing 182 endpoints of which 4 were real -- worse than missing them, since they resurface as phantom orphaned findings. Packages are left to #12945. Measured on indexed source c5939a51 against app.openapi(): 6 external files, endpoints 1986 -> 2041, real routes matched 1736 -> 1789, routes missed 423 -> 370, and scanned-but-not-real held at exactly 70.
Contributor
✅ SSOT Configuration Compliance: Passing🎉 No hardcoded values detected that have SSOT config equivalents! |
Applied Black, isort, and autoflake to match code-quality checks. Triggered by workflow auto-fix-formatting.yml.
mrveiss
added a commit
that referenced
this pull request
Jul 29, 2026
…n APIRouter prefix (#12945) (#12950) * fix(analytics): resolve registry-mounted package routers via their own APIRouter prefix (#12945) #12947 covered registry entries naming a module file; entries naming a package were skipped because applying the registry prefix per-submodule invented endpoints. LLC registers as ('llc.api', '', ...) -- an empty prefix -- while its real paths come from the package router: llc/api/__init__.py: router = APIRouter(prefix='/llc') llc/api/costs.py: router = APIRouter(prefix='/costs') so a submodule serves /api + /llc + /costs. The package-level prefix is now read from its __init__.py and sits between the registry prefix and the submodule prefix _scan_file already applies. Submodules are included only when the package actually mounts routers (include_router present) and the submodule declares one, so helper modules contribute nothing rather than phantom endpoints. Measured on indexed source c5939a51 against app.openapi(): external files 6 -> 28, endpoints 2041 -> 2160, real routes matched 1789 (82.9%) -> 1885 (87.3%), routes missed 370 -> 274, scanned-but-not-real 70 -> 71. * Auto-fix: code formatting Applied Black, isort, and autoflake to match code-quality checks. Triggered by workflow auto-fix-formatting.yml. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #12946. Refs #12945, which stays open for the package case.
Thinking Path
#12945 measured that 419 of 439 unique "missing endpoint" findings (95.4%) are routes that genuinely exist, and that all 419 are scan gaps rather than matcher bugs. The cause is that
scan_all_endpoints()walksbackend_dir / "api"only, while the registries mount seven router modules from sibling packages.Feeding those files into the existing loop is not enough on its own.
_get_module_prefix()resolves a file by its path relative toproject_rootand then bypy_file.stem— and the stem ofservices/advanced_workflow/routes.pyisroutes, which matches nothing. The routes would have been discovered under/apiinstead of/api/advanced-workflow, i.e. still wrong, just wrong in a new way. The registry prefix is therefore carried to the resolved file explicitly.The package case turned out to be actively harmful, and that shaped the scope. My first cut expanded a registry entry naming a package by walking its
*.pyfiles and applying the package's prefix to each. Measured against the live instance, that made things worse:LLC registers as
("llc.api", "", ["llc"], "llc")— an empty prefix — because its real/api/llc/*paths come frominclude_routercalls inside the package's own__init__.py. Applying that per-submodule produced/api/costs/by-agent-modelinstead of/api/llc/costs/by-agent-model: 182 endpoints of which 4 were real. Inventing 178 endpoints is worse than missing them, since they come back as phantom "orphaned" findings — the same class of noise this whole thread is trying to remove. So this change is limited to module files, where the registry prefix is unambiguous, and packages are left to #12945 with the include_router walk they need.What Changed
api/codebase_analytics/api_endpoint_scanner.py:_registry_router_files()resolves registry module paths (dotted, non-api.) against the backend directory and returns{file: prefix}for those that resolve to a module file._external_router_prefixesis consulted first in_get_module_prefix(), so the registered prefix reaches the endpoint path.scan_all_endpoints()scans those files alongsideapi/**, and logs how many lie outsideapi/._get_module_prefix()no longer raises on a file outsideproject_root—relative_towas unguarded, and_scan_fileswallows exceptions, so such a file would have silently contributed zero endpoints.api_endpoint_scanner_test.py: 5 tests — external module resolved with its prefix, the prefix reaching the scanned path end-to-end,api.*entries not duplicated, package entries skipped (with the LLC numbers as the rationale), and a missing module file not breaking the scan.Verification
Measured on the live install's indexed source
c5939a51-…, read-only, against the authoritativeapp.openapi()table (2159 unique normalized paths):api/53 real routes recovered with no new spurious endpoints — the scanned-but-not-real count is unchanged, which is the guard against the failure mode described above.
What remains (#12945)
The false-positive share moves only 95.4% → 94.8%, because the bulk of the remaining 366 sits behind the package case — LLC alone is ~182 routes. Closing that needs the
include_routerwalk to resolve per-submodule prefixes, which is a distinct piece of work and is why this PR does not claim #12945.Model Used
claude-opus-5