sourcecode 2.5.12__tar.gz → 2.5.13__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. {sourcecode-2.5.12 → sourcecode-2.5.13}/CHANGELOG.md +32 -0
  2. {sourcecode-2.5.12 → sourcecode-2.5.13}/PKG-INFO +1 -1
  3. {sourcecode-2.5.12 → sourcecode-2.5.13}/pyproject.toml +1 -1
  4. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/__init__.py +1 -1
  5. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/path_filters.py +48 -0
  6. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/repository_ir.py +32 -1
  7. {sourcecode-2.5.12 → sourcecode-2.5.13}/.github/workflows/build-windows.yml +0 -0
  8. {sourcecode-2.5.12 → sourcecode-2.5.13}/.gitignore +0 -0
  9. {sourcecode-2.5.12 → sourcecode-2.5.13}/.ruff.toml +0 -0
  10. {sourcecode-2.5.12 → sourcecode-2.5.13}/CONTRIBUTING.md +0 -0
  11. {sourcecode-2.5.12 → sourcecode-2.5.13}/LICENSE +0 -0
  12. {sourcecode-2.5.12 → sourcecode-2.5.13}/README.md +0 -0
  13. {sourcecode-2.5.12 → sourcecode-2.5.13}/SECURITY.md +0 -0
  14. {sourcecode-2.5.12 → sourcecode-2.5.13}/raw +0 -0
  15. {sourcecode-2.5.12 → sourcecode-2.5.13}/scripts/compare_integration_engines.py +0 -0
  16. {sourcecode-2.5.12 → sourcecode-2.5.13}/scripts/customer_smoke_test.sh +0 -0
  17. {sourcecode-2.5.12 → sourcecode-2.5.13}/scripts/generate_jdk_exports.py +0 -0
  18. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/adaptive_scanner.py +0 -0
  19. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/archetype.py +0 -0
  20. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/architecture_analyzer.py +0 -0
  21. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/architecture_summary.py +0 -0
  22. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/ast_extractor.py +0 -0
  23. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/cache.py +0 -0
  24. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/call_surface.py +0 -0
  25. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/caller_metrics.py +0 -0
  26. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/canonical_ir.py +0 -0
  27. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/cir_graphs.py +0 -0
  28. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/classifier.py +0 -0
  29. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/cli.py +0 -0
  30. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/code_notes_analyzer.py +0 -0
  31. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/confidence_analyzer.py +0 -0
  32. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/context_cache.py +0 -0
  33. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/context_graph.py +0 -0
  34. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/context_scorer.py +0 -0
  35. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/context_summarizer.py +0 -0
  36. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/contract_model.py +0 -0
  37. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/contract_pipeline.py +0 -0
  38. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/coverage_parser.py +0 -0
  39. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/dependency_analyzer.py +0 -0
  40. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/__init__.py +0 -0
  41. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/base.py +0 -0
  42. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/csproj_parser.py +0 -0
  43. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/dart.py +0 -0
  44. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/dotnet.py +0 -0
  45. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/elixir.py +0 -0
  46. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/go.py +0 -0
  47. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/heuristic.py +0 -0
  48. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/hybrid.py +0 -0
  49. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/java.py +0 -0
  50. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/jvm_ext.py +0 -0
  51. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/nodejs.py +0 -0
  52. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/parsers.py +0 -0
  53. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/php.py +0 -0
  54. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/project.py +0 -0
  55. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/python.py +0 -0
  56. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/ruby.py +0 -0
  57. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/rust.py +0 -0
  58. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/systems.py +0 -0
  59. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/terraform.py +0 -0
  60. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/detectors/tooling.py +0 -0
  61. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/doc_analyzer.py +0 -0
  62. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/dynamic_argument_surface.py +0 -0
  63. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/endpoint_literals.py +0 -0
  64. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/endpoint_metrics.py +0 -0
  65. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/entrypoint_classifier.py +0 -0
  66. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/env_analyzer.py +0 -0
  67. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/error_schema.py +0 -0
  68. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/evidence_provider.py +0 -0
  69. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/explain.py +0 -0
  70. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/file_chunker.py +0 -0
  71. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/file_classifier.py +0 -0
  72. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/format_contract.py +0 -0
  73. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/fqn_utils.py +0 -0
  74. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/git_analyzer.py +0 -0
  75. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/graph_analyzer.py +0 -0
  76. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/graph_evidence.py +0 -0
  77. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/hibernate_strat.py +0 -0
  78. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/jdk_exports.py +0 -0
  79. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/license.py +0 -0
  80. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/mcp/__init__.py +0 -0
  81. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  82. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  83. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  84. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  85. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  86. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/mcp/orchestrator.py +0 -0
  87. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/mcp/registry.py +0 -0
  88. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/mcp/runner.py +0 -0
  89. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/mcp/server.py +0 -0
  90. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/mcp_nudge.py +0 -0
  91. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/metrics_analyzer.py +0 -0
  92. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/migrate_check.py +0 -0
  93. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/openapi_surface.py +0 -0
  94. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/output_budget.py +0 -0
  95. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/pr_comment_renderer.py +0 -0
  96. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/pr_impact.py +0 -0
  97. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/prepare_context.py +0 -0
  98. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/progress.py +0 -0
  99. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/ranking_engine.py +0 -0
  100. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/reconciliation.py +0 -0
  101. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/redactor.py +0 -0
  102. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/relevance_scorer.py +0 -0
  103. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/rename_refactor.py +0 -0
  104. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/repo_classifier.py +0 -0
  105. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/__init__.py +0 -0
  106. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/context.py +0 -0
  107. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/errors.py +0 -0
  108. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/executor.py +0 -0
  109. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/planner.py +0 -0
  110. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/query.py +0 -0
  111. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/request.py +0 -0
  112. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/resolution.py +0 -0
  113. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/result.py +0 -0
  114. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/retriever.py +0 -0
  115. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/runtime.py +0 -0
  116. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/steps.py +0 -0
  117. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  118. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/steps_graph.py +0 -0
  119. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/steps_impact.py +0 -0
  120. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/steps_intf.py +0 -0
  121. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/steps_struct.py +0 -0
  122. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  123. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/ris.py +0 -0
  124. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/runtime_classifier.py +0 -0
  125. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/scanner.py +0 -0
  126. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/schema.py +0 -0
  127. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/security_config.py +0 -0
  128. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/security_posture.py +0 -0
  129. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/semantic_analyzer.py +0 -0
  130. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/semantic_impact_engine.py +0 -0
  131. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/semantic_integration_engine.py +0 -0
  132. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/semantic_services.py +0 -0
  133. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/serializer.py +0 -0
  134. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/spring_event_topology.py +0 -0
  135. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/spring_findings.py +0 -0
  136. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/spring_impact.py +0 -0
  137. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/spring_model.py +0 -0
  138. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/spring_security_audit.py +0 -0
  139. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/spring_semantic.py +0 -0
  140. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/spring_tx_analyzer.py +0 -0
  141. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/summarizer.py +0 -0
  142. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/telemetry/__init__.py +0 -0
  143. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/telemetry/config.py +0 -0
  144. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/telemetry/consent.py +0 -0
  145. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/telemetry/events.py +0 -0
  146. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/telemetry/filters.py +0 -0
  147. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/telemetry/transport.py +0 -0
  148. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/tree_utils.py +0 -0
  149. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/type_usage_surface.py +0 -0
  150. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/validation_inference.py +0 -0
  151. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/validation_surface.py +0 -0
  152. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/version_check.py +0 -0
  153. {sourcecode-2.5.12 → sourcecode-2.5.13}/src/sourcecode/workspace.py +0 -0
  154. {sourcecode-2.5.12 → sourcecode-2.5.13}/supabase/functions/README.md +0 -0
  155. {sourcecode-2.5.12 → sourcecode-2.5.13}/supabase/functions/get-license/index.ts +0 -0
  156. {sourcecode-2.5.12 → sourcecode-2.5.13}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  157. {sourcecode-2.5.12 → sourcecode-2.5.13}/supabase/functions/telemetry/index.ts +0 -0
  158. {sourcecode-2.5.12 → sourcecode-2.5.13}/supabase/sql/license_event_ordering.sql +0 -0
  159. {sourcecode-2.5.12 → sourcecode-2.5.13}/supabase/sql/licensing_schema.sql +0 -0
  160. {sourcecode-2.5.12 → sourcecode-2.5.13}/supabase/sql/telemetry_events.sql +0 -0
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.5.13] — 2026-07-18
4
+
5
+ **Test-fixture endpoints no longer pollute the production API surface.** Post-F1 field
6
+ validation (neo4j, keycloak) found that a JAX-RS / Spring resource shipping under
7
+ `src/main` of a test-utility module — a dummy web service, an Arquillian test-harness
8
+ resource — was reported flat as production API surface with no scope qualifier. This
9
+ inflated endpoint counts and injected phantom attack surface into security exploration,
10
+ the exact task the endpoint command is meant to serve, with no warning. This is the one
11
+ defect the post-F1 review classified as blocking.
12
+
13
+ ### Fixed
14
+
15
+ - **Endpoints from test-fixture / test-utility modules are excluded from the production
16
+ surface** (`repository_ir.py`, `path_filters.py`): a new structural, name-agnostic
17
+ helper `is_test_fixture_module_path` flags a module directory segment that carries a
18
+ delimited `test` token *together* with an infrastructure word (`util(s)`, `fixture(s)`,
19
+ `support`, `framework`, `kit`, `harness`, `tools`, `helper(s)`, `mock(s)`, `stub(s)`,
20
+ `dummy`) — e.g. `server-test-utils`, `foo-testkit`, `bar-test-support` — or a known
21
+ test-module segment (`testsuite`, `integration-arquillian`, `test-framework`). Requiring
22
+ both tokens in the *same* segment keeps legitimate `utils` / `support` / `tools` packages
23
+ from ever matching. `extract_java_endpoints` traces each endpoint back to its declaring
24
+ module and, when it is a test fixture, drops it from `endpoints` / `total` /
25
+ `no_security_signal` and the security-model heuristics. The exclusion is **transparent,
26
+ not silent**: excluded entries are preserved in a `test_util_endpoints` bucket with a
27
+ `scope: "test_util"` tag, a `test_util_excluded` count, and an explanatory `notes` entry.
28
+ Measured: neo4j 18 → 14 production (4 dummy `DummyThirdPartyWebService` endpoints moved to
29
+ the bucket); keycloak 775 → 673 production (102 Arquillian/test-framework harness
30
+ endpoints excluded). Control repos unchanged (spaghetti-api 7, petclinic 17, eureka 18,
31
+ Broadleaf 225). `is_test_or_fixture_path` and the migrate-check blocking count it feeds are
32
+ deliberately untouched — the new helper is narrower and wired only into endpoint
33
+ extraction.
34
+
3
35
  ## [2.5.12] — 2026-07-18
