sourcecode 3.1.0__tar.gz → 3.2.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.
- {sourcecode-3.1.0 → sourcecode-3.2.0}/CHANGELOG.md +184 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/PKG-INFO +3 -3
- {sourcecode-3.1.0 → sourcecode-3.2.0}/README.md +2 -2
- {sourcecode-3.1.0 → sourcecode-3.2.0}/pyproject.toml +1 -1
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/cli.py +128 -9
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/java.py +10 -5
- sourcecode-3.2.0/src/sourcecode/integration_coordinates.py +175 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/mcp/registry.py +3 -1
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/mcp/server.py +5 -2
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/migrate_check.py +22 -1
- sourcecode-3.2.0/src/sourcecode/migration_blast.py +232 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/repository_ir.py +25 -17
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/steps_graph.py +10 -8
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/steps_intf.py +23 -1
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/security_posture.py +165 -19
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/semantic_integration_engine.py +54 -2
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/spring_impact.py +124 -7
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/spring_semantic.py +56 -4
- {sourcecode-3.1.0 → sourcecode-3.2.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/.gitignore +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/.ruff.toml +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/CONTRIBUTING.md +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/LICENSE +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/SECURITY.md +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/raw +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/scripts/compare_integration_engines.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/scripts/customer_smoke_test.sh +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/scripts/generate_jdk_exports.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/scripts/perf_harness.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/archetype.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/architectural_baseline.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/architectural_delta.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/cache.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/call_surface.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/caller_metrics.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/canonical_ir.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/chain_rules.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/change_plan.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/cir_graphs.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/compare.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/constraint_diff.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/context_cache.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/context_graph.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/contract_diff.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/deployment_prefix.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/dynamic_argument_surface.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/endpoint_literals.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/endpoint_metrics.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/envelope.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/error_schema.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/evidence_provider.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/explain.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/file_chunker.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/filter_surface.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/format_contract.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/fqn_utils.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/graph_evidence.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/hibernate_strat.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/jdk_exports.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/license.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/mcp/onboarding/detector.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/mcp/orchestrator.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/mcp/runner.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/mcp_nudge.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/openapi_surface.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/output_budget.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/parse_cache.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/path_filters.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/perf.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/posture.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/pr_impact.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/prepare_context.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/progress.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/readiness_timeline.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/reconciliation.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/rename_refactor.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/__init__.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/context.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/errors.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/executor.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/planner.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/query.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/request.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/resolution.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/result.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/retriever.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/runtime.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/steps.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/steps_impact.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/steps_struct.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/retrieval/steps_txsec.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/ris.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/schema.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/security_config.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/semantic_impact_engine.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/semantic_services.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/serializer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/spring_event_topology.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/spring_findings.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/spring_model.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/spring_profiles.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/spring_properties.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/spring_security_audit.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/spring_tx_analyzer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/token_estimate.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/type_usage_surface.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/validation_inference.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/validation_surface.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/verify_edit.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/verify_repo.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/verify_rules.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/version_check.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/supabase/functions/README.md +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/supabase/functions/get-license/index.ts +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/supabase/functions/telemetry/index.ts +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/supabase/sql/license_event_ordering.sql +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/supabase/sql/licensing_schema.sql +0 -0
- {sourcecode-3.1.0 → sourcecode-3.2.0}/supabase/sql/telemetry_events.sql +0 -0
|
@@ -1,5 +1,189 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.2.0] — 2026-07-28
|
|
4
|
+
|
|
5
|
+
**Answer coherence (M7, first four seams).** One fact, one authority (ADR-0008). Four
|
|
6
|
+
questions this tool answered twice — transaction boundaries, endpoint denominators, endpoint
|
|
7
|
+
security coverage, outbound integrations — now have a single authority each, and every
|
|
8
|
+
consumer binds to it instead of re-deriving.
|
|
9
|
+
|
|
10
|
+
**Upgrade note.** Two behaviours change for anyone gating on the output:
|
|
11
|
+
|
|
12
|
+
- `impact` / `impact-chain` / `plan` / `compare` now count transaction boundaries that were
|
|
13
|
+
structurally reported as `0` on every repository. Risk scores move **up**: on a
|
|
14
|
+
200-class BroadleafCommerce sample, 42 classes go from 0 to ≥1 boundary and 11 change
|
|
15
|
+
`risk_level`, always upward. A pinned `--fail-on` threshold can now trip where it did not.
|
|
16
|
+
- `impact-chain.security_surfaces[]` and `endpoints_affected[]` gain `verdict` +
|
|
17
|
+
`confidence` and no longer let the declared annotation policy stand as a coverage answer.
|
|
18
|
+
`security_policy` is kept as a deprecated alias of `declared_policy` (two minor versions),
|
|
19
|
+
with its value unchanged.
|
|
20
|
+
|
|
21
|
+
New payload keys (`population`, `declared_clients`, three-part endpoint partitions) are
|
|
22
|
+
additive; the deprecated aliases listed below keep their previous values.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **`export --integrations` answered "0 LDAP" in documents whose stack block named Spring
|
|
27
|
+
LDAP.** Reported in two field evaluations two majors apart. Reproduced on a Boot app that
|
|
28
|
+
declares `spring-boot-starter-data-ldap` and configures `spring.ldap.urls` in YAML: the
|
|
29
|
+
directory is wired entirely by properties, so no LDAP client type is ever imported and the
|
|
30
|
+
construct scan is *right* to find nothing — but the report never mentioned the declaration
|
|
31
|
+
it could see, and the document as a whole said the system has no outbound integrations
|
|
32
|
+
while naming a directory client library elsewhere.
|
|
33
|
+
- New authority `integration_coordinates.declared_integration_coordinates(root)` answers
|
|
34
|
+
one question — which client libraries the build declares — from Maven/Gradle
|
|
35
|
+
coordinates with `file:line` evidence, excluding test/provided scope.
|
|
36
|
+
- `export --integrations` (and `export --c4`) now carry `declared_clients`
|
|
37
|
+
(`kind`, `coordinate`, `evidence`, `observed_in_source`), `declared_clients_count` and
|
|
38
|
+
`kinds_declared_not_observed`, and the coverage reason names the declared kind and its
|
|
39
|
+
coordinate. A declared coordinate is never counted as an integration: `count` and
|
|
40
|
+
`by_kind` are unchanged and still report observed constructs only.
|
|
41
|
+
- `retrieve integration-inventory` states the same declared-not-observed kinds as
|
|
42
|
+
observations.
|
|
43
|
+
- `detectors/java.py` reads its directory coordinates from that one list, so the stack
|
|
44
|
+
block and the integration report can never recognize different libraries for the same
|
|
45
|
+
external system (ADR-0008 R12).
|
|
46
|
+
- Measured: alfresco declares `cxf-rt-frontend-jaxws` with no SOAP construct in source —
|
|
47
|
+
previously invisible, now stated. Broadleaf's declared JMS/LDAP coordinates are all
|
|
48
|
+
marked `observed_in_source: true`; repositories that declare no client library keep an
|
|
49
|
+
empty block. The `export --integrations` and `export --c4` baseline cells drift by
|
|
50
|
+
these added keys, by design.
|
|
51
|
+
|
|
52
|
+
- **`impact-chain` called endpoints "none_detected" that the same run classified as guarded
|
|
53
|
+
by a custom authorization mechanism.** Measured on keycloak: `security_posture` reports
|
|
54
|
+
406 endpoints `protected_custom` (Likely) — 324 of them carrying the declared policy
|
|
55
|
+
`none_detected`, because their gate is a bespoke annotation outside every published
|
|
56
|
+
vocabulary — while `impact-chain` printed `security_policy: none_detected` for those same
|
|
57
|
+
`endpoint_id`s. One document answered both "guarded by a custom mechanism" and "no
|
|
58
|
+
security detected" about `DELETE /admin/realms/{realm}`. The declared policy is a fact
|
|
59
|
+
about an annotation; it was never an answer about coverage.
|
|
60
|
+
- New authority `security_posture.endpoint_security_surface(cir)` returns the per-endpoint
|
|
61
|
+
answer keyed by `endpoint_id` (ADR-0008 R1). `impact-chain` binds to it and reuses the
|
|
62
|
+
projection the security audit already derived — no second derivation, memoized per CIR
|
|
63
|
+
so `pr-impact`'s per-class queries pay for it once.
|
|
64
|
+
- `security_surfaces[]` and `endpoints_affected[]` now carry `verdict` + `confidence`
|
|
65
|
+
(`security_verdict` / `security_confidence` on affected endpoints) from the four-way
|
|
66
|
+
posture vocabulary, plus `declared_policy` for the annotation actually found. On the
|
|
67
|
+
28 endpoints reachable from `RealmAdminResource`, all 28 move `none_detected` →
|
|
68
|
+
`protected_custom` (Likely), matching `spring-audit` exactly.
|
|
69
|
+
- An endpoint the authority cannot speak about — or a repository with no posture
|
|
70
|
+
projection — is `coverage_unknown` with the reason stated, never a manufactured "none"
|
|
71
|
+
(ADR-0008 R9). On spring-petclinic all 17 endpoints read `coverage_unknown` in both
|
|
72
|
+
commands instead of `none_detected` in one and `coverage_unknown` in the other.
|
|
73
|
+
- `security_policy` is kept as a deprecated alias of `declared_policy` for two minor
|
|
74
|
+
versions; its value is unchanged.
|
|
75
|
+
|
|
76
|
+
- **The security posture reported three counts under names that all read as "handlers", and
|
|
77
|
+
they did not reconcile.** The field report put `custom_authorization_mechanisms` at
|
|
78
|
+
713/936, `gate_coverage` at 698 and 2635, and a rollup of 939 + 2635 = 3574 — the only
|
|
79
|
+
figure matching `endpoints.total`. None was arithmetically wrong: they counted three
|
|
80
|
+
different populations, and no key said which. Reproduced exactly on keycloak, where the
|
|
81
|
+
old payload showed `gate_bearing: 337` beside `not_carrying_gate: 270` against a stated
|
|
82
|
+
total of 676 — a partition that visibly loses 69 endpoints, because the first number
|
|
83
|
+
counts handler *methods* and the second counts *endpoints*.
|
|
84
|
+
- One authority, `security_posture.endpoint_population`, now supplies every denominator,
|
|
85
|
+
and the posture payload carries a `population` block stating both units once
|
|
86
|
+
(ADR-0008 R1).
|
|
87
|
+
- `gate_coverage` gains an explicit three-part endpoint partition —
|
|
88
|
+
`endpoints_carrying_gate` + `endpoints_standard_guarded` +
|
|
89
|
+
`endpoints_not_carrying_gate` = `endpoints_total` — counted in the single loop that
|
|
90
|
+
visits each endpoint once, so the parts sum by construction. On keycloak:
|
|
91
|
+
406 + 0 + 270 = 676. Handler-method figures keep their own names
|
|
92
|
+
(`handler_methods_total`, `handler_methods_carrying_gate`) and are never part of that
|
|
93
|
+
sum.
|
|
94
|
+
- Custom-gate detection states the unit its percentage used: the denominator is handler
|
|
95
|
+
method *declarations* (overloads share an FQN, so keycloak has 551 distinct handler
|
|
96
|
+
methods across 601 declarations), and `endpoints_total` / `handler_methods_total` are
|
|
97
|
+
printed beside it so no reader has to infer which one a coverage figure meant.
|
|
98
|
+
- `total_controller_handlers`, `gate_bearing`, `not_carrying_gate` and `handler_total`
|
|
99
|
+
are kept as deprecated aliases with their existing values for two minor versions. No
|
|
100
|
+
consumer breaks; the numbers they held did not change.
|
|
101
|
+
|
|
102
|
+
- **`impact` counted zero transaction boundaries on every repository — corrected, and the
|
|
103
|
+
two commands that answer this question now answer it once.** The field report was that
|
|
104
|
+
`impact` reported `transactional_boundaries_count: 0` for a symbol while `retrieve
|
|
105
|
+
transactions-reaching` reported 47 for the same one. The cause was worse than a
|
|
106
|
+
disagreement: the blast radius derived boundaries from a call-graph node
|
|
107
|
+
`role == "transaction_boundary"` that no producer ever assigns — 0 of 28 662 nodes carry
|
|
108
|
+
it on BroadleafCommerce — so the figure was **structurally always zero**, and the
|
|
109
|
+
`n_txn` term never contributed to `risk_score` or to the `n_txn >= 2` escalation rule
|
|
110
|
+
behind `risk_level`.
|
|
111
|
+
- Both consumers now ask one authority, `spring_semantic.boundaries_declared_within`
|
|
112
|
+
over the transaction-boundary index (ADR-0008 R1), which follows Spring's own
|
|
113
|
+
inheritance in both directions: a method carries its class's boundary, a class carries
|
|
114
|
+
the boundaries of the methods it declares, and asking about one method never sweeps in
|
|
115
|
+
a sibling's.
|
|
116
|
+
- Two named facts, one authority (ADR-0008 R3): `transactional_boundaries_touched` is
|
|
117
|
+
the target plus everything that calls it; the new `transactional_boundaries_reaching`
|
|
118
|
+
is the callers-only subset — what would run this change inside a transaction — and is
|
|
119
|
+
the number `retrieve transactions-reaching` reports, now by construction rather than
|
|
120
|
+
by coincidence.
|
|
121
|
+
- **Figures move, upward, because a real cost was being counted as nothing.** Measured
|
|
122
|
+
on BroadleafCommerce (161 declared boundaries): `CatalogServiceImpl` 0 → 10 boundaries
|
|
123
|
+
and `risk_score` 76.73 → 77.54; `OrderServiceImpl` 0 → 32 and 73.83 → 76.86;
|
|
124
|
+
`CustomerServiceImpl` 0 → 32 and 66.10 → 71.01. Across a 200-class sample, 42 classes
|
|
125
|
+
now report at least one boundary and 11 change `risk_level` — every one of them
|
|
126
|
+
upward. A pinned `--fail-on` threshold may therefore trip where it did not before;
|
|
127
|
+
that is the correction, not a regression.
|
|
128
|
+
|
|
129
|
+
### Added
|
|
130
|
+
|
|
131
|
+
- **ADR-0008 "Answer Coherence" — one fact, one authority (normative, no behaviour change
|
|
132
|
+
yet).** Two independent field evaluations, on releases two majors apart, scored the
|
|
133
|
+
product identically and named the same ceiling: not a missing capability, but the same
|
|
134
|
+
question answered twice with different numbers (`impact` reporting 0 transactional
|
|
135
|
+
boundaries where `retrieve` reports 47; `compare` ranking on caller arrays truncated at
|
|
136
|
+
30 while the exact magnitudes sit in `stats`; `has_tests` contradicting `analysis_gaps`
|
|
137
|
+
inside one document). The ADR fixes the rule the codebase had never stated: a fact has
|
|
138
|
+
exactly one authority, consumers transport it, quantities never come from the length of a
|
|
139
|
+
rendered collection, and a surface that cannot resolve emits `unknown` — never `0`. It
|
|
140
|
+
also changes the unit of remediation from *defect* to *fact*: a coherence fix is complete
|
|
141
|
+
only when the authority is corrected, every consumer is rebound, and a cross-command
|
|
142
|
+
parity assertion is added to the release battery.
|
|
143
|
+
- `docs/architecture/adr/0008-answer-coherence.md` — the rule, with the code-verified seam
|
|
144
|
+
inventory in Appendix A.
|
|
145
|
+
- `docs/DEFECT-LEDGER.md` — every field defect, its class, and the release that closed it,
|
|
146
|
+
published rather than tracked privately.
|
|
147
|
+
- `.planning/FIELD-EVAL-SYNTHESIS-2026-07-28.md` and
|
|
148
|
+
`.planning/ROADMAP-STRATEGIC-2026-07-28.md` — the evidence and the resulting direction
|
|
149
|
+
(M7 Coherence → M8 Payload economy → M9 Core & surface → M10 Enrichment interop →
|
|
150
|
+
M11 Compliance substrate).
|
|
151
|
+
|
|
152
|
+
- **`migrate-check --blast-radius` states the regression scope behind each blocker.** The
|
|
153
|
+
finding list says what must change; a migration plan is built around what has to be
|
|
154
|
+
re-tested when that change lands. Each affected product file now resolves to the types
|
|
155
|
+
it declares and runs through the same caller traversal `impact-chain` uses, so the
|
|
156
|
+
block reports the HTTP endpoints whose call path passes through it, ranked. Measured on
|
|
157
|
+
BroadleafCommerce: 62.3% of the request surface (137 of 220 endpoints) runs through a
|
|
158
|
+
file with a finding, and 52 of the 166 affected files carry all of it. Implemented as a
|
|
159
|
+
rule over the existing IR — no new detector, ~3 ms per affected file on top of an IR
|
|
160
|
+
build shared with `impact-chain` / `explain` through the context cache.
|
|
161
|
+
- Reach is regression scope, never build breakage: a `javax→jakarta` finding fails
|
|
162
|
+
compilation whether or not any endpoint reaches it, and the payload says so.
|
|
163
|
+
- Unresolved is not zero — a file whose declared types the IR never resolved is listed
|
|
164
|
+
in `files_unresolved` with reach *unknown*.
|
|
165
|
+
- A repository the IR resolves no endpoints in (alfresco's webscript framework,
|
|
166
|
+
openmrs-core) gets `request_surface_share: null` and a note that ranking fell back to
|
|
167
|
+
caller counts, never a manufactured `0.0`.
|
|
168
|
+
- Opt-in and additive: without the flag the report is byte-identical to before, and the
|
|
169
|
+
key is absent rather than `null`.
|
|
170
|
+
|
|
171
|
+
## [3.1.1] — 2026-07-28
|
|
172
|
+
|
|
173
|
+
Documentation only; no behaviour changed.
|
|
174
|
+
|
|
175
|
+
- **`contracts.md` gains an enforcement-maturity section**: which contract kinds have been
|
|
176
|
+
run against real code and with what result, the one false block found and fixed (a
|
|
177
|
+
fully-qualified security annotation reading as no guard, 3.1.0), and what is still
|
|
178
|
+
unproven — the GitHub Actions workflow has never run in a hosted runner. Plus a
|
|
179
|
+
three-week rollout that starts advisory (`--fail-on never`), because a contract whose
|
|
180
|
+
first act is blocking a merge gets deleted.
|
|
181
|
+
- **`migrate-check.md` documents the output-size guard** added in 3.1.0: the measured
|
|
182
|
+
sizes that motivated it, the emitted `OUTPUT_TOO_LARGE` payload, and the four ways past
|
|
183
|
+
it in the order worth trying.
|
|
184
|
+
- Doc-accuracy tests cover the new claims: the rollout flags exist, every documented
|
|
185
|
+
contract kind is implemented, and the quoted guard hint is the one the code emits.
|
|
186
|
+
|
|
3
187
|
## [3.1.0] — 2026-07-28
|
|
4
188
|
|
|
5
189
|
**Posture answers about requests, not only about beans.** Milestone M4 of the strategic
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
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
|
|
@@ -139,7 +139,7 @@ The system map: module graph, dependency views, REST surface, per-class summarie
|
|
|
139
139
|
→ [reference](docs/USER_GUIDE.md#core-commands)
|
|
140
140
|
|
|
141
141
|
### 4 · Migration & Modernization
|
|
142
|
-
Is this codebase ready to upgrade? Per-dimension readiness (Jakarta / Spring Boot / JDK / Hibernate), located blockers,
|
|
142
|
+
Is this codebase ready to upgrade? Per-dimension readiness (Jakarta / Spring Boot / JDK / Hibernate), located blockers, an effort estimate, and — with `--blast-radius` — the endpoints whose call path runs through each blocker, so the re-test plan is ordered by regression scope.
|
|
143
143
|
`ask migrate-check` · `ask modernize`
|
|
144
144
|
→ [migrate-check reference](docs/migrate-check.md) · [MODERNIZATION.md](docs/MODERNIZATION.md)
|
|
145
145
|
|
|
@@ -233,7 +233,7 @@ Matching endpoints report `policy: "custom"` and drop out of the `no_security_si
|
|
|
233
233
|
|-----|----------------|
|
|
234
234
|
| [USER_GUIDE.md](docs/USER_GUIDE.md) | Full command reference, flags, output schema, workflows |
|
|
235
235
|
| [contracts.md](docs/contracts.md) | Declare invariants in `.ask/contracts.yml`, enforce them in the edit loop and in CI |
|
|
236
|
-
| [posture.md](docs/posture.md) | What a profile set actually wires
|
|
236
|
+
| [posture.md](docs/posture.md) | What a profile set actually wires — down to which endpoints its filter chain permits — and what could not be decided (experimental) |
|
|
237
237
|
| [migrate-check.md](docs/migrate-check.md) | Migration rule catalogue (MIG-001..043) + Hibernate stratification |
|
|
238
238
|
| [MODERNIZATION.md](docs/MODERNIZATION.md) | The modernization product: assess → understand → plan → execute |
|
|
239
239
|
| [PRODUCT_TIERS.md](docs/PRODUCT_TIERS.md) | Free vs Pro, pricing model |
|
|
@@ -101,7 +101,7 @@ The system map: module graph, dependency views, REST surface, per-class summarie
|
|
|
101
101
|
→ [reference](docs/USER_GUIDE.md#core-commands)
|
|
102
102
|
|
|
103
103
|
### 4 · Migration & Modernization
|
|
104
|
-
Is this codebase ready to upgrade? Per-dimension readiness (Jakarta / Spring Boot / JDK / Hibernate), located blockers,
|
|
104
|
+
Is this codebase ready to upgrade? Per-dimension readiness (Jakarta / Spring Boot / JDK / Hibernate), located blockers, an effort estimate, and — with `--blast-radius` — the endpoints whose call path runs through each blocker, so the re-test plan is ordered by regression scope.
|
|
105
105
|
`ask migrate-check` · `ask modernize`
|
|
106
106
|
→ [migrate-check reference](docs/migrate-check.md) · [MODERNIZATION.md](docs/MODERNIZATION.md)
|
|
107
107
|
|
|
@@ -195,7 +195,7 @@ Matching endpoints report `policy: "custom"` and drop out of the `no_security_si
|
|
|
195
195
|
|-----|----------------|
|
|
196
196
|
| [USER_GUIDE.md](docs/USER_GUIDE.md) | Full command reference, flags, output schema, workflows |
|
|
197
197
|
| [contracts.md](docs/contracts.md) | Declare invariants in `.ask/contracts.yml`, enforce them in the edit loop and in CI |
|
|
198
|
-
| [posture.md](docs/posture.md) | What a profile set actually wires
|
|
198
|
+
| [posture.md](docs/posture.md) | What a profile set actually wires — down to which endpoints its filter chain permits — and what could not be decided (experimental) |
|
|
199
199
|
| [migrate-check.md](docs/migrate-check.md) | Migration rule catalogue (MIG-001..043) + Hibernate stratification |
|
|
200
200
|
| [MODERNIZATION.md](docs/MODERNIZATION.md) | The modernization product: assess → understand → plan → execute |
|
|
201
201
|
| [PRODUCT_TIERS.md](docs/PRODUCT_TIERS.md) | Free vs Pro, pricing model |
|
|
@@ -4084,7 +4084,11 @@ def impact_cmd(
|
|
|
4084
4084
|
- direct_callers — classes that directly call or depend on the target
|
|
4085
4085
|
- indirect_callers — transitive callers (BFS, bounded by --depth)
|
|
4086
4086
|
- endpoints_affected — HTTP endpoints that transitively depend on the target
|
|
4087
|
-
- transactional_boundaries_touched — @Transactional
|
|
4087
|
+
- transactional_boundaries_touched — @Transactional declarations on the target
|
|
4088
|
+
and on everything that calls it
|
|
4089
|
+
- transactional_boundaries_reaching — the callers-only subset: what would run
|
|
4090
|
+
this change inside a transaction (the figure
|
|
4091
|
+
`ask retrieve transactions-reaching` reports)
|
|
4088
4092
|
- risk_score / risk_level — quantified change risk
|
|
4089
4093
|
|
|
4090
4094
|
\b
|
|
@@ -4714,7 +4718,10 @@ def export_cmd(
|
|
|
4714
4718
|
),
|
|
4715
4719
|
integrations: bool = typer.Option(
|
|
4716
4720
|
False, "--integrations",
|
|
4717
|
-
help=
|
|
4721
|
+
help=(
|
|
4722
|
+
"Detect outbound integrations (HTTP/LDAP/JMS clients) with file:line "
|
|
4723
|
+
"evidence, plus the client libraries the build declares."
|
|
4724
|
+
),
|
|
4718
4725
|
),
|
|
4719
4726
|
c4: bool = typer.Option(
|
|
4720
4727
|
False, "--c4",
|
|
@@ -4742,6 +4749,11 @@ def export_cmd(
|
|
|
4742
4749
|
rolled up from class-level relation edges.
|
|
4743
4750
|
--integrations Outbound integrations (RestTemplate/WebClient/Feign/LDAP/JMS)
|
|
4744
4751
|
with file:line evidence — external-system dependency arrows.
|
|
4752
|
+
Also lists the client libraries the BUILD declares
|
|
4753
|
+
(declared_clients) and which of those no source construct
|
|
4754
|
+
uses — a config-wired client has no construct to find, so a
|
|
4755
|
+
count of 0 is never left standing alone against a client
|
|
4756
|
+
library named in the stack block.
|
|
4745
4757
|
--c4 Unified architecture document mapped onto the open C4 model
|
|
4746
4758
|
(context/containers/components/code) + an API surface and a
|
|
4747
4759
|
per-directory content-hash manifest for incremental consumers.
|
|
@@ -4792,9 +4804,12 @@ def export_cmd(
|
|
|
4792
4804
|
graph = ir.get("graph", {})
|
|
4793
4805
|
_ep_data = extract_java_endpoints(root)
|
|
4794
4806
|
endpoints = _ep_data.get("endpoints", [])
|
|
4807
|
+
from sourcecode.integration_coordinates import declared_integration_coordinates
|
|
4795
4808
|
_integrations = SemanticIntegrationEngine(
|
|
4796
4809
|
ContextGraph.build(file_list, root)
|
|
4797
|
-
).as_report(
|
|
4810
|
+
).as_report(
|
|
4811
|
+
len(file_list), declared=declared_integration_coordinates(root),
|
|
4812
|
+
)
|
|
4798
4813
|
data = _build_c4_export(
|
|
4799
4814
|
root,
|
|
4800
4815
|
file_list,
|
|
@@ -4831,9 +4846,12 @@ def export_cmd(
|
|
|
4831
4846
|
if integrations:
|
|
4832
4847
|
from sourcecode.context_graph import ContextGraph
|
|
4833
4848
|
from sourcecode.semantic_integration_engine import SemanticIntegrationEngine
|
|
4849
|
+
from sourcecode.integration_coordinates import declared_integration_coordinates
|
|
4834
4850
|
data["integrations"] = SemanticIntegrationEngine(
|
|
4835
4851
|
ContextGraph.build(file_list, root)
|
|
4836
|
-
).as_report(
|
|
4852
|
+
).as_report(
|
|
4853
|
+
len(file_list), declared=declared_integration_coordinates(root),
|
|
4854
|
+
)
|
|
4837
4855
|
|
|
4838
4856
|
_prog.finish()
|
|
4839
4857
|
output = _serialize_dict(data, format)
|
|
@@ -5684,19 +5702,35 @@ def _render_gate_coverage_section(result: "SpringAuditResult") -> list[str]: #
|
|
|
5684
5702
|
gc = (result.security_posture or {}).get("gate_coverage")
|
|
5685
5703
|
if not gc:
|
|
5686
5704
|
return []
|
|
5687
|
-
not_covered = gc.get("not_carrying_gate", 0)
|
|
5705
|
+
not_covered = gc.get("endpoints_not_carrying_gate", gc.get("not_carrying_gate", 0))
|
|
5688
5706
|
gates = ", ".join(f"`{g}`" for g in gc.get("gate_annotations", [])) or "the detected gate"
|
|
5689
|
-
|
|
5707
|
+
# One unit, named: these are endpoints, not handler methods. The two counts differ
|
|
5708
|
+
# (one method can serve several mappings) and must never be summed together.
|
|
5709
|
+
total = gc.get("endpoints_total", gc.get("total_controller_handlers", 0))
|
|
5690
5710
|
lines: list[str] = ["", "---", ""]
|
|
5691
5711
|
if not_covered == 0:
|
|
5692
|
-
|
|
5712
|
+
_gated = gc.get("endpoints_carrying_gate", total)
|
|
5713
|
+
_std = gc.get("endpoints_standard_guarded", 0)
|
|
5714
|
+
if _std:
|
|
5715
|
+
lines.append(
|
|
5716
|
+
f"✅ **Gate coverage** — all {total} endpoints are guarded: {_gated} carry "
|
|
5717
|
+
f"{gates}, {_std} rely on a standard guard."
|
|
5718
|
+
)
|
|
5719
|
+
else:
|
|
5720
|
+
lines.append(f"✅ **Gate coverage** — all {total} endpoints carry {gates}.")
|
|
5693
5721
|
return lines
|
|
5694
5722
|
|
|
5695
5723
|
covered = gc.get("possibly_filter_covered", 0)
|
|
5724
|
+
_standard = gc.get("endpoints_standard_guarded", 0)
|
|
5696
5725
|
lines.append(
|
|
5697
|
-
f"🔓 **Gate coverage** — {not_covered} of {total}
|
|
5726
|
+
f"🔓 **Gate coverage** — {not_covered} of {total} endpoints do not carry "
|
|
5698
5727
|
f"{gates}."
|
|
5699
5728
|
)
|
|
5729
|
+
if _standard:
|
|
5730
|
+
lines.append(
|
|
5731
|
+
f"_{gc.get('endpoints_carrying_gate', 0)} carry the gate, {_standard} rely on a "
|
|
5732
|
+
f"standard guard, {not_covered} on neither — the three sum to {total}._"
|
|
5733
|
+
)
|
|
5700
5734
|
if gc.get("reconstructed_filter_patterns"):
|
|
5701
5735
|
lines.append(
|
|
5702
5736
|
f"_{covered} of those match a reconstructed servlet filter pattern "
|
|
@@ -6201,6 +6235,65 @@ def posture_cmd(
|
|
|
6201
6235
|
# ── Spring Boot Migration Check ───────────────────────────────────────────────
|
|
6202
6236
|
|
|
6203
6237
|
|
|
6238
|
+
def _migration_blast_radius(
|
|
6239
|
+
target: Path,
|
|
6240
|
+
file_list: list[str],
|
|
6241
|
+
report: "object",
|
|
6242
|
+
depth: int,
|
|
6243
|
+
) -> dict:
|
|
6244
|
+
"""Resolve the regression scope behind the migration findings (--blast-radius).
|
|
6245
|
+
|
|
6246
|
+
Reuses the shared context-cache CIR — the same one `impact-chain`, `explain` and
|
|
6247
|
+
`cache warm` use — so a warmed repository skips the Java parse. Every failure mode
|
|
6248
|
+
degrades to a block that says what could not be computed; it never fabricates reach
|
|
6249
|
+
and never breaks the scan that already succeeded.
|
|
6250
|
+
"""
|
|
6251
|
+
from sourcecode.migration_blast import compute_migration_blast, SCHEMA_VERSION
|
|
6252
|
+
|
|
6253
|
+
_depth = max(1, min(int(depth), 8))
|
|
6254
|
+
findings = list(getattr(report, "findings", []) or [])
|
|
6255
|
+
|
|
6256
|
+
def _degraded(reason: str) -> dict:
|
|
6257
|
+
return {
|
|
6258
|
+
"schema_version": SCHEMA_VERSION,
|
|
6259
|
+
"depth": _depth,
|
|
6260
|
+
"status": "unavailable",
|
|
6261
|
+
"endpoints_in_repo": 0,
|
|
6262
|
+
"endpoints_reached": 0,
|
|
6263
|
+
"request_surface_share": None,
|
|
6264
|
+
"files_analyzed": 0,
|
|
6265
|
+
"files_on_request_path": 0,
|
|
6266
|
+
"files_off_request_path": 0,
|
|
6267
|
+
"files_unresolved": [],
|
|
6268
|
+
"files": [],
|
|
6269
|
+
"files_omitted": 0,
|
|
6270
|
+
"notes": [reason],
|
|
6271
|
+
}
|
|
6272
|
+
|
|
6273
|
+
if not file_list:
|
|
6274
|
+
return _degraded(
|
|
6275
|
+
"No Java sources in this repository — there is no call graph to traverse, "
|
|
6276
|
+
"so no regression scope can be attributed."
|
|
6277
|
+
)
|
|
6278
|
+
|
|
6279
|
+
try:
|
|
6280
|
+
from sourcecode import context_cache as _ctxcache
|
|
6281
|
+
from sourcecode.context_graph import ContextGraph
|
|
6282
|
+
from sourcecode.spring_model import SpringSemanticModel
|
|
6283
|
+
|
|
6284
|
+
try:
|
|
6285
|
+
cir, _ = _ctxcache.get_or_build_cir(_resolve_repo_root(target), target, file_list)
|
|
6286
|
+
except Exception:
|
|
6287
|
+
cir = ContextGraph.build(file_list, target).cir
|
|
6288
|
+
model = SpringSemanticModel.build(cir)
|
|
6289
|
+
return compute_migration_blast(cir, model, findings, depth=_depth)
|
|
6290
|
+
except Exception as exc: # pragma: no cover - defensive
|
|
6291
|
+
return _degraded(
|
|
6292
|
+
f"Blast radius unavailable: {type(exc).__name__} while building the "
|
|
6293
|
+
"repository IR. The migration findings above are unaffected."
|
|
6294
|
+
)
|
|
6295
|
+
|
|
6296
|
+
|
|
6204
6297
|
@app.command("migrate-check")
|
|
6205
6298
|
def migrate_check_cmd(
|
|
6206
6299
|
path: Path = typer.Argument(
|
|
@@ -6265,6 +6358,18 @@ def migrate_check_cmd(
|
|
|
6265
6358
|
False, "--force",
|
|
6266
6359
|
help="Emit to stdout even when the report exceeds the output-size guard.",
|
|
6267
6360
|
),
|
|
6361
|
+
blast_radius: bool = typer.Option(
|
|
6362
|
+
False, "--blast-radius",
|
|
6363
|
+
help=(
|
|
6364
|
+
"Rank affected product files by the HTTP endpoints whose call path runs "
|
|
6365
|
+
"through them (regression scope), using the same caller traversal as "
|
|
6366
|
+
"impact-chain. Builds the repository IR — slower than a plain scan."
|
|
6367
|
+
),
|
|
6368
|
+
),
|
|
6369
|
+
depth: int = typer.Option(
|
|
6370
|
+
4, "--depth",
|
|
6371
|
+
help="Caller BFS depth for --blast-radius (1-8, default: 4).",
|
|
6372
|
+
),
|
|
6268
6373
|
) -> None:
|
|
6269
6374
|
"""Spring Boot 2→3 migration readiness: detect javax→jakarta namespace blockers.
|
|
6270
6375
|
|
|
@@ -6298,6 +6403,16 @@ def migrate_check_cmd(
|
|
|
6298
6403
|
ask migrate-check . --output migration.json
|
|
6299
6404
|
ask migrate-check . --snapshot --ref sprint-12 persist a readiness point
|
|
6300
6405
|
ask migrate-check . --trend days-remaining over time
|
|
6406
|
+
ask migrate-check . --blast-radius endpoints behind each blocker
|
|
6407
|
+
|
|
6408
|
+
\b
|
|
6409
|
+
Blast radius (--blast-radius):
|
|
6410
|
+
Ranks the affected product files by the HTTP endpoints whose call path runs
|
|
6411
|
+
through them — the re-test plan, ordered by regression scope. Reach is never
|
|
6412
|
+
build breakage: a javax→jakarta finding fails compilation whether or not an
|
|
6413
|
+
endpoint reaches it. A file whose types the IR could not resolve is reported
|
|
6414
|
+
unresolved (reach unknown, never zero), and a repository with no endpoints in
|
|
6415
|
+
the IR gets a null share with the reason stated instead of a 0.0.
|
|
6301
6416
|
|
|
6302
6417
|
\b
|
|
6303
6418
|
Readiness time series:
|
|
@@ -6366,6 +6481,9 @@ def migrate_check_cmd(
|
|
|
6366
6481
|
if _file_limitations:
|
|
6367
6482
|
report.limitations.extend(_file_limitations)
|
|
6368
6483
|
|
|
6484
|
+
if blast_radius:
|
|
6485
|
+
report.blast_radius = _migration_blast_radius(target, file_list, report, depth)
|
|
6486
|
+
|
|
6369
6487
|
if format == "text":
|
|
6370
6488
|
output = report.to_text(min_severity=min_severity)
|
|
6371
6489
|
else:
|
|
@@ -6513,7 +6631,8 @@ def impact_chain_cmd(
|
|
|
6513
6631
|
- indirect_callers — transitive callers (BFS up to --depth hops)
|
|
6514
6632
|
- endpoints_affected — HTTP endpoints reachable through the call chain
|
|
6515
6633
|
- transaction_boundary — @Transactional semantics on the target (if any)
|
|
6516
|
-
- security_surfaces — per-endpoint security
|
|
6634
|
+
- security_surfaces — per-endpoint security verdict + confidence
|
|
6635
|
+
(security_posture authority) + declared policy + SEC findings
|
|
6517
6636
|
- impact_findings — TX/SEC audit findings touching the call chain
|
|
6518
6637
|
- risk_level — critical | high | medium | low
|
|
6519
6638
|
|
|
@@ -15,6 +15,7 @@ from sourcecode.detectors.parsers import (
|
|
|
15
15
|
substitute_maven_properties as _resolve_maven_property,
|
|
16
16
|
unique_strings,
|
|
17
17
|
)
|
|
18
|
+
from sourcecode.integration_coordinates import coordinates_for_kind
|
|
18
19
|
from sourcecode.schema import FrameworkDetection
|
|
19
20
|
from sourcecode.tree_utils import flatten_file_tree
|
|
20
21
|
|
|
@@ -488,11 +489,15 @@ class JavaDetector(AbstractDetector):
|
|
|
488
489
|
if "spring-boot-starter-data-jpa" in text or "spring-data-jpa" in text:
|
|
489
490
|
frameworks.append(FrameworkDetection(name="Spring Data JPA", source=source))
|
|
490
491
|
# The Boot starter names neither of the module coordinates, so a Boot app
|
|
491
|
-
# wiring LDAP the standard way declared no LDAP framework at all.
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
492
|
+
# wiring LDAP the standard way declared no LDAP framework at all. The coordinate
|
|
493
|
+
# list lives in `integration_coordinates` — the same set `export --integrations`
|
|
494
|
+
# reads, so this stack block and that report can never recognize different
|
|
495
|
+
# libraries for the same external system (ADR-0008 R12).
|
|
496
|
+
# Only the Spring-family coordinates may carry the "Spring LDAP" label — the
|
|
497
|
+
# authority also knows non-Spring directory clients, and naming the wrong library
|
|
498
|
+
# would trade one incoherence for another.
|
|
499
|
+
if any(
|
|
500
|
+
c in text for c in coordinates_for_kind("ldap") if c.startswith("spring-")
|
|
496
501
|
):
|
|
497
502
|
frameworks.append(FrameworkDetection(name="Spring LDAP", source=source))
|
|
498
503
|
if "spring-aspects" in text or "spring-aop" in text:
|