sourcecode 3.2.0__tar.gz → 3.2.1__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 (188) hide show
  1. {sourcecode-3.2.0 → sourcecode-3.2.1}/CHANGELOG.md +195 -0
  2. {sourcecode-3.2.0 → sourcecode-3.2.1}/PKG-INFO +100 -10
  3. {sourcecode-3.2.0 → sourcecode-3.2.1}/README.md +99 -9
  4. {sourcecode-3.2.0 → sourcecode-3.2.1}/pyproject.toml +1 -1
  5. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/__init__.py +1 -1
  6. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/chain_rules.py +49 -8
  7. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/cli.py +218 -176
  8. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/confidence_analyzer.py +10 -6
  9. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/deployment_prefix.py +85 -0
  10. sourcecode-3.2.1/src/sourcecode/facts/__init__.py +71 -0
  11. sourcecode-3.2.1/src/sourcecode/facts/registry.json +158 -0
  12. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/mcp/registry.py +1 -1
  13. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/mcp/server.py +1 -1
  14. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/metrics_analyzer.py +8 -5
  15. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/posture.py +78 -6
  16. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/prepare_context.py +44 -19
  17. sourcecode-3.2.1/src/sourcecode/reference_facts.py +307 -0
  18. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/serializer.py +36 -9
  19. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/spring_profiles.py +93 -13
  20. sourcecode-3.2.1/src/sourcecode/test_sources.py +178 -0
  21. {sourcecode-3.2.0 → sourcecode-3.2.1}/.github/workflows/build-windows.yml +0 -0
  22. {sourcecode-3.2.0 → sourcecode-3.2.1}/.gitignore +0 -0
  23. {sourcecode-3.2.0 → sourcecode-3.2.1}/.ruff.toml +0 -0
  24. {sourcecode-3.2.0 → sourcecode-3.2.1}/CONTRIBUTING.md +0 -0
  25. {sourcecode-3.2.0 → sourcecode-3.2.1}/LICENSE +0 -0
  26. {sourcecode-3.2.0 → sourcecode-3.2.1}/SECURITY.md +0 -0
  27. {sourcecode-3.2.0 → sourcecode-3.2.1}/raw +0 -0
  28. {sourcecode-3.2.0 → sourcecode-3.2.1}/scripts/compare_integration_engines.py +0 -0
  29. {sourcecode-3.2.0 → sourcecode-3.2.1}/scripts/customer_smoke_test.sh +0 -0
  30. {sourcecode-3.2.0 → sourcecode-3.2.1}/scripts/generate_jdk_exports.py +0 -0
  31. {sourcecode-3.2.0 → sourcecode-3.2.1}/scripts/perf_harness.py +0 -0
  32. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/adaptive_scanner.py +0 -0
  33. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/archetype.py +0 -0
  34. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/architectural_baseline.py +0 -0
  35. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/architectural_delta.py +0 -0
  36. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/architecture_analyzer.py +0 -0
  37. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/architecture_summary.py +0 -0
  38. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/ast_extractor.py +0 -0
  39. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/cache.py +0 -0
  40. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/call_surface.py +0 -0
  41. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/caller_metrics.py +0 -0
  42. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/canonical_ir.py +0 -0
  43. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/change_plan.py +0 -0
  44. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/cir_graphs.py +0 -0
  45. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/classifier.py +0 -0
  46. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/code_notes_analyzer.py +0 -0
  47. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/compare.py +0 -0
  48. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/constraint_diff.py +0 -0
  49. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/context_cache.py +0 -0
  50. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/context_graph.py +0 -0
  51. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/context_scorer.py +0 -0
  52. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/context_summarizer.py +0 -0
  53. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/contract_diff.py +0 -0
  54. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/contract_model.py +0 -0
  55. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/contract_pipeline.py +0 -0
  56. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/coverage_parser.py +0 -0
  57. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/dependency_analyzer.py +0 -0
  58. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/__init__.py +0 -0
  59. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/base.py +0 -0
  60. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/csproj_parser.py +0 -0
  61. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/dart.py +0 -0
  62. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/dotnet.py +0 -0
  63. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/elixir.py +0 -0
  64. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/go.py +0 -0
  65. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/heuristic.py +0 -0
  66. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/hybrid.py +0 -0
  67. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/java.py +0 -0
  68. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/jvm_ext.py +0 -0
  69. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/nodejs.py +0 -0
  70. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/parsers.py +0 -0
  71. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/php.py +0 -0
  72. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/project.py +0 -0
  73. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/python.py +0 -0
  74. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/ruby.py +0 -0
  75. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/rust.py +0 -0
  76. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/systems.py +0 -0
  77. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/terraform.py +0 -0
  78. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/detectors/tooling.py +0 -0
  79. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/doc_analyzer.py +0 -0
  80. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/dynamic_argument_surface.py +0 -0
  81. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/endpoint_literals.py +0 -0
  82. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/endpoint_metrics.py +0 -0
  83. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/entrypoint_classifier.py +0 -0
  84. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/env_analyzer.py +0 -0
  85. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/envelope.py +0 -0
  86. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/error_schema.py +0 -0
  87. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/evidence_provider.py +0 -0
  88. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/explain.py +0 -0
  89. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/file_chunker.py +0 -0
  90. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/file_classifier.py +0 -0
  91. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/filter_surface.py +0 -0
  92. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/format_contract.py +0 -0
  93. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/fqn_utils.py +0 -0
  94. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/git_analyzer.py +0 -0
  95. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/graph_analyzer.py +0 -0
  96. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/graph_evidence.py +0 -0
  97. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/hibernate_strat.py +0 -0
  98. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/integration_coordinates.py +0 -0
  99. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/jdk_exports.py +0 -0
  100. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/license.py +0 -0
  101. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/mcp/__init__.py +0 -0
  102. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  103. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  104. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  105. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  106. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  107. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/mcp/orchestrator.py +0 -0
  108. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/mcp/runner.py +0 -0
  109. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/mcp_nudge.py +0 -0
  110. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/migrate_check.py +0 -0
  111. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/migration_blast.py +0 -0
  112. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/openapi_surface.py +0 -0
  113. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/output_budget.py +0 -0
  114. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/parse_cache.py +0 -0
  115. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/path_filters.py +0 -0
  116. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/perf.py +0 -0
  117. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/pr_comment_renderer.py +0 -0
  118. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/pr_impact.py +0 -0
  119. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/progress.py +0 -0
  120. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/ranking_engine.py +0 -0
  121. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/readiness_timeline.py +0 -0
  122. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/reconciliation.py +0 -0
  123. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/redactor.py +0 -0
  124. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/relevance_scorer.py +0 -0
  125. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/rename_refactor.py +0 -0
  126. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/repo_classifier.py +0 -0
  127. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/repository_ir.py +0 -0
  128. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/__init__.py +0 -0
  129. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/context.py +0 -0
  130. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/errors.py +0 -0
  131. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/executor.py +0 -0
  132. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/planner.py +0 -0
  133. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/query.py +0 -0
  134. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/request.py +0 -0
  135. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/resolution.py +0 -0
  136. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/result.py +0 -0
  137. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/retriever.py +0 -0
  138. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/runtime.py +0 -0
  139. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/steps.py +0 -0
  140. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  141. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/steps_graph.py +0 -0
  142. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/steps_impact.py +0 -0
  143. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/steps_intf.py +0 -0
  144. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/steps_struct.py +0 -0
  145. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  146. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/ris.py +0 -0
  147. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/runtime_classifier.py +0 -0
  148. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/scanner.py +0 -0
  149. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/schema.py +0 -0
  150. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
  151. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/security_config.py +0 -0
  152. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/security_posture.py +0 -0
  153. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/semantic_analyzer.py +0 -0
  154. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/semantic_impact_engine.py +0 -0
  155. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/semantic_integration_engine.py +0 -0
  156. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/semantic_services.py +0 -0
  157. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/spring_event_topology.py +0 -0
  158. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/spring_findings.py +0 -0
  159. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/spring_impact.py +0 -0
  160. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/spring_model.py +0 -0
  161. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/spring_properties.py +0 -0
  162. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/spring_security_audit.py +0 -0
  163. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/spring_semantic.py +0 -0
  164. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/spring_tx_analyzer.py +0 -0
  165. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/summarizer.py +0 -0
  166. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/telemetry/__init__.py +0 -0
  167. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/telemetry/config.py +0 -0
  168. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/telemetry/consent.py +0 -0
  169. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/telemetry/events.py +0 -0
  170. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/telemetry/filters.py +0 -0
  171. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/telemetry/transport.py +0 -0
  172. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/token_estimate.py +0 -0
  173. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/tree_utils.py +0 -0
  174. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/type_usage_surface.py +0 -0
  175. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/validation_inference.py +0 -0
  176. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/validation_surface.py +0 -0
  177. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/verify_edit.py +0 -0
  178. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/verify_repo.py +0 -0
  179. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/verify_rules.py +0 -0
  180. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/version_check.py +0 -0
  181. {sourcecode-3.2.0 → sourcecode-3.2.1}/src/sourcecode/workspace.py +0 -0
  182. {sourcecode-3.2.0 → sourcecode-3.2.1}/supabase/functions/README.md +0 -0
  183. {sourcecode-3.2.0 → sourcecode-3.2.1}/supabase/functions/get-license/index.ts +0 -0
  184. {sourcecode-3.2.0 → sourcecode-3.2.1}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  185. {sourcecode-3.2.0 → sourcecode-3.2.1}/supabase/functions/telemetry/index.ts +0 -0
  186. {sourcecode-3.2.0 → sourcecode-3.2.1}/supabase/sql/license_event_ordering.sql +0 -0
  187. {sourcecode-3.2.0 → sourcecode-3.2.1}/supabase/sql/licensing_schema.sql +0 -0
  188. {sourcecode-3.2.0 → sourcecode-3.2.1}/supabase/sql/telemetry_events.sql +0 -0