4
36
 
5
37
  **F-1 (the Evidence Reconciliation Layer) is closed and its rule core frozen.** This release
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 2.5.12
3
+ Version: 2.5.13
4
4
  Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
5
  License-File: LICENSE
6
6
  Keywords: agents,ai,codebase,context,developer-tools,llm
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "2.5.12"
7
+ version = "2.5.13"
8
8
  description = "Persistent structural context and ultra-fast repeated analysis for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -4,4 +4,4 @@ ASK Engine is the product. ``ask`` is the canonical CLI command; ``sourcecode``
4
4
  the legacy compatibility alias and the Python/PyPI package name. See
5
5
  docs/PRODUCT_IDENTITY.md (normative)."""
6
6
 
7
- __version__ = "2.5.12"
7
+ __version__ = "2.5.13"
@@ -120,6 +120,54 @@ def is_test_or_fixture_path(path: str) -> bool:
120
120
  return False
121
121
 
122
122
 
123
+ # Infrastructure words that, combined with a delimited "test" token in the same
124
+ # directory segment, identify a module whose code is test scaffolding even though
125
+ # it lives under src/main (e.g. "server-test-utils", "foo-testkit"). Kept narrow
126
+ # on purpose: the "test" token must be present in the SAME segment, so packages
127
+ # named "utils"/"support"/"tools" alone never match.
128
+ _TEST_INFRA_TOKENS = frozenset({
129
+ "util", "utils", "fixture", "fixtures", "support",
130
+ "framework", "kit", "harness", "tools", "tool",
131
+ "helper", "helpers", "mock", "mocks", "stub", "stubs", "dummy",
132
+ })
133
+
134
+ # Glued (non-hyphenated) test-fixture module suffixes.
135
+ _TEST_INFRA_SUFFIXES = (
136
+ "testutils", "testutil", "testkit", "testfixtures", "testfixture",
137
+ "testsupport", "testframework", "testharness", "testtools",
138
+ "testhelpers", "testmocks", "teststubs",
139
+ )
140
+
141
+
142
+ def is_test_fixture_module_path(path: str) -> bool:
143
+ """Return True when *path* belongs to a test-fixture / test-utility MODULE.
144
+
145
+ Structural, name-agnostic: a directory segment must contain a hyphen/underscore
146
+ -delimited ``test`` token together with an infrastructure word (util(s),
147
+ fixture(s), support, framework, kit, harness, tools, helper(s), mock(s),
148
+ stub(s), dummy) — e.g. ``server-test-utils``, ``foo-testkit``,
149
+ ``bar-test-support`` — or match a known test-module segment outright.
150
+
151
+ This is deliberately NARROWER than :func:`is_test_or_fixture_path`: it targets
152
+ modules that ship HTTP resources, JAX-RS ``@Path`` fixtures, or dummy
153
+ controllers under ``src/main`` so they are not misreported as production API
154
+ surface. Requiring both tokens in one segment keeps legitimate ``utils`` /
155
+ ``support`` / ``tools`` packages from ever matching.
156
+ """
157
+ norm = path.replace("\\", "/").lower()
158
+ parts = norm.split("/")
159
+ for part in parts[:-1]: # skip the filename
160
+ bare = part.rstrip("/")
161
+ if bare in _TEST_MODULE_SEGMENTS:
162
+ return True
163
+ if bare.endswith(_TEST_INFRA_SUFFIXES):
164
+ return True
165
+ toks = bare.replace("_", "-").split("-")
166
+ if "test" in toks and any(t in _TEST_INFRA_TOKENS for t in toks):
167
+ return True
168
+ return False
169
+
170
+
123
171
  def is_vendor_path(path: str) -> bool:
124
172
  """Return True when *path* is inside a vendored / third-party directory.
