sourcecode 2.0.0__tar.gz → 2.1.0__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 (138) hide show
  1. {sourcecode-2.0.0 → sourcecode-2.1.0}/CHANGELOG.md +76 -0
  2. sourcecode-2.1.0/PKG-INFO +241 -0
  3. sourcecode-2.1.0/README.md +203 -0
  4. {sourcecode-2.0.0 → sourcecode-2.1.0}/pyproject.toml +1 -1
  5. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/__init__.py +1 -1
  6. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/cli.py +28 -2
  7. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/migrate_check.py +64 -0
  8. sourcecode-2.1.0/src/sourcecode/security_posture.py +608 -0
  9. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/spring_findings.py +4 -0
  10. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/spring_security_audit.py +31 -0
  11. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/summarizer.py +33 -2
  12. sourcecode-2.1.0/src/sourcecode/validation_inference.py +249 -0
  13. sourcecode-2.0.0/PKG-INFO +0 -925
  14. sourcecode-2.0.0/README.md +0 -887
  15. {sourcecode-2.0.0 → sourcecode-2.1.0}/.github/workflows/build-windows.yml +0 -0
  16. {sourcecode-2.0.0 → sourcecode-2.1.0}/.gitignore +0 -0
  17. {sourcecode-2.0.0 → sourcecode-2.1.0}/.ruff.toml +0 -0
  18. {sourcecode-2.0.0 → sourcecode-2.1.0}/CONTRIBUTING.md +0 -0
  19. {sourcecode-2.0.0 → sourcecode-2.1.0}/LICENSE +0 -0
  20. {sourcecode-2.0.0 → sourcecode-2.1.0}/SECURITY.md +0 -0
  21. {sourcecode-2.0.0 → sourcecode-2.1.0}/raw +0 -0
  22. {sourcecode-2.0.0 → sourcecode-2.1.0}/scripts/compare_integration_engines.py +0 -0
  23. {sourcecode-2.0.0 → sourcecode-2.1.0}/scripts/customer_smoke_test.sh +0 -0
  24. {sourcecode-2.0.0 → sourcecode-2.1.0}/scripts/generate_jdk_exports.py +0 -0
  25. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/adaptive_scanner.py +0 -0
  26. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/architecture_analyzer.py +0 -0
  27. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/architecture_summary.py +0 -0
  28. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/ast_extractor.py +0 -0
  29. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/cache.py +0 -0
  30. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/call_surface.py +0 -0
  31. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/canonical_ir.py +0 -0
  32. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/cir_graphs.py +0 -0
  33. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/classifier.py +0 -0
  34. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/code_notes_analyzer.py +0 -0
  35. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/confidence_analyzer.py +0 -0
  36. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/context_graph.py +0 -0
  37. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/context_scorer.py +0 -0
  38. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/context_summarizer.py +0 -0
  39. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/contract_model.py +0 -0
  40. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/contract_pipeline.py +0 -0
  41. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/coverage_parser.py +0 -0
  42. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/dependency_analyzer.py +0 -0
  43. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/__init__.py +0 -0
  44. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/base.py +0 -0
  45. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
  46. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/dart.py +0 -0
  47. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/dotnet.py +0 -0
  48. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/elixir.py +0 -0
  49. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/go.py +0 -0
  50. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/heuristic.py +0 -0
  51. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/hybrid.py +0 -0
  52. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/java.py +0 -0
  53. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
  54. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/nodejs.py +0 -0
  55. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/parsers.py +0 -0
  56. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/php.py +0 -0
  57. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/project.py +0 -0
  58. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/python.py +0 -0
  59. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/ruby.py +0 -0
  60. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/rust.py +0 -0
  61. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/systems.py +0 -0
  62. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/terraform.py +0 -0
  63. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/detectors/tooling.py +0 -0
  64. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/doc_analyzer.py +0 -0
  65. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/dynamic_argument_surface.py +0 -0
  66. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/endpoint_literals.py +0 -0
  67. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/entrypoint_classifier.py +0 -0
  68. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/env_analyzer.py +0 -0
  69. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/error_schema.py +0 -0
  70. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/evidence_provider.py +0 -0
  71. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/explain.py +0 -0
  72. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/file_chunker.py +0 -0
  73. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/file_classifier.py +0 -0
  74. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/format_contract.py +0 -0
  75. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/fqn_utils.py +0 -0
  76. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/git_analyzer.py +0 -0
  77. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/graph_analyzer.py +0 -0
  78. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/hibernate_strat.py +0 -0
  79. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/jdk_exports.py +0 -0
  80. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/license.py +0 -0
  81. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/mcp/__init__.py +0 -0
  82. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  83. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  84. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  85. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  86. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  87. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/mcp/orchestrator.py +0 -0
  88. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/mcp/registry.py +0 -0
  89. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/mcp/runner.py +0 -0
  90. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/mcp/server.py +0 -0
  91. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/mcp_nudge.py +0 -0
  92. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/metrics_analyzer.py +0 -0
  93. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/openapi_surface.py +0 -0
  94. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/output_budget.py +0 -0
  95. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/path_filters.py +0 -0
  96. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/pr_comment_renderer.py +0 -0
  97. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/pr_impact.py +0 -0
  98. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/prepare_context.py +0 -0
  99. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/progress.py +0 -0
  100. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/ranking_engine.py +0 -0
  101. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/redactor.py +0 -0
  102. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/relevance_scorer.py +0 -0
  103. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/rename_refactor.py +0 -0
  104. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/repo_classifier.py +0 -0
  105. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/repository_ir.py +0 -0
  106. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/ris.py +0 -0
  107. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/runtime_classifier.py +0 -0
  108. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/scanner.py +0 -0
  109. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/schema.py +0 -0
  110. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/security_config.py +0 -0
  111. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/semantic_analyzer.py +0 -0
  112. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/semantic_impact_engine.py +0 -0
  113. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/semantic_integration_engine.py +0 -0
  114. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/semantic_services.py +0 -0
  115. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/serializer.py +0 -0
  116. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/spring_event_topology.py +0 -0
  117. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/spring_impact.py +0 -0
  118. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/spring_model.py +0 -0
  119. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/spring_semantic.py +0 -0
  120. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/spring_tx_analyzer.py +0 -0
  121. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/telemetry/__init__.py +0 -0
  122. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/telemetry/config.py +0 -0
  123. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/telemetry/consent.py +0 -0
  124. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/telemetry/events.py +0 -0
  125. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/telemetry/filters.py +0 -0
  126. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/telemetry/transport.py +0 -0
  127. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/tree_utils.py +0 -0
  128. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/type_usage_surface.py +0 -0
  129. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/validation_surface.py +0 -0
  130. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/version_check.py +0 -0
  131. {sourcecode-2.0.0 → sourcecode-2.1.0}/src/sourcecode/workspace.py +0 -0
  132. {sourcecode-2.0.0 → sourcecode-2.1.0}/supabase/functions/README.md +0 -0
  133. {sourcecode-2.0.0 → sourcecode-2.1.0}/supabase/functions/get-license/index.ts +0 -0
  134. {sourcecode-2.0.0 → sourcecode-2.1.0}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  135. {sourcecode-2.0.0 → sourcecode-2.1.0}/supabase/functions/telemetry/index.ts +0 -0
  136. {sourcecode-2.0.0 → sourcecode-2.1.0}/supabase/sql/license_event_ordering.sql +0 -0
  137. {sourcecode-2.0.0 → sourcecode-2.1.0}/supabase/sql/licensing_schema.sql +0 -0
  138. {sourcecode-2.0.0 → sourcecode-2.1.0}/supabase/sql/telemetry_events.sql +0 -0