@@ -1,5 +1,200 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.2.1] — 2026-07-29
4
+
5
+ **M7 complete — the last four seams, the fact registry, and a `--help` that leads with the
6
+ product.** Every fact ADR-0008 named now has one authority, a registered consumer list and an
7
+ assertion that keeps its emitters agreeing. Two of the four seams closed here were found by
8
+ field evaluation #3 *between the two commands it valued most*, and reproducing them showed
9
+ both were worse than reported.
10
+
11
+ **Upgrade note.** Three published numbers change value. None is a schema break; all are
12
+ corrections, and a pinned threshold may move:
13
+
14
+ - `modernize.summary.statically_unreferenced` grows sharply — the population it is measured
15
+ over never matched its own definition (keycloak 14 → 108, BroadleafCommerce 10 → 36,
16
+ alfresco 13 → 88). `summary.high_coupling_nodes` likewise stops reporting the display cap
17
+ of 20 and reports the measured count.
18
+ - `endpoints --limit N` no longer reports `total: N`. `total` and the security counters
19
+ describe the endpoints the *filters* selected; the new `shown` says how many are listed.
20
+ - `posture` verdicts change on repositories that declare `spring.mvc.servlet.path`: rules are
21
+ matched against the URL the filter chain actually receives, so endpoints previously reported
22
+ `no_rule_matched` — or, with a catch-all behind the rule, `permit_all` — now carry the
23
+ decision the matcher makes. Repositories without a declared servlet path are byte-identical.
24
+
25
+ New keys (`summary.reference_status`, `effective_access.path_basis`, `matched_path`, `shown`,
26
+ and `registries` in both `ask schema` and `ask version`) are additive.
27
+
28
+ ### Changed
29
+
30
+ - **`--help` no longer introduces the product with its weakest 20 %.** The command table was
31
+ ordered by the order commands are registered in the source, so a reader met
32
+ `prepare-context` and `repo-ir` — the two surfaces field evaluation #3 scored lowest —
33
+ before `posture` and `endpoints`, which the same evaluation called "the product". Commands
34
+ are now grouped into ordered panels, led by **Java/Spring analysis — start here**
35
+ (`posture`, `endpoints`, `spring-audit`, `migrate-check`), then *Change and risk*, *Context
36
+ for AI agents*, *Setup and inspection*, and *Experimental*. Nothing is hidden and nothing is
37
+ removed; the ordering is one table (`cli.HELP_PANELS`) and the battery fails if a registered
38
+ command is missing from it, if the table names a command that does not exist, or if the core
39
+ four stop leading the rendering.
40
+ - **The hand-written "Also available:" catalogue is gone.** A curated list beside a generated
41
+ one drifts, and it did: it is how `endpoints` shipped while appearing in no `--help` list at
42
+ all, leaving an evaluator to find the command they valued most inside another command's JSON.
43
+ - **`ask spring-audit --help` now says which gate it is.** `--ci` fails on *any* finding,
44
+ including debt that was already there — the field read it as baseline-relative and concluded
45
+ the gate was unusable on a legacy repository. The help now states the gate is absolute and
46
+ names the baseline-relative one (`ask verify --fail-on new`, `ask pr-impact --fail-on`) and
47
+ `ask baseline` for tracking the debt. A test asserts those commands and flags exist.
48
+
49
+ ### Added
50
+
51
+ - **Invariant I-3 in the battery: every registered fact declares what it says when it cannot
52
+ decide.** ADR-0008 R9 is easy to state and easy to lose — `statically_unreferenced: 0` over
53
+ 3 374 classes is what losing it costs. Each registry entry now carries `unresolved_answer`
54
+ (the outcome the fact emits when undecidable) plus the reason, or `null` with an explanation
55
+ of why the fact is total. A declared outcome must exist in the authority that derives it, so
56
+ the registry cannot document an `unknown` nothing emits — a check that immediately corrected
57
+ two entries where the unresolved answer belongs to the consumer, not the authority.
58
+
59
+ - **Invariant I-2 in the battery: a published count may not come from a truncated list.**
60
+ A static audit over every module (`tests/test_invariant_counts_from_measurement.py`): inside
61
+ one function, a name bound to a slice and then measured with `len()` in a payload position
62
+ fails the suite. Display sizes stay legal under an explicit key (`shown`, `omitted`, …),
63
+ because "how many did you show me" is a different question from "how many are there". The
64
+ audit is itself guarded against being vacuous.
65
+ - It found two live ones. `modernize.summary.high_coupling_nodes` reported the display cap
66
+ (20) as the number of high-coupling classes — a repository with 300 of them said 20; the
67
+ exact count now ships alongside `high_coupling_nodes_shown`. And `endpoints --limit N`
68
+ reported `total: N`: `--limit` cuts the rendering, not the population.
69
+ - **Value change:** with `--limit`, `endpoints` now reports `total` (and the security
70
+ counters) for the endpoints the *filters* selected, plus a new `shown` for how many are
71
+ listed. Before, all of them collapsed to the limit. Filters (`--path-prefix`,
72
+ `--controller`) genuinely change the population and still move `total`.
73
+
74
+ - **The fact registry ADR-0008 asks for (R11), shipped inside the package and enforced.**
75
+ `src/sourcecode/facts/registry.json` names every fact that has a single authority: what it
76
+ means, which `module:symbol` derives it, which modules may emit it, and the parity assertion
77
+ that keeps those emitters agreeing. Read it from an installed version with
78
+ `ask schema facts-v1` — a release states its own contract instead of pointing at
79
+ documentation that can drift from it. No new command: `ask schema` lists it under
80
+ `registries`, beside the output `schemas`.
81
+ - The registry is checked, not asserted: the battery fails if an authority does not exist,
82
+ if a registered consumer does not bind to it (R13 — the LDAP defect survived two releases
83
+ exactly because a consumer was never rebound), or if a fact carries no parity test (R12).
84
+ Writing it found two claims of mine that were untrue and one surface — the endpoint
85
+ security verdict — whose only real consumer is `impact`.
86
+ - Eleven facts registered, one per M7 seam plus the two each seam split apart.
87
+
88
+ ### Fixed
89
+
90
+ - **A repository with no tests at all reported `has_tests: true` and "N test files".** Third
91
+ field evaluation: a Spring monolith whose `src/test` does not exist was described as having
92
+ 4 test files, because production classes living in a package named `test` matched a
93
+ substring rule. The same document's `analysis_gaps` contradicted it — one document, two
94
+ derivations of one fact.
95
+ - New authority `test_sources`: a file is a test source when it sits under a **declared
96
+ test source root** (`src/test`, `src/it`, `src/androidTest`, `src/testFixtures`, or a
97
+ repository-level `tests/`, `spec/`, `__tests__/`) or matches an ecosystem **naming
98
+ convention** (`FooTest.java`, `test_foo.py`, `foo.spec.ts`, `foo_test.go`). A `test`
99
+ package **inside a main source root is production code** — the build tool's own layout
100
+ outranks any name.
101
+ - Five consumers now bind to it instead of re-deriving: `signals.has_tests`, the
102
+ `analysis_gaps` backend-coverage check (which now also states its basis),
103
+ `metrics_analyzer.is_test_file`, `prepare-context`'s test/source split, and the review-pr
104
+ coverage risk.
105
+ - **Value change:** files under a *main* source root in a directory named `test`/`tests`
106
+ stop counting as tests. Measured on the fleet: keycloak 2 220 → 1 777 Java test files
107
+ (443 files under `testsuite/…/src/main/java/…`), BroadleafCommerce 201 → 197,
108
+ spring-petclinic and openmrs-core unchanged.
109
+ - **`review-pr` reported `test_coverage_risk: "low"` for a diff containing no source files.**
110
+ In a repository with no tests, a documentation-only PR read as "covered" — the traffic light
111
+ inverted exactly where a reviewer relies on it. The surface now emits `unknown` with the
112
+ reason (`no source files changed in this diff`, or `repository declares no test sources`),
113
+ plus `changed_source_files` and `repository_has_test_sources`. `low` is now reachable only
114
+ when changed source files were checked and matched (ADR-0008 R9).
115
+
116
+ - **A class was reported as conditional on a profile *and* on its negation, contradicting
117
+ `ask posture` in the same run.** Found in the third field evaluation (3.2.0): a
118
+ configuration class declaring `@Profile("!m3")` — active exactly when `m3` is **absent** —
119
+ appeared in `signals.spring_profiles.profile_conditional_beans` under `m3` as well. Read as
120
+ a security fact, it inverts the answer.
121
+ - Root cause, reproduced: the source scan attributed every `@Profile` to *the next type
122
+ declaration ahead of it*, falling back to the file name when there was none. A
123
+ `@Profile("m3")` on a `@Bean` **method** inside that class was therefore attributed to the
124
+ class itself, next to the class-level `@Profile("!m3")`.
125
+ - Attribution is now the declaration the annotation actually annotates: only annotations
126
+ and modifiers may stand between it and a type declaration; a member-level annotation is
127
+ reported as `Type#member`, and an annotation whose target cannot be read is **not
128
+ reported at all** rather than attributed to a guess (ADR-0008 R9).
129
+ - **Value change:** member-level declarations move from the enclosing type to the member.
130
+ On the reference fleet this affects one symbol (`spring-petclinic`:
131
+ `MysqlTestApplication` → `MysqlTestApplication#container`); on repositories that put
132
+ `@Profile` on `@Bean` methods it affects each such method.
133
+ - New in the payload: `signals.spring_profiles.conditional_beans[]` with `symbol`,
134
+ `profiles`, `expression` (as written) and `source_file`. The token map
135
+ (`profile_conditional_beans`) buckets `@Profile("a & !b")` under `a` and `!b` with the
136
+ operator lost; the list keeps the expression, and both are now projections of **one**
137
+ derivation, so they cannot disagree.
138
+ - Parity assertion added: no symbol may be reported as conditional on a profile that
139
+ `posture` resolves it inactive under (invariant I-1).
140
+
141
+ - **`ask modernize` reported `statically_unreferenced: 0` across a 3 374-class monolith.**
142
+ Read as "there is no unreferenced code here", when what it meant was "a check that cannot
143
+ see framework dispatch found nothing" — the one place the product broke its own rule about
144
+ never publishing a number where the honest answer is `unknown` (ADR-0008 R9). Two causes:
145
+ - The population never matched the published definition. The note said "zero static
146
+ callers", the code examined only types with **no edges in either direction** — so a type
147
+ that calls something but is called by nothing was never a candidate at all.
148
+ - The surface had two buckets and the question has three answers.
149
+ - One authority now derives it, `reference_facts`, as a partition that sums to the classes
150
+ examined: `referenced` (an incoming edge, or another source naming it) /
151
+ `unknown_framework_dispatch` (no static caller, but a published annotation, an entry-point
152
+ signature, or the name wired from a configuration file) / `no_static_callers` (neither —
153
+ **absence of evidence, never "dead code"**). `summary.reference_status` carries the exact
154
+ counts and a `statement` that explains a zero instead of publishing it bare.
155
+ - Counts come from the measurement, not from `len()` of the lists, which are cut to 20 for
156
+ display (R5). Every listed type carries the `basis` for being there.
157
+ - Dispatch detection extended with published stereotype, persistence and entry-point
158
+ vocabulary (`@Component`/`@Service`/`@Repository`/`@Controller`/`@RestController`/
159
+ `@Configuration`/`@Entity`/`@Embeddable`/`@MappedSuperclass`/`@Aspect`/`@WebServlet`/
160
+ `@WebFilter`/`@WebListener`/`@SpringBootApplication`, `public static void main`), and the
161
+ configuration-name scan now matches whole names — `Order` inside `OrderItemImpl` was
162
+ marking live-looking classes dispatched.
163
+ - **Value change:** the candidate population is much larger, so the counts move.
164
+ keycloak 14 → 108 `no_static_callers`, BroadleafCommerce 10 → 36, alfresco 13 → 88;
165
+ spring-petclinic stays 0 and now says why (its one callerless type is an `@Entity`, so
166
+ the answer is `unknown`). The scan is also faster than the one it replaces: one repository
167
+ walk instead of two, and fixed patterns intersected with the candidate set rather than an
168
+ alternation over it (keycloak 2.5 s → 1.2 s).
169
+
170
+ - **`ask posture` decided endpoint access against a URL the application never serves.** A
171
+ repository declaring `spring.mvc.servlet.path=/api` is served at `/api/orders`, and that is
172
+ the URL its filter chain matches — but the chain rules were compared against the
173
+ mapping-relative `/orders`. A rule reading `.antMatchers("/api/**").authenticated()`
174
+ therefore covered nothing. Reported from the field as 2 632 endpoints falsely
175
+ `no_rule_matched`; reproduced in a fixture, it is worse than that — where a catch-all
176
+ `anyRequest().permitAll()` follows the rule, those endpoints were reported **`permit_all`**,
177
+ which states a protected endpoint is open.
178
+ - One authority for the prefix: `deployment_prefix`, the same module `ask endpoints` reads
179
+ for `effective_path`. `posture` asks it for the part a matcher actually sees — the
180
+ **servlet path**, resolved for the profile set you asked about, so a servlet path declared
181
+ only under `prod` applies exactly when `prod` does. The servlet **context** path is
182
+ excluded on purpose: the container strips it before the filter chain runs.
183
+ - Matcher semantics are honoured rather than averaged: `antMatchers`/`regexMatchers` match
184
+ the servlet-path-prefixed URL, `mvcMatchers` matches relative to the servlet mapping, and
185
+ `requestMatchers` is either one depending on the Spring Security version and classpath —
186
+ unreadable from source, so a rule covering the request under either reading is honoured.
187
+ - New in the payload: `effective_access.path_basis` states the servlet path used and where
188
+ it came from, and each stated endpoint carries `matched_path` in its evidence when the
189
+ matched URL differs from the endpoint's mapping-relative path.
190
+ - **Never a guessed URL** (ADR-0008 R9): where a servlet path is declared but cannot be
191
+ resolved (a `${PLACEHOLDER}`, or documents that disagree), every verdict that depends on a
192
+ path pattern is reported `undecided` with `decision_if_unprefixed`, instead of computed
193
+ against an assumed empty prefix.
194
+ - **Value change:** none on a repository that declares no servlet path — the two readings
195
+ coincide and the matching is unchanged. Verified byte-identical on spring-petclinic,
196
+ keycloak, BroadleafCommerce and spaghetti-api, none of which declare one.
197
+
3
198
  ## [3.2.0] — 2026-07-28
