sourcecode 3.2.1__tar.gz → 3.2.2__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 (189) hide show
  1. {sourcecode-3.2.1 → sourcecode-3.2.2}/CHANGELOG.md +105 -0
  2. {sourcecode-3.2.1 → sourcecode-3.2.2}/PKG-INFO +64 -40
  3. {sourcecode-3.2.1 → sourcecode-3.2.2}/README.md +63 -39
  4. {sourcecode-3.2.1 → sourcecode-3.2.2}/pyproject.toml +1 -1
  5. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/__init__.py +1 -1
  6. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/cache.py +104 -0
  7. sourcecode-3.2.2/src/sourcecode/cache_model.py +295 -0
  8. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/cli.py +201 -29
  9. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/context_cache.py +7 -27
  10. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/license.py +1 -1
  11. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/mcp/server.py +2 -2
  12. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/prepare_context.py +7 -7
  13. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/ris.py +27 -1
  14. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/telemetry/__init__.py +4 -3
  15. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/telemetry/config.py +31 -23
  16. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/telemetry/consent.py +20 -16
  17. {sourcecode-3.2.1 → sourcecode-3.2.2}/.github/workflows/build-windows.yml +0 -0
  18. {sourcecode-3.2.1 → sourcecode-3.2.2}/.gitignore +0 -0
  19. {sourcecode-3.2.1 → sourcecode-3.2.2}/.ruff.toml +0 -0
  20. {sourcecode-3.2.1 → sourcecode-3.2.2}/CONTRIBUTING.md +0 -0
  21. {sourcecode-3.2.1 → sourcecode-3.2.2}/LICENSE +0 -0
  22. {sourcecode-3.2.1 → sourcecode-3.2.2}/SECURITY.md +0 -0
  23. {sourcecode-3.2.1 → sourcecode-3.2.2}/raw +0 -0
  24. {sourcecode-3.2.1 → sourcecode-3.2.2}/scripts/compare_integration_engines.py +0 -0
  25. {sourcecode-3.2.1 → sourcecode-3.2.2}/scripts/customer_smoke_test.sh +0 -0
  26. {sourcecode-3.2.1 → sourcecode-3.2.2}/scripts/generate_jdk_exports.py +0 -0
  27. {sourcecode-3.2.1 → sourcecode-3.2.2}/scripts/perf_harness.py +0 -0
  28. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/adaptive_scanner.py +0 -0
  29. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/archetype.py +0 -0
  30. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/architectural_baseline.py +0 -0
  31. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/architectural_delta.py +0 -0
  32. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/architecture_analyzer.py +0 -0
  33. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/architecture_summary.py +0 -0
  34. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/ast_extractor.py +0 -0
  35. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/call_surface.py +0 -0
  36. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/caller_metrics.py +0 -0
  37. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/canonical_ir.py +0 -0
  38. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/chain_rules.py +0 -0
  39. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/change_plan.py +0 -0
  40. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/cir_graphs.py +0 -0
  41. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/classifier.py +0 -0
  42. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/code_notes_analyzer.py +0 -0
  43. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/compare.py +0 -0
  44. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/confidence_analyzer.py +0 -0
  45. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/constraint_diff.py +0 -0
  46. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/context_graph.py +0 -0
  47. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/context_scorer.py +0 -0
  48. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/context_summarizer.py +0 -0
  49. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/contract_diff.py +0 -0
  50. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/contract_model.py +0 -0
  51. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/contract_pipeline.py +0 -0
  52. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/coverage_parser.py +0 -0
  53. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/dependency_analyzer.py +0 -0
  54. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/deployment_prefix.py +0 -0
  55. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/__init__.py +0 -0
  56. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/base.py +0 -0
  57. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/csproj_parser.py +0 -0
  58. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/dart.py +0 -0
  59. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/dotnet.py +0 -0
  60. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/elixir.py +0 -0
  61. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/go.py +0 -0
  62. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/heuristic.py +0 -0
  63. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/hybrid.py +0 -0
  64. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/java.py +0 -0
  65. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/jvm_ext.py +0 -0
  66. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/nodejs.py +0 -0
  67. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/parsers.py +0 -0
  68. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/php.py +0 -0
  69. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/project.py +0 -0
  70. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/python.py +0 -0
  71. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/ruby.py +0 -0
  72. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/rust.py +0 -0
  73. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/systems.py +0 -0
  74. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/terraform.py +0 -0
  75. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/detectors/tooling.py +0 -0
  76. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/doc_analyzer.py +0 -0
  77. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/dynamic_argument_surface.py +0 -0
  78. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/endpoint_literals.py +0 -0
  79. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/endpoint_metrics.py +0 -0
  80. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/entrypoint_classifier.py +0 -0
  81. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/env_analyzer.py +0 -0
  82. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/envelope.py +0 -0
  83. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/error_schema.py +0 -0
  84. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/evidence_provider.py +0 -0
  85. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/explain.py +0 -0
  86. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/facts/__init__.py +0 -0
  87. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/facts/registry.json +0 -0
  88. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/file_chunker.py +0 -0
  89. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/file_classifier.py +0 -0
  90. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/filter_surface.py +0 -0
  91. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/format_contract.py +0 -0
  92. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/fqn_utils.py +0 -0
  93. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/git_analyzer.py +0 -0
  94. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/graph_analyzer.py +0 -0
  95. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/graph_evidence.py +0 -0
  96. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/hibernate_strat.py +0 -0
  97. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/integration_coordinates.py +0 -0
  98. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/jdk_exports.py +0 -0
  99. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/mcp/__init__.py +0 -0
  100. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  101. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  102. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  103. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  104. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  105. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/mcp/orchestrator.py +0 -0
  106. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/mcp/registry.py +0 -0
  107. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/mcp/runner.py +0 -0
  108. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/mcp_nudge.py +0 -0
  109. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/metrics_analyzer.py +0 -0
  110. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/migrate_check.py +0 -0
  111. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/migration_blast.py +0 -0
  112. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/openapi_surface.py +0 -0
  113. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/output_budget.py +0 -0
  114. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/parse_cache.py +0 -0
  115. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/path_filters.py +0 -0
  116. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/perf.py +0 -0
  117. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/posture.py +0 -0
  118. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/pr_comment_renderer.py +0 -0
  119. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/pr_impact.py +0 -0
  120. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/progress.py +0 -0
  121. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/ranking_engine.py +0 -0
  122. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/readiness_timeline.py +0 -0
  123. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/reconciliation.py +0 -0
  124. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/redactor.py +0 -0
  125. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/reference_facts.py +0 -0
  126. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/relevance_scorer.py +0 -0
  127. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/rename_refactor.py +0 -0
  128. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/repo_classifier.py +0 -0
  129. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/repository_ir.py +0 -0
  130. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/__init__.py +0 -0
  131. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/context.py +0 -0
  132. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/errors.py +0 -0
  133. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/executor.py +0 -0
  134. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/planner.py +0 -0
  135. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/query.py +0 -0
  136. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/request.py +0 -0
  137. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/resolution.py +0 -0
  138. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/result.py +0 -0
  139. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/retriever.py +0 -0
  140. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/runtime.py +0 -0
  141. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/steps.py +0 -0
  142. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
  143. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/steps_graph.py +0 -0
  144. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/steps_impact.py +0 -0
  145. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/steps_intf.py +0 -0
  146. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/steps_struct.py +0 -0
  147. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/retrieval/steps_txsec.py +0 -0
  148. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/runtime_classifier.py +0 -0
  149. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/scanner.py +0 -0
  150. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/schema.py +0 -0
  151. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
  152. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/security_config.py +0 -0
  153. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/security_posture.py +0 -0
  154. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/semantic_analyzer.py +0 -0
  155. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/semantic_impact_engine.py +0 -0
  156. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/semantic_integration_engine.py +0 -0
  157. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/semantic_services.py +0 -0
  158. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/serializer.py +0 -0
  159. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/spring_event_topology.py +0 -0
  160. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/spring_findings.py +0 -0
  161. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/spring_impact.py +0 -0
  162. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/spring_model.py +0 -0
  163. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/spring_profiles.py +0 -0
  164. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/spring_properties.py +0 -0
  165. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/spring_security_audit.py +0 -0
  166. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/spring_semantic.py +0 -0
  167. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/spring_tx_analyzer.py +0 -0
  168. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/summarizer.py +0 -0
  169. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/telemetry/events.py +0 -0
  170. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/telemetry/filters.py +0 -0
  171. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/telemetry/transport.py +0 -0
  172. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/test_sources.py +0 -0
  173. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/token_estimate.py +0 -0
  174. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/tree_utils.py +0 -0
  175. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/type_usage_surface.py +0 -0
  176. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/validation_inference.py +0 -0
  177. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/validation_surface.py +0 -0
  178. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/verify_edit.py +0 -0
  179. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/verify_repo.py +0 -0
  180. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/verify_rules.py +0 -0
  181. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/version_check.py +0 -0
  182. {sourcecode-3.2.1 → sourcecode-3.2.2}/src/sourcecode/workspace.py +0 -0
  183. {sourcecode-3.2.1 → sourcecode-3.2.2}/supabase/functions/README.md +0 -0
  184. {sourcecode-3.2.1 → sourcecode-3.2.2}/supabase/functions/get-license/index.ts +0 -0
  185. {sourcecode-3.2.1 → sourcecode-3.2.2}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  186. {sourcecode-3.2.1 → sourcecode-3.2.2}/supabase/functions/telemetry/index.ts +0 -0
  187. {sourcecode-3.2.1 → sourcecode-3.2.2}/supabase/sql/license_event_ordering.sql +0 -0
  188. {sourcecode-3.2.1 → sourcecode-3.2.2}/supabase/sql/licensing_schema.sql +0 -0
  189. {sourcecode-3.2.1 → sourcecode-3.2.2}/supabase/sql/telemetry_events.sql +0 -0
@@ -1,5 +1,110 @@
1
1
  # Changelog
2
2
 
3
+ ## [Unreleased]
4
+
5
+ ### Changed
6
+
7
+ - **Telemetry is opt-in** (P-1). It was opt-out; with no explicit choice on record, nothing is
8
+ now collected or transmitted — not on the first run, not in CI, nowhere. **An explicit choice
9
+ you made before is honoured unchanged**; if you never chose, telemetry is off.
10
+ - The default was not a preference. Field evaluation #3 was the first audit of regulated
11
+ third-party code (public-sector health data), where the reviewer must be able to state that
12
+ nothing left the machine — and *"disable it after the first run"* cannot give that
13
+ guarantee. It was recorded as a **procurement** blocker, not an analysis defect.
14
+ - The CI special case is gone along with the default it existed to override. CI is still
15
+ detected for exactly one purpose: skipping a first-run notice that has no terminal to
16
+ appear on.
17
+ - The first-run notice is now an **invitation**: it says telemetry is off, states what *would*
18
+ be collected if you enabled it, and names `ask telemetry enable`. It changes no setting.
19
+ - `ask telemetry status` distinguishes **"no choice recorded"** from **"you chose disabled"**
20
+ (`stored_choice()` returns `None`, never a silent `False`) — a buyer auditing this needs to
21
+ know which of the two they have.
22
+
23
+ ## [3.2.2] — 2026-07-29
24
+
25
+ **M7.5 complete — the product now says what it promises.** Three things a user could only
26
+ infer are written down and enforced: which of their commands a warm cache actually helps, what
27
+ a cold run costs where they meet it, and **what each command's output is worth relying on**.
28
+ Documenting the first of those found the cache had two freshness authorities and was answering
29
+ for trees it had not read — the pattern of this cycle, where writing down the promise is what
30
+ exposes where we do not keep it.
31
+
32
+ ### Added
33
+
34
+ - **Command tiers, named and published** (C4-6). Every command now carries one of four tiers —
35
+ `core`, `supported`, `experimental`, `parked` — from a single authority (`cli.COMMAND_TIERS`),
36
+ rendered by `ask --help`, the README command table and the user guide.
37
+ - A tier is a **stability promise**: `core` is safe to gate CI on, `experimental` may change
38
+ shape in a minor, `parked` still works but is no longer developed (empty today, and the help
39
+ says so rather than leaving the row blank).
40
+ - It is **not a value ranking** — `posture` is experimental *and* the first command the help
41
+ recommends — and it is **not the pricing tier**, which gates repository size and never
42
+ capability. Both distinctions are printed, not implied.
43
+ - The battery fails if a registered command sits in no tier, if a tier names a command that
44
+ does not exist, if a command calls itself `[EXPERIMENTAL]` while published in another tier,
45
+ or if either document drifts from the table.
46
+ - Which commands *move* between tiers, and whether `parked` disappears from the default help,
47
+ is the tiering work in M9. This release names the vocabulary before it is used against a
48
+ command.
49
+ - **The cache model is published, per command, and measured.** `ask cache model` (`--json`,
50
+ `--markdown`) and [docs/CACHE.md](docs/CACHE.md) answer the question field evaluation #3 could
51
+ not: *which of my commands does a warm actually help?* One rule for invalidation — every layer
52
+ keys on the exact tree state — then, per command, whether a warm gives it **the answer**, only
53
+ **the shared work**, or **nothing**, with the layers it reads and a measured figure.
54
+ - Which layers each command reads was derived by instrumenting the layers, not by reading the
55
+ code and hoping. What a warm is worth was then timed **per command in isolation**: cleared
56
+ caches, `cache warm`, then the command. Timed in a batch instead, one command warms the next
57
+ and the table lies — `impact-chain` looked unaffected by a warm in a batch and goes 9.9 s →
58
+ 1.7 s in isolation.
59
+ - Two measured results contradict the obvious assumption and are published as such: a warm
60
+ buys **`endpoints` nothing** (2.8 s → 2.9 s) and **`migrate-check` nothing** (4.8 s → 4.8 s),
61
+ and on a small diff it makes `review-pr` *slower* (1.1 s → 2.3 s) because loading the warmed
62
+ IR costs more than the work it saves.
63
+ - The battery fails if a registered command has no row, if the model names a command that does
64
+ not exist, if a row claims a warm helps while reading no layer a warm builds, or if
65
+ `docs/CACHE.md` has drifted from the registry. This is the `--help` lesson (C4-1) applied: a
66
+ curated list beside a generated one drifts.
67
+ - **`cache warm` states its cost and its limits when it finishes** (C4-5): elapsed time, what it
68
+ warmed, what it did **not** — `prepare-context` task answers, runs whose analysis flags differ,
69
+ and the two commands measured to gain nothing — plus the line that matters in CI: without a
70
+ persisted `~/.sourcecode`, every pipeline pays that time again. `cache warm --help` says it
71
+ before the run, not after.
72
+
73
+ ### Fixed
74
+
75
+ - **One authority for cache freshness: the tree state, never the committed HEAD.** The
76
+ snapshot cache behind the root command keyed on the HEAD sha alone, so it answered for a
77
+ tree it had not read. Reproduced on spring-petclinic: add `spring-boot-starter-data-ldap`
78
+ to a tracked `pom.xml`, do not commit, run `ask --compact` again — the pre-edit analysis
79
+ comes back from `L2_view` with `is_stale: false`, the new dependency invisible. The context
80
+ cache sitting next to it keyed on the exact tree and invalidated correctly, which is why
81
+ field evaluation #3 could only describe the behaviour as *"el modelo de invalidación no
82
+ está documentado y no es predecible"*: there were two models.
83
+ - `cache.worktree_signature` is now the single authority every layer keys on — the root
84
+ command's L1/L2 snapshots, the `prepare-context` task cache, the shared Canonical IR, and
85
+ the RIS bootstrap. One rule describes all of them: **any change to the analysed files
86
+ invalidates, committed or not.** A clean tree keys on the sha, so the ordinary repeat run
87
+ still hits (BroadleafCommerce: 18.1 s cold → 0.3 s warm, unchanged; the signature itself
88
+ costs ~50 ms on that tree, ~140 ms on keycloak).
89
+ - Untracked files are covered by size and mtime: they appear in no diff, so a new file — or
90
+ an edit to one — was invisible to a signature built from `git diff` alone.
91
+ - A tree git cannot describe (no repository, no commit) gets a fingerprint over the size and
92
+ mtime of every non-ignored file. Before, such trees used a **constant** key, so the cache
93
+ answered forever with the first analysis it ever ran; the context cache disabled itself
94
+ instead. Both now cache and both now invalidate.
95
+ - `--scope`-style subdirectory runs sign only their own subtree: an edit in a sibling module
96
+ no longer invalidates the module actually analysed.
97
+ - **RIS staleness answers the question it is asked.** `get_cold_start_context` reported
98
+ `stale: false` for any tree sharing the commit the snapshot was taken at, while carrying
99
+ `has_uncommitted_changes: true` in the same object — two facts about one thing, disagreeing.
100
+ The snapshot now records the tree it describes (`metadata.tree_signature`), and the
101
+ `prepare-context` RIS fast path serves `onboard`/`explain` only when that tree is still the
102
+ one on disk. Snapshots written before this release fall back to the old pair of facts.
103
+ - **`prepare-context --all` and `--include-config` are in the cache key.** They changed the
104
+ answer and were not keyed; the collision could not surface only because the key demanded a
105
+ git sha, and non-git trees cached nothing at all. Now that every tree has a signature, an
106
+ unkeyed flag would be served the other flag's answer.
107
+
3
108
  ## [3.2.1] — 2026-07-29
4
109
 
5
110
  **M7 complete — the last four seams, the fact registry, and a `--help` that leads with the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 3.2.1
3
+ Version: 3.2.2
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
@@ -74,13 +74,17 @@ Cache keyed on content hashes — invalidated only when source changes. On repea
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
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).
77
+ shared structural layers (L1/L2 + the Repository Intelligence Snapshot + the shared Canonical
78
+ IR) and the **compact view**. Pass `--agent` to warm the agent view as well. Deeper projections
79
+ are separate keys and are *not* covered by either — `--full`, `--env-map` and a raised `--depth`
80
+ recompute on first use, as do most `prepare-context` tasks. Measured on a 3,342-file Spring
81
+ monolith: `cache warm` 103s → `--compact --git-context` 1s (hit), but `--agent --full --env-map
82
+ --depth 20` still 171s (miss).
83
+
84
+ **One rule for invalidation, and a table that names every command.** Every layer keys on the
85
+ exact tree state: any change to the analysed files invalidates it, committed or not. Which of
86
+ *your* commands a warm helps — the answer, only the shared work, or nothing — is published per
87
+ command: `ask cache model`, or [docs/CACHE.md](docs/CACHE.md).
84
88
 
85
89
  ---
86
90
 
@@ -200,38 +204,58 @@ The everyday loop: diff-based PR review, symptom-driven bug triage, and delta co
200
204
  → [reference](docs/USER_GUIDE.md#typical-workflows)
201
205
 
202
206
  ### 7 · Utilities
203
- `ask rename-class` (word-boundary Java rename) · `ask chunk-file` (split large files for agents) · `ask cache` (status / warm / clear / freshness)
207
+ `ask rename-class` (word-boundary Java rename) · `ask chunk-file` (split large files for agents) · `ask cache` (status / warm / model / clear / freshness)
204
208
  → [reference](docs/USER_GUIDE.md)
205
209
 
206
210
  ---
207
211
 
212
+ ## Command tiers
213
+
214
+ A tier says **what an output is worth relying on** — it is a stability promise, not a value
215
+ ranking, and not the [pricing tier](docs/PRODUCT_TIERS.md) (Free/Pro gates repository size,
216
+ never capability). `posture` is the most differentiated command in the product *and* it is
217
+ experimental: both are true, and they are two different facts.
218
+
219
+ | Tier | Promise | Commands |
220
+ |---|---|---|
221
+ | **core** | contract stable within a major — safe to gate CI on | `endpoints` · `spring-audit` · `migrate-check` · `impact` · `impact-chain` · `pr-impact` · `verify` |
222
+ | **supported** | maintained; fields are added, never removed without a major | every command not named in another row |
223
+ | **experimental** | shape may change in a minor — do not gate CI on it | `posture` · `archetype` · `retrieve` |
224
+ | **parked** | kept working, no longer developed | none today |
225
+
226
+ The same table is printed by `ask --help`, and both are generated from one authority
227
+ (`cli.COMMAND_TIERS`) — the battery fails if a command is in no tier, or if this file drifts
228
+ from it.
229
+
230
+ ---
231
+
208
232
  ## Every command, in one table
209
233
 
210
234
  *`ask --help` shows a short header; this is the full surface. If you only read one row, read
211
235
  `posture`.*
212
236
 
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 | |
237
+ | Command | Tier | Answers | Note |
238
+ |---|---|---|---|
239
+ | `posture` | experimental | which beans a profile set wires, and how effective endpoint access differs between two sets | the most differentiated capability here |
240
+ | `endpoints` | core | every REST endpoint, effective path, security policy, confidence | Spring MVC + JAX-RS (~65 % recall on JAX-RS sub-resource locators) |
241
+ | `spring-audit` | core | transactional anomalies + security surface + validation gaps | `--ci`, `-f github-comment` |
242
+ | `migrate-check` | core | Boot 2→3 readiness: located blockers, per-dimension score, effort | `--blast-radius` orders the re-test plan |
243
+ | `impact` / `impact-chain` | core | blast radius of a change, to the endpoints it reaches | target the **interface**, not the `Impl` |
244
+ | `pr-impact` | core | the same, scoped to a PR diff | gating command: `--fail-on`, exit codes |
245
+ | `verify` | core | does the repo satisfy its declared contracts, **relative to a baseline** | `.ask/contracts.yml`; exit 0/1/2 |
246
+ | `verify-edit` | supported | did the working-tree edits change runtime behaviour | semantic diff gate for the edit loop |
247
+ | `--compact` / `--agent` | — | bounded structural context for an agent | flags of the root command, not commands: not tiered |
248
+ | `onboard` / `explain` / `cold-start` | supported | orientation in an unfamiliar repo; per-class summary; bootstrap snapshot | |
249
+ | `export` / `repo-ir` / `schema` | supported | tool-agnostic views (C4, module graph, integrations); symbol-level IR; published JSON Schemas | |
250
+ | `modernize` | supported | coupling hubs, cycles, dead zones, refactor candidates | |
251
+ | `review-pr` / `fix-bug` / `prepare-context` | supported | diff review, symptom triage, task-shaped context | |
252
+ | `plan` / `compare` / `delta` / `contract-diff` | supported | what to review for a change; candidates by measured cost; outcome of a change; public-contract break | no verdicts, measured cost only |
253
+ | `validation` | supported | request-body validation coverage and gaps | |
254
+ | `baseline capture\|diff\|trend` | supported | versioned architectural metrics over time | trend reporting, not gating |
255
+ | `retrieve` | experimental | typed knowledge queries over the model | |
256
+ | `archetype` | experimental | evidence-based architectural archetype | |
257
+ | `rename-class` / `chunk-file` | supported | word-boundary Java rename; split a large file for an agent | |
258
+ | `cache status\|warm\|model\|clear` · `auth` · `telemetry` · `mcp` · `config` · `version` | supported | housekeeping | `activate` too |
235
259
 
236
260
  ---
237
261
 
@@ -287,18 +311,18 @@ Full breakdown: [docs/PRODUCT_TIERS.md](docs/PRODUCT_TIERS.md).
287
311
 
288
312
  ```bash
289
313
  ask config # version, config file path, telemetry status
290
- ask telemetry disable # anonymous telemetry is on by default (opt-out)
314
+ ask telemetry enable # anonymous telemetry is OFF by default (opt-in)
291
315
  ```
292
316
 
293
- Telemetry collects version, OS, commands, flags, duration, repo-size range, and errors
294
- **no source code, paths, secrets, or output**. Disable any time with
295
- `export SOURCECODE_TELEMETRY=0` (or `DO_NOT_TRACK=1`). It defaults to **off in CI**.
317
+ **Nothing is collected or transmitted unless you turn telemetry on** not on the first run,
318
+ not in CI. If you do opt in, it collects version, OS, commands, flags, duration, repo-size
319
+ range, and errors: **no source code, paths, secrets, or output**. Turn it off again with
320
+ `ask telemetry disable`, `export SOURCECODE_TELEMETRY=0`, or `DO_NOT_TRACK=1`.
296
321
 
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.
322
+ > **Auditing someone else's code — regulated, client-owned or public-sector?** Nothing to do.
323
+ > Telemetry was opt-out until 3.3.0; it is opt-in now (`docs/DEFECT-LEDGER.md` P-1), because a
324
+ > default you must remember to disable is the wrong default for third-party code. An explicit
325
+ > choice you made before is unchanged.
302
326
 
303
327
  **Custom security annotations.** Teach `endpoints`, `spring-audit`, and `explain` about
304
328
  project-specific authorization annotations via an optional `sourcecode.config.json` at the
@@ -36,13 +36,17 @@ Cache keyed on content hashes — invalidated only when source changes. On repea
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
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).
39
+ shared structural layers (L1/L2 + the Repository Intelligence Snapshot + the shared Canonical
40
+ IR) and the **compact view**. Pass `--agent` to warm the agent view as well. Deeper projections
41
+ are separate keys and are *not* covered by either — `--full`, `--env-map` and a raised `--depth`
42
+ recompute on first use, as do most `prepare-context` tasks. Measured on a 3,342-file Spring
43
+ monolith: `cache warm` 103s → `--compact --git-context` 1s (hit), but `--agent --full --env-map
44
+ --depth 20` still 171s (miss).
45
+
46
+ **One rule for invalidation, and a table that names every command.** Every layer keys on the
47
+ exact tree state: any change to the analysed files invalidates it, committed or not. Which of
48
+ *your* commands a warm helps — the answer, only the shared work, or nothing — is published per
49
+ command: `ask cache model`, or [docs/CACHE.md](docs/CACHE.md).
46
50
 
47
51
  ---
48
52
 
@@ -162,38 +166,58 @@ The everyday loop: diff-based PR review, symptom-driven bug triage, and delta co
162
166
  → [reference](docs/USER_GUIDE.md#typical-workflows)
163
167
 
164
168
  ### 7 · Utilities
165
- `ask rename-class` (word-boundary Java rename) · `ask chunk-file` (split large files for agents) · `ask cache` (status / warm / clear / freshness)
169
+ `ask rename-class` (word-boundary Java rename) · `ask chunk-file` (split large files for agents) · `ask cache` (status / warm / model / clear / freshness)
166
170
  → [reference](docs/USER_GUIDE.md)
167
171
 
168
172
  ---
169
173
 
174
+ ## Command tiers
175
+
176
+ A tier says **what an output is worth relying on** — it is a stability promise, not a value
177
+ ranking, and not the [pricing tier](docs/PRODUCT_TIERS.md) (Free/Pro gates repository size,
178
+ never capability). `posture` is the most differentiated command in the product *and* it is
179
+ experimental: both are true, and they are two different facts.
180
+
181
+ | Tier | Promise | Commands |
182
+ |---|---|---|
183
+ | **core** | contract stable within a major — safe to gate CI on | `endpoints` · `spring-audit` · `migrate-check` · `impact` · `impact-chain` · `pr-impact` · `verify` |
184
+ | **supported** | maintained; fields are added, never removed without a major | every command not named in another row |
185
+ | **experimental** | shape may change in a minor — do not gate CI on it | `posture` · `archetype` · `retrieve` |
186
+ | **parked** | kept working, no longer developed | none today |
187
+
188
+ The same table is printed by `ask --help`, and both are generated from one authority
189
+ (`cli.COMMAND_TIERS`) — the battery fails if a command is in no tier, or if this file drifts
190
+ from it.
191
+
192
+ ---
193
+
170
194
  ## Every command, in one table
171
195
 
172
196
  *`ask --help` shows a short header; this is the full surface. If you only read one row, read
173
197
  `posture`.*
174
198
 
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 | |
199
+ | Command | Tier | Answers | Note |
200
+ |---|---|---|---|
201
+ | `posture` | experimental | which beans a profile set wires, and how effective endpoint access differs between two sets | the most differentiated capability here |
202
+ | `endpoints` | core | every REST endpoint, effective path, security policy, confidence | Spring MVC + JAX-RS (~65 % recall on JAX-RS sub-resource locators) |
203
+ | `spring-audit` | core | transactional anomalies + security surface + validation gaps | `--ci`, `-f github-comment` |
204
+ | `migrate-check` | core | Boot 2→3 readiness: located blockers, per-dimension score, effort | `--blast-radius` orders the re-test plan |
205
+ | `impact` / `impact-chain` | core | blast radius of a change, to the endpoints it reaches | target the **interface**, not the `Impl` |
206
+ | `pr-impact` | core | the same, scoped to a PR diff | gating command: `--fail-on`, exit codes |
207
+ | `verify` | core | does the repo satisfy its declared contracts, **relative to a baseline** | `.ask/contracts.yml`; exit 0/1/2 |
208
+ | `verify-edit` | supported | did the working-tree edits change runtime behaviour | semantic diff gate for the edit loop |
209
+ | `--compact` / `--agent` | — | bounded structural context for an agent | flags of the root command, not commands: not tiered |
210
+ | `onboard` / `explain` / `cold-start` | supported | orientation in an unfamiliar repo; per-class summary; bootstrap snapshot | |
211
+ | `export` / `repo-ir` / `schema` | supported | tool-agnostic views (C4, module graph, integrations); symbol-level IR; published JSON Schemas | |
212
+ | `modernize` | supported | coupling hubs, cycles, dead zones, refactor candidates | |
213
+ | `review-pr` / `fix-bug` / `prepare-context` | supported | diff review, symptom triage, task-shaped context | |
214
+ | `plan` / `compare` / `delta` / `contract-diff` | supported | what to review for a change; candidates by measured cost; outcome of a change; public-contract break | no verdicts, measured cost only |
215
+ | `validation` | supported | request-body validation coverage and gaps | |
216
+ | `baseline capture\|diff\|trend` | supported | versioned architectural metrics over time | trend reporting, not gating |
217
+ | `retrieve` | experimental | typed knowledge queries over the model | |
218
+ | `archetype` | experimental | evidence-based architectural archetype | |
219
+ | `rename-class` / `chunk-file` | supported | word-boundary Java rename; split a large file for an agent | |
220
+ | `cache status\|warm\|model\|clear` · `auth` · `telemetry` · `mcp` · `config` · `version` | supported | housekeeping | `activate` too |
197
221
 
198
222
  ---
199
223
 
@@ -249,18 +273,18 @@ Full breakdown: [docs/PRODUCT_TIERS.md](docs/PRODUCT_TIERS.md).
249
273
 
250
274
  ```bash
251
275
  ask config # version, config file path, telemetry status
252
- ask telemetry disable # anonymous telemetry is on by default (opt-out)
276
+ ask telemetry enable # anonymous telemetry is OFF by default (opt-in)
253
277
  ```
254
278
 
255
- Telemetry collects version, OS, commands, flags, duration, repo-size range, and errors
256
- **no source code, paths, secrets, or output**. Disable any time with
257
- `export SOURCECODE_TELEMETRY=0` (or `DO_NOT_TRACK=1`). It defaults to **off in CI**.
279
+ **Nothing is collected or transmitted unless you turn telemetry on** not on the first run,
280
+ not in CI. If you do opt in, it collects version, OS, commands, flags, duration, repo-size
281
+ range, and errors: **no source code, paths, secrets, or output**. Turn it off again with
282
+ `ask telemetry disable`, `export SOURCECODE_TELEMETRY=0`, or `DO_NOT_TRACK=1`.
258
283
 
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.
284
+ > **Auditing someone else's code — regulated, client-owned or public-sector?** Nothing to do.
285
+ > Telemetry was opt-out until 3.3.0; it is opt-in now (`docs/DEFECT-LEDGER.md` P-1), because a
286
+ > default you must remember to disable is the wrong default for third-party code. An explicit
287
+ > choice you made before is unchanged.
264
288
 
265
289
  **Custom security annotations.** Teach `endpoints`, `spring-audit`, and `explain` about
266
290
  project-specific authorization annotations via an optional `sourcecode.config.json` at the
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "3.2.1"
7
+ version = "3.2.2"
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.1"
7
+ __version__ = "3.2.2"
@@ -178,6 +178,110 @@ def _get_git_head(repo_root: Path) -> str:
178
178
  return ""
179
179
 
180
180
 
181
+ #: Directories never read as source, so their contents cannot change an answer.
182
+ #: Used only by the non-git fingerprint below (git ignores them by .gitignore).
183
+ _SIG_SKIP_DIRS: frozenset[str] = frozenset({
184
+ ".git", ".hg", ".svn", "target", "build", "out", "dist", "bin",
185
+ "node_modules", ".gradle", ".idea", ".vscode", "__pycache__", ".venv",
186
+ "venv", ".sourcecode-cache", ".mypy_cache", ".pytest_cache",
187
+ })
188
+
189
+
190
+ def _untracked_tree_fingerprint(target: Path) -> str:
191
+ """Fingerprint of a tree git cannot describe (no repository, or no commit).
192
+
193
+ Hashes ``(relative path, size, mtime_ns)`` of every non-ignored file. It is a
194
+ fingerprint of the tree, not of a commit — which is the point: without it the
195
+ key for a non-git tree is constant, and the cache answers forever with the
196
+ first analysis it ever ran.
197
+ """
198
+ h = hashlib.sha256()
199
+ try:
200
+ root = Path(target).resolve()
201
+ for dirpath, dirnames, filenames in os.walk(root):
202
+ dirnames[:] = sorted(d for d in dirnames if d not in _SIG_SKIP_DIRS)
203
+ for name in sorted(filenames):
204
+ p = Path(dirpath) / name
205
+ try:
206
+ st = p.stat()
207
+ except OSError:
208
+ continue
209
+ h.update(str(p.relative_to(root)).encode("utf-8", "replace"))
210
+ h.update(f"\x00{st.st_size}\x00{st.st_mtime_ns}\x00".encode())
211
+ except Exception:
212
+ return ""
213
+ return f"ff{h.hexdigest()[:14]}"
214
+
215
+
216
+ def worktree_signature(repo_root: Path, scope: Optional[Path] = None) -> str:
217
+ """Hex fingerprint of the **exact tree state an analysis would read**.
218
+
219
+ This is the single authority for cache freshness (ADR-0008): every layer that
220
+ stores an answer about the tree keys on this, so one rule describes them all —
221
+ *any change to the analysed files invalidates, committed or not*. Keying on the
222
+ committed HEAD alone made the root command serve an answer for a tree it had
223
+ not read: a dependency added to an uncommitted ``pom.xml`` stayed invisible
224
+ while the payload reported ``is_stale: false``.
225
+
226
+ Clean tree → the HEAD sha, so repeated runs hit. Dirty tree → HEAD plus a hash
227
+ of the porcelain status, the diff against HEAD, and the size/mtime of untracked
228
+ files (whose bytes the diff does not carry). *scope* restricts the status and
229
+ the diff to a subdirectory, so an edit in a sibling module does not invalidate
230
+ the module actually analysed.
231
+
232
+ Returns hex only — cache file names embed it and the GC parses them. Empty
233
+ string when the tree cannot be described at all; callers then skip the cache.
234
+ """
235
+ root = Path(repo_root)
236
+ head = _get_git_head(root)
237
+ if not head:
238
+ return _untracked_tree_fingerprint(scope if scope is not None else root)
239
+
240
+ pathspec: list[str] = []
241
+ try:
242
+ if scope is not None and Path(scope).resolve() != root.resolve():
243
+ pathspec = ["--", str(Path(scope).resolve())]
244
+ except Exception:
245
+ pathspec = []
246
+
247
+ try:
248
+ st = subprocess.run(
249
+ ["git", "-C", str(root), "status", "--porcelain", *pathspec],
250
+ capture_output=True, text=True, timeout=10,
251
+ )
252
+ porcelain = st.stdout if st.returncode == 0 else ""
253
+ except Exception:
254
+ porcelain = ""
255
+ if not porcelain.strip():
256
+ return head # clean — HEAD fully describes the tree
257
+
258
+ try:
259
+ df = subprocess.run(
260
+ ["git", "-C", str(root), "diff", "HEAD", *pathspec],
261
+ capture_output=True, text=True, timeout=20,
262
+ )
263
+ diff_txt = df.stdout if df.returncode == 0 else ""
264
+ except Exception:
265
+ diff_txt = ""
266
+
267
+ h = hashlib.sha256()
268
+ h.update(porcelain.encode("utf-8", "replace"))
269
+ h.update(b"\x00")
270
+ h.update(diff_txt.encode("utf-8", "replace"))
271
+ # Untracked files appear in the porcelain by name only; their bytes are in no
272
+ # diff, so an edit to one would be invisible without their stat.
273
+ for line in porcelain.splitlines():
274
+ if not line.startswith("?? "):
275
+ continue
276
+ try:
277
+ p = root / line[3:].strip().strip('"')
278
+ st_u = p.stat()
279
+ h.update(f"\x00{line[3:]}\x00{st_u.st_size}\x00{st_u.st_mtime_ns}".encode("utf-8", "replace"))
280
+ except OSError:
281
+ continue
282
+ return f"{head}d{h.hexdigest()[:12]}"
283
+
284
+
181
285
  # ---------------------------------------------------------------------------
182
286
  # Public API — location helpers
183
287
  # ---------------------------------------------------------------------------