@@ -1,5 +1,81 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.1.0] — 2026-07-10
4
+
5
+ **Robustness & Security Inference — vendor-agnostic security & validation posture.**
6
+ Two new Knowledge-Layer projections let the engine reason about a repo's security
7
+ and validation *model* without prior knowledge of the framework or any per-client
8
+ configuration. Detection is structural (coverage, specificity, correlation,
9
+ request-interception topology); a config file may only raise confidence, never
10
+ drive detection. No IR change — both projections reuse facts the engine already
11
+ extracts.
12
+
13
+ ### Added
14
+
15
+ - **`spring-audit` — `security_posture` block (custom authorization inference).**
16
+ The engine now infers custom endpoint security models on a repo it has never
17
+ seen. It auto-detects a candidate custom-authorization annotation purely by
18
+ structure — an annotation outside the published open-standard baseline that
19
+ covers a systematic share of controller handlers, tracks the endpoint surface
20
+ (specificity), and correlates with a request-interception pipeline. The
21
+ concrete annotation name appears only as *evidence*, never in detection logic.
22
+ Every conclusion carries an Evidence-Confidence envelope
23
+ (`Verified | Likely | Unknown | RequiresManualReview`): detection fires at
24
+ `Likely` with no config present, and a `sourcecode.config.json` entry naming
25
+ the annotation upgrades it to `Verified`. Emits per-endpoint 4-way verdicts
26
+ (`protected_standard` / `protected_custom` / `coverage_unknown` /
27
+ `probably_exposed`) plus a request-interception ("authentication pipeline
28
+ inferred") conclusion. This kills the recurring false "N endpoints without
29
+ security" alarm on repos guarded by a bespoke gate — when a custom mechanism is
30
+ inferred, uncovered endpoints degrade to `coverage_unknown`, never a categorical
31
+ "unsecured". The related `SEC-001` finding is suppressed on any endpoint carrying
32
+ an auto-detected gate.
33
+
34
+ - **`validation` — `validation_pattern` block (present-but-unused inference).**
35
+ Instead of reporting silent zeros when a repo has no `@Valid`, the engine now
36
+ classifies the request-body validation *pattern*: `bean_validation_active`
37
+ (Verified), `bean_validation_available_unused` (Likely — a validation framework
38
+ is on the classpath and body endpoints exist but none validate), or
39
+ `no_validation_detected` (Unknown). The middle class is the real finding on
40
+ repos with many `@RequestBody` handlers and zero `@Valid`: "framework present
41
+ but unused", with a prioritized recommendation — not an empty report.
42
+
43
+ ### Notes
44
+
45
+ - Both projections are vendor-agnostic: inference predicates key only on published
46
+ open standards (`org.springframework.security`, `jakarta`/`javax.validation`,
47
+ JAX-RS), loaded as a declarative baseline table used to subtract the known
48
+ vocabulary. No proprietary annotation, framework, or naming convention appears
49
+ in any inference predicate (enforced by test).
50
+ - Manual (throw-on-bad-parameter) validation detection is intentionally deferred:
51
+ guard-clause atoms do not yet cover endpoint-kind handler bodies. See
52
+ `docs/architecture/DESIGN-robustness-security-inference.md` §2.
53
+ - No breaking changes. Default command outputs are unchanged except for the added
54
+ blocks above.
55
+
56
+ ## [2.0.1] — 2026-07-10
57
+
58
+ ### Added
59
+
60
+ - **`migrate-check --compact` — bounded decision summary.** A new opt-in view of the
61
+ migration report that keeps every decision field (readiness scores, headline blocker,
62
+ blocking count, estimated effort, executive summary, Hibernate classification, aggregate
63
+ counts) and caps each large collection to its top items with a `+N more` count. On real
64
+ monoliths the report shrinks ~97% (BroadleafCommerce 1.2 MB → 32 KB, OpenMRS 908 KB →
65
+ 23 KB) — an agent or reviewer gets the verdict without the leaf-level firehose. The full
66
+ report remains the default output.
67
+
68
+ ### Fixed
69
+
70
+ - **`onboard` project summary no longer leaks raw README markup.** READMEs that open with
71
+ a logo block (`<img>` / `<p>` HTML) or use inline markdown links (`[label](url)`) had
72
+ produced summaries like `API: <img src="…"/>`. The summary is now rendered to clean prose
73
+ — HTML/badge blocks are skipped and inline links are unwrapped to their label.
74
+
75
+ ### Notes
76
+
77
+ - No breaking changes. Default command outputs are unchanged except where explicitly noted.
78
+
3
79
  ## [2.0.0] — 2026-07-06
4
80
 
5
81
  **ASK Engine 2.0** — the product identity is now final. The engine is unchanged;
@@ -0,0 +1,241 @@
1
+ Metadata-Version: 2.4
2
+ Name: sourcecode
3
+ Version: 2.1.0
4
+ Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
+ License-File: LICENSE
6
+ Keywords: agents,ai,codebase,context,developer-tools,llm
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Topic :: Utilities
19
+ Requires-Python: >=3.9
20
+ Requires-Dist: defusedxml>=0.7
21
+ Requires-Dist: mcp>=1.0.0
22
+ Requires-Dist: pathspec>=1.0
23
+ Requires-Dist: ruamel-yaml>=0.18
24
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
25
+ Requires-Dist: typer>=0.24
26
+ Provides-Extra: ast
27
+ Requires-Dist: tree-sitter-javascript>=0.21; extra == 'ast'
28
+ Requires-Dist: tree-sitter-typescript>=0.21; extra == 'ast'
29
+ Requires-Dist: tree-sitter>=0.21; extra == 'ast'
30
+ Provides-Extra: dev
31
+ Requires-Dist: mcp>=1.0.0; extra == 'dev'
32
+ Requires-Dist: mypy>=1.10; extra == 'dev'
33
+ Requires-Dist: pytest>=8; extra == 'dev'
34
+ Requires-Dist: ruff>=0.15; extra == 'dev'
35
+ Provides-Extra: mcp
36
+ Requires-Dist: mcp>=1.0.0; extra == 'mcp'
37
+ Description-Content-Type: text/markdown
38
+
39
+ # ASK Engine
40
+
41
+ > **Persistent structural intelligence for AI coding agents.**
42
+
43
+ **Context · Impact · Migration · Architecture · Review — everything from one structural model.**
44
+
45
+ ![Version](https://img.shields.io/badge/version-2.1.0-blue)
46
+ ![Python](https://img.shields.io/badge/python-3.9%2B-green)
47
+
48
+ > **ASK Engine** is the product. The CLI command is **`ask`**. The legacy **`sourcecode`**
49
+ > command still works as a deprecated alias (it prints a one-line notice and forwards to
50
+ > `ask`) and remains the Python/PyPI package name for now. The authoritative version is
51
+ > whatever `ask version` reports. See [docs/PRODUCT_IDENTITY.md](docs/PRODUCT_IDENTITY.md).
52
+
53
+ ---
54
+
55
+ ## The problem
56
+
57
+ Every time an AI coding agent starts a new session, it has to re-parse the repository from scratch. For a large Java or TypeScript monolith, that means 5–15 seconds per invocation. Multiply by dozens of agent turns per hour, and repo context acquisition becomes a real bottleneck — not just latency, but tokens, compute, and iteration velocity.
58
+
59
+ ASK Engine solves this with a persistent structural cache keyed on file content hashes. After the first scan, every subsequent invocation returns pre-built context in milliseconds. The repo doesn't change? The cache doesn't expire.
60
+
61
+ **The cache is not a performance optimization. It is what makes ASK Engine usable as infrastructure rather than a one-off tool.**
62
+
63
+ ---
64
+
65
+ ## Proof — measured on real repos
66
+
67
+ | Repo | Size | Cold scan | Cache hit | Speedup |
68
+ |------|------|-----------|-----------|---------|
69
+ | Keycloak | 7,885 Java files | 10.5s | 0.6s | **~17x** |
70
+ | BroadleafCommerce | 2,985 Java files | 2.7s | 0.3s | **~9x** |
71
+
72
+ Cache keyed on content hashes — invalidated only when source changes. On repeated agent sessions against the same codebase, nearly every invocation is a cache hit.
73
+
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
+
76
+ ---
77
+
78
+ ## Install
79
+
80
+ ```bash
81
+ # Homebrew (macOS / Linux)
82
+ brew tap haroundominique/sourcecode && brew install sourcecode
83
+
84
+ # pip / pipx
85
+ pipx install sourcecode # or: pip install sourcecode
86
+
87
+ ask version # ask 2.1.0
88
+ ```
89
+
90
+ > **Package vs. command.** The install package is named `sourcecode` this release
91
+ > (renaming the distribution is a separate, breaking change). Installing it gives you the
92
+ > canonical **`ask`** command plus the deprecated **`sourcecode`** alias.
93
+
94
+ ---
95
+
96
+ ## Quickstart
97
+
98
+ ```bash
99
+ # High-signal structural summary — warm cache ~0.3s, cold 2–10s
100
+ ask --compact
101
+
102
+ # Blast radius: what breaks if this class changes?
103
+ ask impact OrderService /path/to/repo
104
+
105
+ # Spring Boot 2→3 migration readiness (bounded decision summary)
106
+ ask migrate-check /path/to/repo --compact
107
+
108
+ # Spring semantic audit: TX anomalies + security surface
109
+ ask spring-audit /path/to/repo
110
+
111
+ # Onboard to an unfamiliar codebase
112
+ ask onboard /path/to/repo
113
+
114
+ # PR review: risk, test gaps, changed modules
115
+ ask review-pr /path/to/repo --since main
116
+ ```
117
+
118
+ Full command reference: **[docs/USER_GUIDE.md](docs/USER_GUIDE.md)**.
119
+
120
+ ---
121
+
122
+ ## Capabilities
123
+
124
+ Everything is computed from one cached structural model. Seven groups:
125
+
126
+ ### 1 · Structural Context
127
+ Bounded, noise-free repo context designed to drop straight into an agent's context window.
128
+ `ask --compact` · `ask --agent` · `ask onboard` · `ask cold-start`
129
+ → [reference](docs/USER_GUIDE.md#core-commands)
130
+
131
+ ### 2 · Impact Analysis
132
+ Blast radius from a class or interface — reverse dependencies, through Spring DI, to the HTTP endpoints a change reaches.
133
+ `ask impact` · `ask impact-chain` (TX/SEC-enriched) · `ask pr-impact`
134
+ → [reference](docs/USER_GUIDE.md#core-commands)
135
+
136
+ ### 3 · Architecture Intelligence
137
+ The system map: module graph, dependency views, REST surface, per-class summaries, and a symbol-level IR for downstream tooling.
138
+ `ask export` · `ask repo-ir` · `ask endpoints` · `ask explain`
139
+ → [reference](docs/USER_GUIDE.md#core-commands)
140
+
141
+ ### 4 · Migration & Modernization
142
+ Is this codebase ready to upgrade? Per-dimension readiness (Jakarta / Spring Boot / JDK / Hibernate), located blockers, and an effort estimate.
143
+ `ask migrate-check` · `ask modernize`
144
+ → [migrate-check reference](docs/migrate-check.md) · [MODERNIZATION.md](docs/MODERNIZATION.md)
145
+
146
+ ### 5 · Spring Analysis
147
+ Deterministic Spring semantics: transactional anomalies (e.g. `@Transactional` on a private method = silent CGLIB no-op), security surface, request-body validation.
148
+ `ask spring-audit` · `ask validation`
149
+ → [reference](docs/USER_GUIDE.md#core-commands)
150
+
151
+ ### 6 · Developer Workflows
152
+ The everyday loop: diff-based PR review, symptom-driven bug triage, and delta context for continuous agent runs.
153
+ `ask review-pr` · `ask fix-bug` · `ask prepare-context`
154
+ → [reference](docs/USER_GUIDE.md#typical-workflows)
155
+
156
+ ### 7 · Utilities
157
+ `ask rename-class` (word-boundary Java rename) · `ask chunk-file` (split large files for agents) · `ask cache` (status / warm / clear / freshness)
158
+ → [reference](docs/USER_GUIDE.md)
159
+
160
+ ---
161
+
162
+ ## What it does — and doesn't
163
+
164
+ **ASK Engine reduces exploration cost.** It accelerates context acquisition and computes
165
+ blast radius; it does not replace reading code — it reduces how often an agent needs to.
166
+ All signals are **static and deterministic** (annotations, import graph, file structure) —
167
+ no runtime analysis, no LLM guessing.
168
+
169
+ Honest limits worth knowing before you rely on it:
170
+
171
+ - `impact` on an **implementation** class (`OrderServiceImpl`) returns 0 callers in Spring Boot — callers inject the interface. **Always target the interface.**
172
+ - `no_security_signal` on an endpoint means *no recognized method-level annotation*, **not** "unsecured" — Spring Security filter chains and custom authorization annotations show as `no_security_signal` unless taught via config (below).
173
+ - `spring-audit` / `impact-chain` are **Java/Spring only**; non-Java repos return `spring_detected: false`.
174
+ - Event topology (`--type events`) resolves Spring `ApplicationEvent` / `@EventListener` chains only — **not** Kafka/RabbitMQ/Redis routes.
175
+ - Architecture classification is tuned for Spring MVC layered apps; SPI/plugin models (e.g. Quarkus extensions) may be misclassified. JAX-RS subresource-locator endpoint recall is ~65%.
176
+ - Self-invocation `@Transactional` bypass (same-class call skipping the proxy) is not detected.
177
+
178
+ ---
179
+
180
+ ## Pricing
181
+
182
+ > **🎉 Early-adoption: Pro is currently unlocked for everyone.** Every install runs with
183
+ > full Pro entitlements — no size gate, no key. The tiers below describe the model the
184
+ > paywall will return to later.
185
+
186
+ **Gating is by repo size and automation — never by command.** Every command runs at full
187
+ power on Free for small and mid-size repos; you upgrade when the work gets bigger or automated.
188
+
189
+ | | **Free** — €0 | **Pro** — €19/mo · €190/yr per dev |
190
+ |---|---|---|
191
+ | Repo size | ≤ 500 Java source files | **> 500 Java files** (enterprise monoliths) |
192
+ | Commands | All of them, full output | Same commands, unlocked at scale |
193
+ | `impact` / `fix-bug` / `review-pr` / `modernize` | ✅ full on small repos | ✅ full on large repos (Free gets a capped preview) |
194
+ | `prepare-context delta` | 30 free runs/repo | unlimited — CI/CD automation |
195
+ | MCP local server, offline, no data egress | ✅ | ✅ |
196
+
197
+ **Non-Java repos are free at any size** — the size limit counts Java source files only.
198
+ ASK Engine monetises enterprise Java monoliths. Activate with `ask activate <key>`.
199
+ Full breakdown: [docs/PRODUCT_TIERS.md](docs/PRODUCT_TIERS.md).
200
+
201
+ ---
202
+
203
+ ## Configuration & privacy
204
+
205
+ ```bash
206
+ ask config # version, config file path, telemetry status
207
+ ask telemetry disable # anonymous telemetry is on by default (opt-out)
208
+ ```
209
+
210
+ Telemetry collects version, OS, commands, flags, duration, repo-size range, and errors —
211
+ **no source code, paths, secrets, or output**. Disable any time with
212
+ `export SOURCECODE_TELEMETRY=0` (or `DO_NOT_TRACK=1`).
213
+
214
+ **Custom security annotations.** Teach `endpoints`, `spring-audit`, and `explain` about
215
+ project-specific authorization annotations via an optional `sourcecode.config.json` at the
216
+ repo root (otherwise they report `policy: "none_detected"`):
217
+
218
+ ```json
219
+ {
220
+ "customSecurityAnnotations": [
221
+ { "fullyQualifiedName": "com.example.security.M3FiltroSeguridad", "shortName": "M3FiltroSeguridad" }
222
+ ]
223
+ }
224
+ ```
225
+
226
+ Matching endpoints report `policy: "custom"` and drop out of the `no_security_signal` count.
227
+
228
+ ---
229
+
230
+ ## Documentation
231
+
232
+ | Doc | What it covers |
233
+ |-----|----------------|
234
+ | [USER_GUIDE.md](docs/USER_GUIDE.md) | Full command reference, flags, output schema, workflows |
235
+ | [migrate-check.md](docs/migrate-check.md) | Migration rule catalogue (MIG-001..043) + Hibernate stratification |
236
+ | [MODERNIZATION.md](docs/MODERNIZATION.md) | The modernization product: assess → understand → plan → execute |
237
+ | [PRODUCT_TIERS.md](docs/PRODUCT_TIERS.md) | Free vs Pro, pricing model |
238
+ | [DEMO-5MIN.md](docs/DEMO-5MIN.md) | A reproducible 5-minute demo |
239
+ | [MANUAL-USUARIO.md](docs/MANUAL-USUARIO.md) | Guía de usuario en español |
240
+ | [PRODUCT_IDENTITY.md](docs/PRODUCT_IDENTITY.md) | `ask` (command) vs `sourcecode` (package/alias) |
241
+ | [privacy.md](docs/privacy.md) | Telemetry and data-handling policy |
@@ -0,0 +1,203 @@
1
+ # ASK Engine
2
+
3
+ > **Persistent structural intelligence for AI coding agents.**
4
+
5
+ **Context · Impact · Migration · Architecture · Review — everything from one structural model.**
6
+
7
+ ![Version](https://img.shields.io/badge/version-2.1.0-blue)
8
+ ![Python](https://img.shields.io/badge/python-3.9%2B-green)
9
+
10
+ > **ASK Engine** is the product. The CLI command is **`ask`**. The legacy **`sourcecode`**
11
+ > command still works as a deprecated alias (it prints a one-line notice and forwards to
12
+ > `ask`) and remains the Python/PyPI package name for now. The authoritative version is
13
+ > whatever `ask version` reports. See [docs/PRODUCT_IDENTITY.md](docs/PRODUCT_IDENTITY.md).
14
+
15
+ ---
16
+
17
+ ## The problem
18
+
19
+ Every time an AI coding agent starts a new session, it has to re-parse the repository from scratch. For a large Java or TypeScript monolith, that means 5–15 seconds per invocation. Multiply by dozens of agent turns per hour, and repo context acquisition becomes a real bottleneck — not just latency, but tokens, compute, and iteration velocity.
20
+
21
+ ASK Engine solves this with a persistent structural cache keyed on file content hashes. After the first scan, every subsequent invocation returns pre-built context in milliseconds. The repo doesn't change? The cache doesn't expire.
22
+
23
+ **The cache is not a performance optimization. It is what makes ASK Engine usable as infrastructure rather than a one-off tool.**
24
+
25
+ ---
26
+
27
+ ## Proof — measured on real repos
28
+
29
+ | Repo | Size | Cold scan | Cache hit | Speedup |
30
+ |------|------|-----------|-----------|---------|
31
+ | Keycloak | 7,885 Java files | 10.5s | 0.6s | **~17x** |
32
+ | BroadleafCommerce | 2,985 Java files | 2.7s | 0.3s | **~9x** |
33
+
34
+ Cache keyed on content hashes — invalidated only when source changes. On repeated agent sessions against the same codebase, nearly every invocation is a cache hit.
35
+
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
+
38
+ ---
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ # Homebrew (macOS / Linux)
44
+ brew tap haroundominique/sourcecode && brew install sourcecode
45
+
46
+ # pip / pipx
47
+ pipx install sourcecode # or: pip install sourcecode
48
+
49
+ ask version # ask 2.1.0
50
+ ```
51
+
52
+ > **Package vs. command.** The install package is named `sourcecode` this release
53
+ > (renaming the distribution is a separate, breaking change). Installing it gives you the
54
+ > canonical **`ask`** command plus the deprecated **`sourcecode`** alias.
55
+
56
+ ---
57
+
58
+ ## Quickstart
59
+
60
+ ```bash
61
+ # High-signal structural summary — warm cache ~0.3s, cold 2–10s
62
+ ask --compact
63
+
64
+ # Blast radius: what breaks if this class changes?
65
+ ask impact OrderService /path/to/repo
66
+
67
+ # Spring Boot 2→3 migration readiness (bounded decision summary)
68
+ ask migrate-check /path/to/repo --compact
69
+
70
+ # Spring semantic audit: TX anomalies + security surface
71
+ ask spring-audit /path/to/repo
72
+
73
+ # Onboard to an unfamiliar codebase
74
+ ask onboard /path/to/repo
75
+
76
+ # PR review: risk, test gaps, changed modules
77
+ ask review-pr /path/to/repo --since main
78
+ ```
79
+
80
+ Full command reference: **[docs/USER_GUIDE.md](docs/USER_GUIDE.md)**.
81
+
82
+ ---
83
+
84
+ ## Capabilities
85
+
86
+ Everything is computed from one cached structural model. Seven groups:
87
+
88
+ ### 1 · Structural Context
89
+ Bounded, noise-free repo context designed to drop straight into an agent's context window.
90
+ `ask --compact` · `ask --agent` · `ask onboard` · `ask cold-start`
91
+ → [reference](docs/USER_GUIDE.md#core-commands)
92
+
93
+ ### 2 · Impact Analysis
94
+ Blast radius from a class or interface — reverse dependencies, through Spring DI, to the HTTP endpoints a change reaches.
95
+ `ask impact` · `ask impact-chain` (TX/SEC-enriched) · `ask pr-impact`
96
+ → [reference](docs/USER_GUIDE.md#core-commands)
97
+
98
+ ### 3 · Architecture Intelligence
99
+ The system map: module graph, dependency views, REST surface, per-class summaries, and a symbol-level IR for downstream tooling.
100
+ `ask export` · `ask repo-ir` · `ask endpoints` · `ask explain`
101
+ → [reference](docs/USER_GUIDE.md#core-commands)
102
+
103
+ ### 4 · Migration & Modernization
104
+ Is this codebase ready to upgrade? Per-dimension readiness (Jakarta / Spring Boot / JDK / Hibernate), located blockers, and an effort estimate.
105
+ `ask migrate-check` · `ask modernize`
106
+ → [migrate-check reference](docs/migrate-check.md) · [MODERNIZATION.md](docs/MODERNIZATION.md)
107
+
108
+ ### 5 · Spring Analysis
109
+ Deterministic Spring semantics: transactional anomalies (e.g. `@Transactional` on a private method = silent CGLIB no-op), security surface, request-body validation.
110
+ `ask spring-audit` · `ask validation`
111
+ → [reference](docs/USER_GUIDE.md#core-commands)
112
+
113
+ ### 6 · Developer Workflows
114
+ The everyday loop: diff-based PR review, symptom-driven bug triage, and delta context for continuous agent runs.
115
+ `ask review-pr` · `ask fix-bug` · `ask prepare-context`
116
+ → [reference](docs/USER_GUIDE.md#typical-workflows)
117
+
118
+ ### 7 · Utilities
119
+ `ask rename-class` (word-boundary Java rename) · `ask chunk-file` (split large files for agents) · `ask cache` (status / warm / clear / freshness)
120
+ → [reference](docs/USER_GUIDE.md)
121
+
122
+ ---
123
+
124
+ ## What it does — and doesn't
125
+
126
+ **ASK Engine reduces exploration cost.** It accelerates context acquisition and computes
127
+ blast radius; it does not replace reading code — it reduces how often an agent needs to.
128
+ All signals are **static and deterministic** (annotations, import graph, file structure) —
129
+ no runtime analysis, no LLM guessing.
130
+
131
+ Honest limits worth knowing before you rely on it:
132
+
133
+ - `impact` on an **implementation** class (`OrderServiceImpl`) returns 0 callers in Spring Boot — callers inject the interface. **Always target the interface.**
134
+ - `no_security_signal` on an endpoint means *no recognized method-level annotation*, **not** "unsecured" — Spring Security filter chains and custom authorization annotations show as `no_security_signal` unless taught via config (below).
135
+ - `spring-audit` / `impact-chain` are **Java/Spring only**; non-Java repos return `spring_detected: false`.
136
+ - Event topology (`--type events`) resolves Spring `ApplicationEvent` / `@EventListener` chains only — **not** Kafka/RabbitMQ/Redis routes.
137
+ - Architecture classification is tuned for Spring MVC layered apps; SPI/plugin models (e.g. Quarkus extensions) may be misclassified. JAX-RS subresource-locator endpoint recall is ~65%.
138
+ - Self-invocation `@Transactional` bypass (same-class call skipping the proxy) is not detected.
139
+
140
+ ---
141
+
142
+ ## Pricing
143
+
144
+ > **🎉 Early-adoption: Pro is currently unlocked for everyone.** Every install runs with
145
+ > full Pro entitlements — no size gate, no key. The tiers below describe the model the
146
+ > paywall will return to later.
147
+
148
+ **Gating is by repo size and automation — never by command.** Every command runs at full
149
+ power on Free for small and mid-size repos; you upgrade when the work gets bigger or automated.
150
+
151
+ | | **Free** — €0 | **Pro** — €19/mo · €190/yr per dev |
152
+ |---|---|---|
153
+ | Repo size | ≤ 500 Java source files | **> 500 Java files** (enterprise monoliths) |
154
+ | Commands | All of them, full output | Same commands, unlocked at scale |
155
+ | `impact` / `fix-bug` / `review-pr` / `modernize` | ✅ full on small repos | ✅ full on large repos (Free gets a capped preview) |
156
+ | `prepare-context delta` | 30 free runs/repo | unlimited — CI/CD automation |
157
+ | MCP local server, offline, no data egress | ✅ | ✅ |
158
+
159
+ **Non-Java repos are free at any size** — the size limit counts Java source files only.
160
+ ASK Engine monetises enterprise Java monoliths. Activate with `ask activate <key>`.
161
+ Full breakdown: [docs/PRODUCT_TIERS.md](docs/PRODUCT_TIERS.md).
162
+
163
+ ---
164
+
165
+ ## Configuration & privacy
166
+
167
+ ```bash
168
+ ask config # version, config file path, telemetry status
169
+ ask telemetry disable # anonymous telemetry is on by default (opt-out)
170
+ ```
171
+
172
+ Telemetry collects version, OS, commands, flags, duration, repo-size range, and errors —
173
+ **no source code, paths, secrets, or output**. Disable any time with
174
+ `export SOURCECODE_TELEMETRY=0` (or `DO_NOT_TRACK=1`).
175
+
176
+ **Custom security annotations.** Teach `endpoints`, `spring-audit`, and `explain` about
177
+ project-specific authorization annotations via an optional `sourcecode.config.json` at the
178
+ repo root (otherwise they report `policy: "none_detected"`):
179
+
180
+ ```json
181
+ {
182
+ "customSecurityAnnotations": [
183
+ { "fullyQualifiedName": "com.example.security.M3FiltroSeguridad", "shortName": "M3FiltroSeguridad" }
184
+ ]
185
+ }
186
+ ```
187
+
188
+ Matching endpoints report `policy: "custom"` and drop out of the `no_security_signal` count.
189
+
190
+ ---
191
+
192
+ ## Documentation
193
+
194
+ | Doc | What it covers |
195
+ |-----|----------------|
196
+ | [USER_GUIDE.md](docs/USER_GUIDE.md) | Full command reference, flags, output schema, workflows |
197
+ | [migrate-check.md](docs/migrate-check.md) | Migration rule catalogue (MIG-001..043) + Hibernate stratification |
198
+ | [MODERNIZATION.md](docs/MODERNIZATION.md) | The modernization product: assess → understand → plan → execute |
199
+ | [PRODUCT_TIERS.md](docs/PRODUCT_TIERS.md) | Free vs Pro, pricing model |
200
+ | [DEMO-5MIN.md](docs/DEMO-5MIN.md) | A reproducible 5-minute demo |
201
+ | [MANUAL-USUARIO.md](docs/MANUAL-USUARIO.md) | Guía de usuario en español |
202
+ | [PRODUCT_IDENTITY.md](docs/PRODUCT_IDENTITY.md) | `ask` (command) vs `sourcecode` (package/alias) |
203
+ | [privacy.md](docs/privacy.md) | Telemetry and data-handling policy |
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "2.0.0"
7
+ version = "2.1.0"
8
8
  description = "Persistent structural context and ultra-fast repeated analysis for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -4,4 +4,4 @@ ASK Engine is the product. ``ask`` is the canonical CLI command; ``sourcecode``
4
4
  the legacy compatibility alias and the Python/PyPI package name. See
5
5
  docs/PRODUCT_IDENTITY.md (normative)."""
6
6
 
7
- __version__ = "2.0.0"
7
+ __version__ = "2.1.0"
@@ -4496,7 +4496,18 @@ def validation_cmd(
4496
4496
  from sourcecode.context_graph import ContextGraph
4497
4497
  from sourcecode.validation_surface import build_validation_surface
4498
4498
  # Structural facts come from the ContextGraph — the single access layer.
4499
- data = build_validation_surface(target, graph=ContextGraph.build_from_root(target))
4499
+ _graph = ContextGraph.build_from_root(target)
4500
+ data = build_validation_surface(target, graph=_graph)
4501
+
4502
+ # P1-C: classify the request-body validation *pattern* so a repo with a
4503
+ # validation framework on the classpath but no @Valid reads as
4504
+ # "present-but-unused" instead of a silent sea of zeros (DESIGN §2/§5).
4505
+ try:
4506
+ from sourcecode.validation_inference import infer_validation_pattern
4507
+
4508
+ data["validation_pattern"] = infer_validation_pattern(_graph.cir).to_dict()
4509
+ except Exception:
4510
+ pass
4500
4511
 
4501
4512
  if path_prefix:
4502
4513
  data["endpoints"] = [
@@ -4836,6 +4847,16 @@ def migrate_check_cmd(
4836
4847
  help="Minimum severity to include: critical, high, medium, or low (default).",
4837
4848
  show_default=True,
4838
4849
  ),
4850
+ compact: bool = typer.Option(
4851
+ False,
4852
+ "--compact",
4853
+ help=(
4854
+ "Bounded decision summary (JSON): readiness scores, headline blocker, "
4855
+ "effort, executive summary, Hibernate classification and aggregate "
4856
+ "counts, with large collections capped to their top items (+N more). "
4857
+ "No new analysis — same report, projected. Full detail is the default."
4858
+ ),
4859
+ ),
4839
4860
  no_cache: bool = typer.Option(
4840
4861
  False, "--no-cache",
4841
4862
  help="Accepted for compatibility; this command always reads fresh source (no snapshot cache). No-op.",
@@ -4867,6 +4888,7 @@ def migrate_check_cmd(
4867
4888
  \b
4868
4889
  Examples:
4869
4890
  ask migrate-check .
4891
+ ask migrate-check . --compact bounded decision summary
4870
4892
  ask migrate-check /path/to/repo --format text
4871
4893
  ask migrate-check . --min-severity high
4872
4894
  ask migrate-check . --output migration.json
@@ -4905,7 +4927,11 @@ def migrate_check_cmd(
4905
4927
  if format == "text":
4906
4928
  output = report.to_text(min_severity=min_severity)
4907
4929
  else:
4908
- output = _serialize_dict(report.to_dict(), "json")
4930
+ # --compact projects the SAME report into a bounded, decision-grade view
4931
+ # (output-shaping only). Default (full) output is unchanged — it is the
4932
+ # baseline-oracle form and must stay byte-identical.
4933
+ payload = report.to_compact_dict() if compact else report.to_dict()
4934
+ output = _serialize_dict(payload, "json")
4909
4935
 
4910
4936
  _total = report.summary.get("total_findings", 0)
4911
4937
  _emit_command_output(
@@ -1308,6 +1308,33 @@ def _dimension_score(
1308
1308
  return max(0, 100 - deduction)
1309
1309
 
1310
1310
 
1311
+ # --- Compact projection (output-shaping only) --------------------------------
1312
+ # `--compact` renders a bounded, decision-grade view of the SAME report: it caps
1313
+ # each large leaf collection to its top-N items and reports total/omitted counts.
1314
+ # It performs NO new analysis — it is a pure projection over the dict `to_dict()`
1315
+ # already produces (Semantic IR / ContextGraph / semantic engines untouched).
1316
+ # The default (full) output is unchanged and remains the baseline-oracle form.
1317
+ # FUTURE (not this increment): invert the default — emit compact by default and
1318
+ # add `--full` for the complete report — once the oracle baseline is recaptured.
1319
+ _COMPACT_TOP_N = 5
1320
+
1321
+
1322
+ def _cap_collection(items: list, top_n: int, key=None) -> dict:
1323
+ """Project a list into a bounded `{total, shown, omitted, items}` view. Items
1324
+ are optionally reordered by `key` (stable) so the retained slice is the most
1325
+ important; ordering falls back to the engine's own emission order. No values
1326
+ are recomputed — only selected and counted."""
1327
+ total = len(items)
1328
+ ordered = sorted(items, key=key) if key is not None else items
1329
+ shown = ordered[:top_n]
1330
+ return {
1331
+ "total": total,
1332
+ "shown": len(shown),
1333
+ "omitted": max(0, total - len(shown)),
1334
+ "items": shown,
1335
+ }
1336
+
1337
+
1311
1338
  @dataclass
1312
1339
  class MigrationReport:
1313
1340
  schema_version: str = "1.4"
@@ -1679,6 +1706,43 @@ class MigrationReport:
1679
1706
  "metadata": self.metadata,
1680
1707
  }
1681
1708
 
1709
+ def to_compact_dict(self, top_n: int = _COMPACT_TOP_N) -> dict:
1710
+ """Bounded, decision-grade projection of `to_dict()`.
1711
+
1712
+ Keeps every decision field verbatim (readiness_score, per-dimension
1713
+ readiness, headline_blocker, blocking_count, estimated_effort_days,
1714
+ effort_breakdown, summary, Hibernate classification, aggregate counts) and
1715
+ caps only the large leaf collections — top-level `findings` and the
1716
+ Hibernate `rewrite_targets` / `findings` / `critical_call_chains` /
1717
+ `golden_sql_hotspots` — to their top-`top_n` items, each wrapped as
1718
+ `{total, shown, omitted, items}`. It reuses the already-computed
1719
+ `to_dict()` output and recomputes nothing (output-shaping only)."""
1720
+ full = self.to_dict()
1721
+ compact = dict(full)
1722
+ compact["mode"] = "compact"
1723
+ compact["compact_note"] = (
1724
+ f"Bounded decision summary: large collections are capped to the top {top_n} "
1725
+ "items (see each collection's total/omitted). Run without --compact (JSON "
1726
+ "default) for the complete report. A future release will invert this — "
1727
+ "compact by default with --full for the full detail."
1728
+ )
1729
+ # Top-level findings: retain the most severe first, then engine order.
1730
+ compact["findings"] = _cap_collection(
1731
+ full["findings"], top_n,
1732
+ key=lambda f: SEVERITY_ORDER.get(f.get("severity"), 3),
1733
+ )
1734
+ hib = full.get("hibernate")
1735
+ if isinstance(hib, dict):
1736
+ h = dict(hib)
1737
+ for _col in (
1738
+ "rewrite_targets", "findings",
1739
+ "critical_call_chains", "golden_sql_hotspots",
1740
+ ):
1741
+ if isinstance(h.get(_col), list):
1742
+ h[_col] = _cap_collection(h[_col], top_n)
1743
+ compact["hibernate"] = h
1744
+ return compact
1745
+
1682
1746
  def to_text(self, min_severity: str = "low") -> str:
1683
1747
  min_order = SEVERITY_ORDER.get(min_severity, 3)
1684
1748
  visible = [f for f in self.findings if SEVERITY_ORDER.get(f.severity, 3) <= min_order]