4
199
 
5
200
  **Answer coherence (M7, first four seams).** One fact, one authority (ADR-0008). Four
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 3.2.0
3
+ Version: 3.2.1
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
@@ -73,6 +73,15 @@ Cache keyed on content hashes — invalidated only when source changes. On repea
73
73
 
74
74
  At 0.3s per call, ASK Engine becomes **constant infrastructure** inside agent loops — call it before every edit, every PR review, every test run, without batching or caching manually.
75
75
 
76
+ **What a warm actually covers.** `ask cache warm` runs the compact analysis: it rebuilds the
77
+ shared structural layers (L1/L2 + the Repository Intelligence Snapshot) and the **compact
78
+ view**. Pass `--agent` to warm the agent view as well. Deeper projections are separate view
79
+ keys and are *not* covered by either — `--full`, `--env-map` and a raised `--depth` recompute
80
+ on first use, as do most `prepare-context` tasks. Measured on a 3,342-file Spring monolith:
81
+ `cache warm` 103s → `--compact --git-context` 1s (hit), but `--agent --full --env-map
82
+ --depth 20` still 171s (miss). Budget the deep run explicitly in CI, or warm exactly the view
83
+ your pipeline consumes. Full model: [USER_GUIDE.md#caching](docs/USER_GUIDE.md#caching).
84
+
76
85
  ---
77
86
 
78
87
  ## Install
@@ -95,27 +104,55 @@ ask version # ask 2.5.1
95
104
 
96
105
  ## Quickstart
97
106
 
107
+ **Start with these four.** In three independent field evaluations they carried most of the
108
+ measured value, and `posture --diff` is the one no evaluator found an equivalent for —
109
+ commercial or open source.
110
+
98
111
  ```bash
99
- # High-signal structural summarywarm cache ~0.3s, cold 2–10s
100
- ask --compact
112
+ # What does each profile set ACTUALLY wire and what changes between them?
113
+ # Resolves conditional beans and the filter chain, then diffs effective endpoint access.
114
+ ask posture /path/to/repo --diff dev:prod
101
115
 
102
- # Blast radius: what breaks if this class changes?
103
- ask impact OrderService /path/to/repo
116
+ # Every REST endpoint with its effective path (context-path + servlet path resolved),
117
+ # its inferred security policy and a confidence per endpoint.
118
+ ask endpoints /path/to/repo
119
+
120
+ # Spring semantic audit: transactional anomalies (private @Transactional = silent
121
+ # CGLIB no-op), security surface, request-body validation.
122
+ ask spring-audit /path/to/repo
104
123
 
105
- # Spring Boot 2→3 migration readiness (bounded decision summary)
124
+ # Spring Boot 2→3 readiness: located blockers, per-dimension score, effort estimate.
106
125
  ask migrate-check /path/to/repo --compact
126
+ ```
107
127
 
108
- # Spring semantic audit: TX anomalies + security surface
109
- ask spring-audit /path/to/repo
128
+ Then the everyday loop:
129
+
130
+ ```bash
131
+ # High-signal structural summary — warm cache ~0.3s, cold 2–10s
132
+ ask --compact
133
+
134
+ # Blast radius: what breaks if this class changes? (target the INTERFACE, not the Impl)
135
+ ask impact OrderService /path/to/repo
110
136
 
111
137
  # Onboard to an unfamiliar codebase
112
138
  ask onboard /path/to/repo
113
139
 
114
140
  # PR review: risk, test gaps, changed modules
115
141
  ask review-pr /path/to/repo --since main
142
+
143
+ # CI gate on NEW violations only, instead of on pre-existing debt
144
+ ask verify /path/to/repo --capture-baseline # accept today's debt, once
145
+ ask verify /path/to/repo # then: only new violations block
116
146
  ```
117
147
 
118
- Full command reference: **[docs/USER_GUIDE.md](docs/USER_GUIDE.md)**.
148
+ > **Adopting a gate on a real codebase.** A repository that starts declaring contracts already
149
+ > violates them somewhere; `ask verify` is **baseline-relative by default** (`--fail-on new`)
150
+ > so the gate survives contact with reality instead of being switched off on day one.
151
+ > `ask baseline capture|diff|trend` is a different thing: versioned architectural metrics over
152
+ > time, for trend reporting rather than blocking.
153
+
154
+ Full command reference: **[docs/USER_GUIDE.md](docs/USER_GUIDE.md)** · posture in depth:
155
+ **[docs/posture.md](docs/posture.md)**.
119
156
 
120
157
  ---
121
158
 
@@ -148,6 +185,15 @@ Deterministic Spring semantics: transactional anomalies (e.g. `@Transactional` o
148
185
  `ask spring-audit` · `ask validation`
149
186
  → [reference](docs/USER_GUIDE.md#core-commands)
150
187
 
188
+ ### 5b · Runtime Posture *(experimental — and the most differentiated thing here)*
189
+ What a profile set **actually wires**: which conditional beans register, which do not, and which conditions could not be decided at all — then the effective endpoint access that follows from the filter chain. `--diff` answers the question nobody else answers in one command: *what changes between `dev` and `prod`, across every endpoint at once.*
190
+ `ask posture` · `ask posture --diff dev:prod` · `ask posture --property k=v`
191
+ → [posture.md](docs/posture.md)
192
+
193
+ Unresolved is a first-class outcome: a condition the resolver cannot decide is reported as a
194
+ hole with the condition named, never folded into active or inactive. **A posture answer that
195
+ guesses is a confident security falsehood — the worst failure mode this tool has.**
196
+
151
197
  ### 6 · Developer Workflows
152
198
  The everyday loop: diff-based PR review, symptom-driven bug triage, and delta context for continuous agent runs.
153
199
  `ask review-pr` · `ask fix-bug` · `ask prepare-context`
@@ -159,6 +205,36 @@ The everyday loop: diff-based PR review, symptom-driven bug triage, and delta co
159
205
 
160
206
  ---
161
207
 
208
+ ## Every command, in one table
209
+
210
+ *`ask --help` shows a short header; this is the full surface. If you only read one row, read
211
+ `posture`.*
212
+
213
+ | Command | Answers | Note |
214
+ |---|---|---|
215
+ | `posture` | which beans a profile set wires, and how effective endpoint access differs between two sets | **experimental**, most differentiated |
216
+ | `endpoints` | every REST endpoint, effective path, security policy, confidence | Spring MVC + JAX-RS (~65 % recall on JAX-RS sub-resource locators) |
217
+ | `spring-audit` | transactional anomalies + security surface + validation gaps | `--ci`, `-f github-comment` |
218
+ | `migrate-check` | Boot 2→3 readiness: located blockers, per-dimension score, effort | `--blast-radius` orders the re-test plan |
219
+ | `impact` / `impact-chain` | blast radius of a change, to the endpoints it reaches | target the **interface**, not the `Impl` |
220
+ | `pr-impact` | the same, scoped to a PR diff | gating command: `--fail-on`, exit codes |
221
+ | `verify` | does the repo satisfy its declared contracts, **relative to a baseline** | `.ask/contracts.yml`; exit 0/1/2 |
222
+ | `verify-edit` | did the working-tree edits change runtime behaviour | semantic diff gate for the edit loop |
223
+ | `--compact` / `--agent` | bounded structural context for an agent | `--compact` is the token-cheap one |
224
+ | `onboard` / `explain` / `cold-start` | orientation in an unfamiliar repo; per-class summary; bootstrap snapshot | |
225
+ | `export` / `repo-ir` / `schema` | tool-agnostic views (C4, module graph, integrations); symbol-level IR; published JSON Schemas | |
226
+ | `modernize` | coupling hubs, cycles, dead zones, refactor candidates | |
227
+ | `review-pr` / `fix-bug` / `prepare-context` | diff review, symptom triage, task-shaped context | |
228
+ | `plan` / `compare` / `delta` / `contract-diff` | what to review for a change; candidates by measured cost; outcome of a change; public-contract break | no verdicts, measured cost only |
229
+ | `validation` | request-body validation coverage and gaps | |
230
+ | `baseline capture\|diff\|trend` | versioned architectural metrics over time | trend reporting, not gating |
231
+ | `retrieve` | typed knowledge queries over the model | **experimental** |
232
+ | `archetype` | evidence-based architectural archetype | **experimental** |
233
+ | `rename-class` / `chunk-file` | word-boundary Java rename; split a large file for an agent | |
234
+ | `cache status\|warm\|clear` · `auth` · `telemetry` · `mcp` · `config` · `version` | housekeeping | |
235
+
236
+ ---
237
+
162
238
  ## What it does — and doesn't
163
239
 
164
240
  **ASK Engine reduces exploration cost.** It accelerates context acquisition and computes
@@ -182,6 +258,13 @@ Honest limits worth knowing before you rely on it:
182
258
  > **🎉 Early-adoption: Pro is currently unlocked for everyone.** Every install runs with
183
259
  > full Pro entitlements — no size gate, no key. The tiers below describe the model the
184
260
  > paywall will return to later.
261
+ >
262
+ > **What that means concretely.** `ask auth status` reports `"status": "unauthenticated"`
263
+ > together with `"pro": true`, `"pro_reason": "early-adoption unlock"` — that combination is
264
+ > expected, not a bug: you are unauthenticated *and* unlocked. When the unlock ends, gating
265
+ > returns **by repo size and automation, never by command**: `posture`, `endpoints`,
266
+ > `spring-audit` and `migrate-check` stay in the base tier at full output. Nothing you can run
267
+ > today becomes a paid-only command tomorrow.
185
268
 
186
269
  **Gating is by repo size and automation — never by command.** Every command runs at full
187
270
  power on Free for small and mid-size repos; you upgrade when the work gets bigger or automated.
@@ -209,7 +292,13 @@ ask telemetry disable # anonymous telemetry is on by default (opt-out)
209
292
 
210
293
  Telemetry collects version, OS, commands, flags, duration, repo-size range, and errors —
211
294
  **no source code, paths, secrets, or output**. Disable any time with
212
- `export SOURCECODE_TELEMETRY=0` (or `DO_NOT_TRACK=1`).
295
+ `export SOURCECODE_TELEMETRY=0` (or `DO_NOT_TRACK=1`). It defaults to **off in CI**.
296
+
297
+ > **Auditing someone else's code — regulated, client-owned or public-sector?** Turn it off
298
+ > *before* the first run, not after: `SOURCECODE_TELEMETRY=0 ask …`, or `ask telemetry disable`
299
+ > once (remembered). The default is opt-out today; **moving it to opt-in is on the roadmap**
300
+ > (`docs/DEFECT-LEDGER.md` P-1) precisely because a default you must remember to disable is the
301
+ > wrong default for third-party code.
213
302
 
214
303
  **Custom security annotations.** Teach `endpoints`, `spring-audit`, and `explain` about
215
304
  project-specific authorization annotations via an optional `sourcecode.config.json` at the
@@ -241,3 +330,4 @@ Matching endpoints report `policy: "custom"` and drop out of the `no_security_si
241
330
  | [MANUAL-USUARIO.md](docs/MANUAL-USUARIO.md) | Guía de usuario en español |
242
331
  | [PRODUCT_IDENTITY.md](docs/PRODUCT_IDENTITY.md) | `ask` (command) vs `sourcecode` (package/alias) |
243
332
  | [privacy.md](docs/privacy.md) | Telemetry and data-handling policy |
333
+ | [DEFECT-LEDGER.md](docs/DEFECT-LEDGER.md) | Every defect found in the field, its class, and which release closed it — published on purpose |
@@ -35,6 +35,15 @@ Cache keyed on content hashes — invalidated only when source changes. On repea
35
35
 
36
36
  At 0.3s per call, ASK Engine becomes **constant infrastructure** inside agent loops — call it before every edit, every PR review, every test run, without batching or caching manually.
37
37
 
38
+ **What a warm actually covers.** `ask cache warm` runs the compact analysis: it rebuilds the
39
+ shared structural layers (L1/L2 + the Repository Intelligence Snapshot) and the **compact
40
+ view**. Pass `--agent` to warm the agent view as well. Deeper projections are separate view
41
+ keys and are *not* covered by either — `--full`, `--env-map` and a raised `--depth` recompute
42
+ on first use, as do most `prepare-context` tasks. Measured on a 3,342-file Spring monolith:
43
+ `cache warm` 103s → `--compact --git-context` 1s (hit), but `--agent --full --env-map
44
+ --depth 20` still 171s (miss). Budget the deep run explicitly in CI, or warm exactly the view
45
+ your pipeline consumes. Full model: [USER_GUIDE.md#caching](docs/USER_GUIDE.md#caching).
46
+
38
47
  ---
39
48
 
40
49
  ## Install
@@ -57,27 +66,55 @@ ask version # ask 2.5.1
57
66
 
58
67
  ## Quickstart
59
68
 
69
+ **Start with these four.** In three independent field evaluations they carried most of the
70
+ measured value, and `posture --diff` is the one no evaluator found an equivalent for —
71
+ commercial or open source.
72
+
60
73
  ```bash
61
- # High-signal structural summarywarm cache ~0.3s, cold 2–10s
62
- ask --compact
74
+ # What does each profile set ACTUALLY wire and what changes between them?
75
+ # Resolves conditional beans and the filter chain, then diffs effective endpoint access.
76
+ ask posture /path/to/repo --diff dev:prod
63
77
 
64
- # Blast radius: what breaks if this class changes?
65
- ask impact OrderService /path/to/repo
78
+ # Every REST endpoint with its effective path (context-path + servlet path resolved),
79
+ # its inferred security policy and a confidence per endpoint.
80
+ ask endpoints /path/to/repo
81
+
82
+ # Spring semantic audit: transactional anomalies (private @Transactional = silent
83
+ # CGLIB no-op), security surface, request-body validation.
84
+ ask spring-audit /path/to/repo
66
85
 
67
- # Spring Boot 2→3 migration readiness (bounded decision summary)
86
+ # Spring Boot 2→3 readiness: located blockers, per-dimension score, effort estimate.
68
87
  ask migrate-check /path/to/repo --compact
88
+ ```
69
89
 
70
- # Spring semantic audit: TX anomalies + security surface
71
- ask spring-audit /path/to/repo
90
+ Then the everyday loop:
91
+
92
+ ```bash
93
+ # High-signal structural summary — warm cache ~0.3s, cold 2–10s
94
+ ask --compact
95
+
96
+ # Blast radius: what breaks if this class changes? (target the INTERFACE, not the Impl)
97
+ ask impact OrderService /path/to/repo
72
98
 
73
99
  # Onboard to an unfamiliar codebase
74
100
  ask onboard /path/to/repo
75
101
 
76
102
  # PR review: risk, test gaps, changed modules
77
103
  ask review-pr /path/to/repo --since main
104
+
105
+ # CI gate on NEW violations only, instead of on pre-existing debt
106
+ ask verify /path/to/repo --capture-baseline # accept today's debt, once
107
+ ask verify /path/to/repo # then: only new violations block
78
108
  ```
79
109
 
80
- Full command reference: **[docs/USER_GUIDE.md](docs/USER_GUIDE.md)**.
110
+ > **Adopting a gate on a real codebase.** A repository that starts declaring contracts already
111
+ > violates them somewhere; `ask verify` is **baseline-relative by default** (`--fail-on new`)
112
+ > so the gate survives contact with reality instead of being switched off on day one.
113
+ > `ask baseline capture|diff|trend` is a different thing: versioned architectural metrics over
114
+ > time, for trend reporting rather than blocking.
115
+
116
+ Full command reference: **[docs/USER_GUIDE.md](docs/USER_GUIDE.md)** · posture in depth:
117
+ **[docs/posture.md](docs/posture.md)**.
81
118
 
82
119
  ---
83
120
 
@@ -110,6 +147,15 @@ Deterministic Spring semantics: transactional anomalies (e.g. `@Transactional` o
110
147
  `ask spring-audit` · `ask validation`
111
148
  → [reference](docs/USER_GUIDE.md#core-commands)
112
149
 
150
+ ### 5b · Runtime Posture *(experimental — and the most differentiated thing here)*
151
+ What a profile set **actually wires**: which conditional beans register, which do not, and which conditions could not be decided at all — then the effective endpoint access that follows from the filter chain. `--diff` answers the question nobody else answers in one command: *what changes between `dev` and `prod`, across every endpoint at once.*
152
+ `ask posture` · `ask posture --diff dev:prod` · `ask posture --property k=v`
153
+ → [posture.md](docs/posture.md)
154
+
155
+ Unresolved is a first-class outcome: a condition the resolver cannot decide is reported as a
156
+ hole with the condition named, never folded into active or inactive. **A posture answer that
157
+ guesses is a confident security falsehood — the worst failure mode this tool has.**
158
+
113
159
  ### 6 · Developer Workflows
114
160
  The everyday loop: diff-based PR review, symptom-driven bug triage, and delta context for continuous agent runs.
115
161
  `ask review-pr` · `ask fix-bug` · `ask prepare-context`
@@ -121,6 +167,36 @@ The everyday loop: diff-based PR review, symptom-driven bug triage, and delta co
121
167
 
122
168
  ---
123
169
 
170
+ ## Every command, in one table
171
+
172
+ *`ask --help` shows a short header; this is the full surface. If you only read one row, read
173
+ `posture`.*
174
+
175
+ | Command | Answers | Note |
176
+ |---|---|---|
177
+ | `posture` | which beans a profile set wires, and how effective endpoint access differs between two sets | **experimental**, most differentiated |
178
+ | `endpoints` | every REST endpoint, effective path, security policy, confidence | Spring MVC + JAX-RS (~65 % recall on JAX-RS sub-resource locators) |
179
+ | `spring-audit` | transactional anomalies + security surface + validation gaps | `--ci`, `-f github-comment` |
180
+ | `migrate-check` | Boot 2→3 readiness: located blockers, per-dimension score, effort | `--blast-radius` orders the re-test plan |
181
+ | `impact` / `impact-chain` | blast radius of a change, to the endpoints it reaches | target the **interface**, not the `Impl` |
182
+ | `pr-impact` | the same, scoped to a PR diff | gating command: `--fail-on`, exit codes |
183
+ | `verify` | does the repo satisfy its declared contracts, **relative to a baseline** | `.ask/contracts.yml`; exit 0/1/2 |
184
+ | `verify-edit` | did the working-tree edits change runtime behaviour | semantic diff gate for the edit loop |
185
+ | `--compact` / `--agent` | bounded structural context for an agent | `--compact` is the token-cheap one |
186
+ | `onboard` / `explain` / `cold-start` | orientation in an unfamiliar repo; per-class summary; bootstrap snapshot | |
187
+ | `export` / `repo-ir` / `schema` | tool-agnostic views (C4, module graph, integrations); symbol-level IR; published JSON Schemas | |
188
+ | `modernize` | coupling hubs, cycles, dead zones, refactor candidates | |
189
+ | `review-pr` / `fix-bug` / `prepare-context` | diff review, symptom triage, task-shaped context | |
190
+ | `plan` / `compare` / `delta` / `contract-diff` | what to review for a change; candidates by measured cost; outcome of a change; public-contract break | no verdicts, measured cost only |
191
+ | `validation` | request-body validation coverage and gaps | |
192
+ | `baseline capture\|diff\|trend` | versioned architectural metrics over time | trend reporting, not gating |
193
+ | `retrieve` | typed knowledge queries over the model | **experimental** |
194
+ | `archetype` | evidence-based architectural archetype | **experimental** |
195
+ | `rename-class` / `chunk-file` | word-boundary Java rename; split a large file for an agent | |
196
+ | `cache status\|warm\|clear` · `auth` · `telemetry` · `mcp` · `config` · `version` | housekeeping | |
197
+
198
+ ---
199
+
124
200
  ## What it does — and doesn't
125
201
 
126
202
  **ASK Engine reduces exploration cost.** It accelerates context acquisition and computes
@@ -144,6 +220,13 @@ Honest limits worth knowing before you rely on it:
144
220
  > **🎉 Early-adoption: Pro is currently unlocked for everyone.** Every install runs with
145
221
  > full Pro entitlements — no size gate, no key. The tiers below describe the model the
146
222
  > paywall will return to later.
223
+ >
224
+ > **What that means concretely.** `ask auth status` reports `"status": "unauthenticated"`
225
+ > together with `"pro": true`, `"pro_reason": "early-adoption unlock"` — that combination is
226
+ > expected, not a bug: you are unauthenticated *and* unlocked. When the unlock ends, gating
227
+ > returns **by repo size and automation, never by command**: `posture`, `endpoints`,
228
+ > `spring-audit` and `migrate-check` stay in the base tier at full output. Nothing you can run
229
+ > today becomes a paid-only command tomorrow.
147
230
 
148
231
  **Gating is by repo size and automation — never by command.** Every command runs at full
149
232
  power on Free for small and mid-size repos; you upgrade when the work gets bigger or automated.
@@ -171,7 +254,13 @@ ask telemetry disable # anonymous telemetry is on by default (opt-out)
171
254
 
172
255
  Telemetry collects version, OS, commands, flags, duration, repo-size range, and errors —
173
256
  **no source code, paths, secrets, or output**. Disable any time with
174
- `export SOURCECODE_TELEMETRY=0` (or `DO_NOT_TRACK=1`).
257
+ `export SOURCECODE_TELEMETRY=0` (or `DO_NOT_TRACK=1`). It defaults to **off in CI**.
258
+
259
+ > **Auditing someone else's code — regulated, client-owned or public-sector?** Turn it off
260
+ > *before* the first run, not after: `SOURCECODE_TELEMETRY=0 ask …`, or `ask telemetry disable`
261
+ > once (remembered). The default is opt-out today; **moving it to opt-in is on the roadmap**
262
+ > (`docs/DEFECT-LEDGER.md` P-1) precisely because a default you must remember to disable is the
263
+ > wrong default for third-party code.
175
264
 
176
265
  **Custom security annotations.** Teach `endpoints`, `spring-audit`, and `explain` about
177
266
  project-specific authorization annotations via an optional `sourcecode.config.json` at the
@@ -203,3 +292,4 @@ Matching endpoints report `policy: "custom"` and drop out of the `no_security_si
203
292
  | [MANUAL-USUARIO.md](docs/MANUAL-USUARIO.md) | Guía de usuario en español |
204
293
  | [PRODUCT_IDENTITY.md](docs/PRODUCT_IDENTITY.md) | `ask` (command) vs `sourcecode` (package/alias) |
205
294
  | [privacy.md](docs/privacy.md) | Telemetry and data-handling policy |
295
+ | [DEFECT-LEDGER.md](docs/DEFECT-LEDGER.md) | Every defect found in the field, its class, and which release closed it — published on purpose |
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "3.2.0"
7
+ version = "3.2.1"
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__ = "3.2.0"
7
+ __version__ = "3.2.1"
@@ -274,16 +274,57 @@ def rule_matches(rule: AccessRule, method: str, path: str) -> bool:
274
274
  return any(ant_matches(pattern, path) for pattern in rule.patterns)
275
275
 
276
276
 
277
- def first_matching_rule(
278
- rules: "list[AccessRule]", method: str, path: str
279
- ) -> "Optional[AccessRule]":
280
- """The rule Spring would apply: the first declared one that matches."""
277
+ #: Matchers that see the DispatcherServlet path. An ant matcher matches the URL
278
+ #: the filter chain receives — servlet path included, servlet CONTEXT path never,
279
+ #: because the container has already stripped it. An mvc matcher matches the path
280
+ #: relative to the servlet mapping, so the servlet path is not part of it.
281
+ #: `requestMatchers` is either one depending on the Spring Security version and
282
+ #: what is on the classpath — undecidable from source, so both readings are tried
283
+ #: and the pattern is honoured if it covers the request under either.
284
+ _SERVLET_PATH_MATCHERS = ("antMatchers", "regexMatchers", "requestMatchers")
285
+ _SERVLET_RELATIVE_MATCHERS = ("mvcMatchers", "requestMatchers")
286
+
287
+
288
+ def candidate_paths(rule: AccessRule, path: str, servlet_prefix: str = "") -> "tuple[str, ...]":
289
+ """The path(s) `rule`'s matcher compares against a mapping-relative `path`.
290
+
291
+ Without a declared servlet path the two readings coincide and this is just
292
+ `(path,)` — which is every repository that never set `spring.mvc.servlet.path`.
293
+ """
294
+ if not servlet_prefix or rule.is_any_request:
295
+ return (path,)
296
+ prefixed = f"{servlet_prefix.rstrip('/')}/{path.lstrip('/')}".rstrip("/") or "/"
297
+ out: list[str] = []
298
+ if rule.matcher in _SERVLET_PATH_MATCHERS:
299
+ out.append(prefixed)
300
+ if rule.matcher in _SERVLET_RELATIVE_MATCHERS or not out:
301
+ out.append(path)
302
+ return tuple(dict.fromkeys(out))
303
+
304
+
305
+ def first_match(
306
+ rules: "list[AccessRule]", method: str, path: str, servlet_prefix: str = ""
307
+ ) -> "tuple[Optional[AccessRule], str]":
308
+ """`(rule Spring would apply, the path it matched)` — first declared wins.
309
+
310
+ The matched path is returned because with a servlet path declared it is not
311
+ the mapping-relative path the endpoint is keyed by, and a reader checking the
312
+ claim against the source line needs to see the URL the rule actually covers.
313
+ """
281
314
  for rule in rules:
282
315
  if rule.paths_unknown:
283
316
  # A rule whose paths could not be read may cover this request, and
284
317
  # everything after it is only reachable if it does not. Stopping here
285
318
  # is what keeps a later `permitAll` from being reported as the answer.
286
- return rule
287
- if rule_matches(rule, method, path):
288
- return rule
289
- return None
319
+ return rule, path
320
+ for candidate in candidate_paths(rule, path, servlet_prefix):
321
+ if rule_matches(rule, method, candidate):
322
+ return rule, candidate
323
+ return None, path
324
+
325
+
326
+ def first_matching_rule(
327
+ rules: "list[AccessRule]", method: str, path: str, servlet_prefix: str = ""
328
+ ) -> "Optional[AccessRule]":
329
+ """The rule Spring would apply: the first declared one that matches."""
330
+ return first_match(rules, method, path, servlet_prefix)[0]