125
173
 
@@ -5584,7 +5584,7 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
5584
5584
  """
5585
5585
  import re as _re
5586
5586
  from typing import Any as _Any
5587
- from sourcecode.path_filters import is_test_path
5587
+ from sourcecode.path_filters import is_test_path, is_test_fixture_module_path
5588
5588
 
5589
5589
  _EXTENDS_FROM_SIG = _re.compile(r'\bextends\s+(\w+)')
5590
5590
 
@@ -5753,6 +5753,16 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
5753
5753
  endpoint_warnings = _recovery["warnings"]
5754
5754
  _openapi_spec_path = _recovery["spec_path"]
5755
5755
 
5756
+ # Map each declared class/interface FQN to its source file so an endpoint can be
5757
+ # traced back to the MODULE that ships it. Used to flag endpoints that originate
5758
+ # in a test-fixture / test-utility module (dummy web services, JAX-RS @Path
5759
+ # fixtures) living under src/main — these are not production API surface.
5760
+ _fqn_to_file: dict[str, str] = {
5761
+ s.symbol: (s.source_file or s.declaring_file)
5762
+ for s in all_symbols
5763
+ if s.type in ("class", "interface")
5764
+ }
5765
+
5756
5766
  endpoints: list[dict] = []
5757
5767
  for route in routes:
5758
5768
  handler = (
@@ -5769,6 +5779,9 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
5769
5779
  "handler": handler,
5770
5780
  "return_type": route.get("return_type", "void"),
5771
5781
  }
5782
+ _src = _fqn_to_file.get(route.get("effective_class", ""), "")
5783
+ if _src and is_test_fixture_module_path(_src):
5784
+ entry["scope"] = "test_util"
5772
5785
  # Use security_annotations already extracted by _build_route_surface
5773
5786
  # via the canonical _route_security_from_sym extractor.
5774
5787
  security_info = route.get("security_annotations")
@@ -5791,6 +5804,14 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
5791
5804
  )
5792
5805
  endpoints = [e for e in endpoints if not _FQN_PATH_RE.search(e.get("path", ""))]
5793
5806
 
5807
+ # Partition test-fixture/test-utility endpoints out of the production surface.
5808
+ # They ship under src/main of a *-test-utils / *-testkit / test-framework module
5809
+ # (dummy web services, JAX-RS @Path fixtures) and must not be reported as, or
5810
+ # counted with, the real API surface — but they are preserved in their own bucket
5811
+ # so nothing is silently dropped and the exclusion is auditable.
5812
+ _test_util_endpoints = [e for e in endpoints if e.get("scope") == "test_util"]
5813
+ endpoints = [e for e in endpoints if e.get("scope") != "test_util"]
5814
+
5794
5815
  # "no_security_signal" = no recognized security annotation at method OR class level.
5795
5816
  # Note: repos may use framework-level security (e.g. Keycloak itself) with no
5796
5817
  # per-endpoint annotations — this count reflects annotation-based coverage only.
@@ -5919,6 +5940,16 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
5919
5940
  # Keep legacy field name for backward compat, now means same as no_security_signal
5920
5941
  "undocumented": no_security_signal,
5921
5942
  }
5943
+ # Surface test-fixture endpoints excluded from the production surface above.
5944
+ # Reported separately (not hidden) so the exclusion is transparent and auditable.
5945
+ if _test_util_endpoints:
5946
+ result["test_util_endpoints"] = _test_util_endpoints
5947
+ result["test_util_excluded"] = len(_test_util_endpoints)
5948
+ result.setdefault("notes", []).append(
5949
+ f"{len(_test_util_endpoints)} endpoint(s) from test-fixture/test-utility "
5950
+ f"module(s) under src/main were excluded from the production surface "
5951
+ f"(see test_util_endpoints)."
5952
+ )
5922
5953
  # Surface incomplete-endpoint warnings (interface-defined controllers) only when
5923
5954
  # present, to keep output backward-compatible for the common case.
5924
5955
  if endpoint_warnings:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes