sourcecode 1.45.0__tar.gz → 1.50.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-1.45.0 → sourcecode-1.50.0}/CHANGELOG.md +116 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/PKG-INFO +10 -2
- {sourcecode-1.45.0 → sourcecode-1.50.0}/README.md +9 -1
- {sourcecode-1.45.0 → sourcecode-1.50.0}/pyproject.toml +1 -1
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/mcp/orchestrator.py +209 -23
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/mcp/server.py +76 -4
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/repository_ir.py +37 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/spring_impact.py +114 -7
- {sourcecode-1.45.0 → sourcecode-1.50.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/.gitignore +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/.ruff.toml +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/CONTRIBUTING.md +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/LICENSE +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/SECURITY.md +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/raw +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/cache.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/canonical_ir.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/cir_graphs.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/cli.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/error_schema.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/explain.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/file_chunker.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/flow_analyzer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/format_contract.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/fqn_utils.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/license.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/mcp/onboarding/detector.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/mcp/registry.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/mcp/runner.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/mcp_nudge.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/migrate_check.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/openapi_surface.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/output_budget.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/path_filters.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/pr_impact.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/prepare_context.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/progress.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/rename_refactor.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/ris.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/schema.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/security_config.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/serializer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/spring_event_topology.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/spring_findings.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/spring_model.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/spring_security_audit.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/spring_semantic.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/spring_tx_analyzer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/validation_surface.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/version_check.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/supabase/functions/README.md +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/supabase/functions/get-license/index.ts +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/supabase/functions/telemetry/index.ts +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/supabase/sql/license_event_ordering.sql +0 -0
- {sourcecode-1.45.0 → sourcecode-1.50.0}/supabase/sql/telemetry_events.sql +0 -0
|
@@ -1,5 +1,121 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.50.0] — 2026-06-17
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **F-1 — `impact-chain` confidence no longer capped at `medium` by informational
|
|
7
|
+
warnings.** Confidence was computed as `medium` whenever *any* warning was present, but
|
|
8
|
+
the CH-001a/b interface↔implementation expansion notices ("Interface implementation
|
|
9
|
+
expansion: added N symbols…") are appended on every Spring interface/impl query — the
|
|
10
|
+
overwhelmingly common case — so a clean exact/`class_expanded` resolution could never
|
|
11
|
+
report `high`. The signal was meaningless: `medium` meant "normal", not "degraded".
|
|
12
|
+
|
|
13
|
+
Fix: track a `confidence_reducing` flag set only by genuinely degrading conditions
|
|
14
|
+
(hub-guard capped traversal); informational expansion notices no longer affect
|
|
15
|
+
confidence. `partial` resolution and blind-spot guards (CH-003/CH-005) still degrade as
|
|
16
|
+
before. Verified on shopizer: `impact-chain ProductService` now reports `confidence:high`
|
|
17
|
+
(was `medium`) while still surfacing the expansion notice. 2 regression tests
|
|
18
|
+
(`TestConfidenceNotCappedByInfoWarnings`): expansion warning keeps `high`, hub-guard
|
|
19
|
+
truncation still caps to `medium`.
|
|
20
|
+
|
|
21
|
+
Closes the v1.47.0 field-benchmark backlog (F-3 → CH-006 v1.48.0, F-2 → v1.49.0,
|
|
22
|
+
F-1 → this release).
|
|
23
|
+
|
|
24
|
+
## [1.49.0] — 2026-06-17
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- **F-2 — honest WebFlux / functional-routing signal in `endpoints`.** The endpoint
|
|
28
|
+
surface models annotation-based routing (`@RequestMapping`/`@GetMapping`, JAX-RS) only.
|
|
29
|
+
Routes registered via the functional DSL (`route().GET("/path", handler)` /
|
|
30
|
+
`RouterFunction` / `CustomEndpoint`) were silently invisible — the v1.47.0 field
|
|
31
|
+
benchmark found `endpoints` returned **0** for all of halo (a reactive app with 168
|
|
32
|
+
functional registrations across 51 files), which an agent could misread as "this app
|
|
33
|
+
exposes no endpoints". `endpoints` now detects functional routing and reports a
|
|
34
|
+
`functional_routing` block (`files`, `route_registrations`, `modeled: false`) plus a
|
|
35
|
+
warning; when the annotation surface is empty but functional routes exist, the warning
|
|
36
|
+
explicitly says not to read it as "no endpoints".
|
|
37
|
+
|
|
38
|
+
Deliberately does **not** synthesize endpoint entries: the literal DSL paths are
|
|
39
|
+
relative (real paths depend on `nest()`/group-version prefixes unresolvable statically),
|
|
40
|
+
and emitting partial paths would mislead more than an empty surface (same false-positive
|
|
41
|
+
hazard CH-006 just removed). Full functional-route modeling is a separate effort.
|
|
42
|
+
Validated: halo → 168 registrations surfaced; shopizer (annotation MVC) → no false
|
|
43
|
+
trigger, 286 endpoints unchanged. 3 regression tests
|
|
44
|
+
(`test_functional_routing_surface.py`).
|
|
45
|
+
|
|
46
|
+
## [1.48.0] — 2026-06-17
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
- **CH-006 — hub-interface caller over-expansion (false-positive callers) in
|
|
50
|
+
`impact-chain`.** `implements` and `extends` are structural type declarations, not
|
|
51
|
+
calls, but they were being traversed in the caller BFS. The reverse edge on an
|
|
52
|
+
interface/base lists its implementors/subclasses, so querying a class that implements a
|
|
53
|
+
high-fanout in-repo interface attributed **every sibling implementor** as a "direct
|
|
54
|
+
caller". Found in the v1.47.0 field benchmark: `impact-chain ThumbnailEndpoint` on halo
|
|
55
|
+
reported 42 direct callers — the other 42 `CustomEndpoint` implementors, none of which
|
|
56
|
+
call it — inflating a leaf endpoint to `risk:high`. The shopizer monolith had the same
|
|
57
|
+
pattern via its shared `Mapper<E,D>` base (45 phantom mapper callers on `ProductService`).
|
|
58
|
+
|
|
59
|
+
Fix: add `implements`/`extends` to the BFS edge-skip set. This is loss-free — the wanted
|
|
60
|
+
interface→implementation expansion (CH-001a/b) flows through `ImplementationGraph`
|
|
61
|
+
indices, not these reverse-graph edges, and real callers travel `injects`/`calls` edges.
|
|
62
|
+
Verified on both repos: halo `ThumbnailEndpoint` 42→0 false callers (`risk:high`→`low`);
|
|
63
|
+
shopizer `ProductService` real callers preserved (32 direct unchanged) while 45 phantom
|
|
64
|
+
`Mapper` callers — confirmed to have zero references to the queried symbol — were dropped.
|
|
65
|
+
2 regression tests (`TestHubInterfaceOverExpansion`): sibling implementors excluded, real
|
|
66
|
+
`injects` caller through a shared interface preserved.
|
|
67
|
+
|
|
68
|
+
Complements CH-005: that guard handles *external* supertypes (under-reporting); CH-006
|
|
69
|
+
handles *in-repo high-fanout* supertypes (over-reporting). False positives are worse than
|
|
70
|
+
an empty result — they actively misdirect a change — so this is the higher-leverage half.
|
|
71
|
+
|
|
72
|
+
## [1.47.0] — 2026-06-17
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
- **CH-005 — framework/external-interface DI blind-spot detection in `impact-chain`.**
|
|
76
|
+
When a queried class has an empty blast radius *and* implements/extends an external
|
|
77
|
+
framework supertype (one the in-repo `ImplementationGraph` deliberately drops — e.g.
|
|
78
|
+
Spring Security's `RedirectStrategy`, a servlet `Filter`), `impact-chain` now positively
|
|
79
|
+
detects it instead of silently reporting `0 callers / risk:low` at `confidence=high`.
|
|
80
|
+
Such classes are wired by framework DI/config and invoked polymorphically through the
|
|
81
|
+
external type, so no in-repo edge names their methods — the empty result is an
|
|
82
|
+
unmodeled-edge blind spot, not proof of dead code. The query now:
|
|
83
|
+
- drops `confidence` to `low`,
|
|
84
|
+
- exposes `metadata.blind_spots` (`framework_di`) and `metadata.external_supertypes`,
|
|
85
|
+
- emits a `CH-005` warning pointing the agent to search DI/security/config wiring for
|
|
86
|
+
the supertype to recover the real callers.
|
|
87
|
+
|
|
88
|
+
Inert marker interfaces (`Serializable`, `Cloneable`, `Externalizable`) are excluded —
|
|
89
|
+
they carry no methods, so no polymorphic dispatch and no hidden blast radius.
|
|
90
|
+
|
|
91
|
+
This converts a dangerous false negative ("looks safe to change") into an honest "look
|
|
92
|
+
further" signal. It does **not** recover the real callers (they flow through framework
|
|
93
|
+
wiring the static call-graph never traverses); that is a separate, larger effort.
|
|
94
|
+
Mirrors the existing CH-003 value-type guard. Validated end-to-end on BroadleafCommerce
|
|
95
|
+
(`LocalRedirectStrategy` → flagged; `FieldDaoImpl` with 16 real callers → not flagged).
|
|
96
|
+
|
|
97
|
+
## [1.46.0] — 2026-06-16
|
|
98
|
+
|
|
99
|
+
### Added
|
|
100
|
+
- **Two Java/Spring agent flow presets in the MCP orchestrator.** These wrap existing
|
|
101
|
+
tools into one-call, intent-routed flows so an agent can describe a task in natural
|
|
102
|
+
language and get the right sequence without reading docs:
|
|
103
|
+
- `run_migrate_flow` — wraps `migrate-check` for Spring Boot 2→3 planning and lifts a
|
|
104
|
+
top-level `headline` (`readiness_score`, `blocking_count`, `estimated_effort_days`,
|
|
105
|
+
`by_severity`, `by_target`) so the agent need not parse the full report.
|
|
106
|
+
- `run_security_audit_flow` — wraps `spring-audit` + `endpoints`. **Config-less
|
|
107
|
+
safeguard:** when no `sourcecode.config.json` is present and every endpoint reads
|
|
108
|
+
`none_detected`, the flow flags a likely custom-annotation blind spot
|
|
109
|
+
(`quality_warnings`) and returns a ready-to-paste `security_config_hint`, instead of
|
|
110
|
+
letting a misleading 100%-unsecured surface stand (prevents a false negative).
|
|
111
|
+
- **Intent routing for migration and security audit.** New `INTENT_MIGRATION` /
|
|
112
|
+
`INTENT_SECURITY_AUDIT` detection plus orchestration rules R5 (migration → lead with
|
|
113
|
+
`get_migration_readiness`) and R6 (security audit → `get_endpoints` first, mirroring R2)
|
|
114
|
+
route `start_session` / `analyze_task` to the new presets.
|
|
115
|
+
|
|
116
|
+
Note: these presets add no new analysis — they are orchestration ergonomics over tools
|
|
117
|
+
that were already callable directly. The standout value is the config-less safeguard.
|
|
118
|
+
|
|
3
119
|
## [1.45.0] — 2026-06-16
|
|
4
120
|
|
|
5
121
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.50.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
|
|
@@ -40,7 +40,7 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
|
|
41
41
|
**Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
|
|
42
42
|
|
|
43
|
-

|
|
44
44
|

|
|
45
45
|
|
|
46
46
|
---
|
|
@@ -368,6 +368,8 @@ sourcecode endpoints /path/to/repo --output endpoints.json
|
|
|
368
368
|
|
|
369
369
|
Extracts all Spring MVC (`@GetMapping`, `@PostMapping`, `@RequestMapping`, etc.) and JAX-RS (`@GET`, `@POST`, `@Path`) endpoint methods. Returns HTTP method, path, controller class, and handler method.
|
|
370
370
|
|
|
371
|
+
**Functional / WebFlux routing (honest limitation).** Routes registered via the functional DSL — `route().GET("/path", handler)` / `RouterFunction` / `CustomEndpoint`, common in reactive Spring apps — are **not** modeled (their real paths depend on `nest()`/group-version prefixes that can't be resolved statically). Rather than emit partial paths that would mislead, the output reports a `functional_routing` block (`files`, `route_registrations`, `modeled: false`) plus a warning. When the annotation surface is empty but functional routes exist, the warning explicitly tells you not to read it as "no endpoints". Annotation-based (MVC/JAX-RS) repos are unaffected.
|
|
372
|
+
|
|
371
373
|
**Custom security annotations.** Enterprise repos often guard endpoints with a bespoke annotation instead of `@PreAuthorize`/`@Secured`. Drop a `sourcecode.config.json` at the repo root to teach the scanner about it — otherwise those endpoints report `policy: "none_detected"`:
|
|
372
374
|
|
|
373
375
|
```json
|
|
@@ -435,6 +437,12 @@ Unlike `impact` (which traces the caller graph), `impact-chain` builds on the Sp
|
|
|
435
437
|
| `security_surfaces` | Per-endpoint security policy + SEC finding IDs |
|
|
436
438
|
| `impact_findings` | TX-001..005 and SEC-001..003 findings that touch the call chain |
|
|
437
439
|
| `risk_level` | `critical` \| `high` \| `medium` \| `low` |
|
|
440
|
+
| `confidence` | `high` \| `medium` \| `low` — `low` on a detected blind spot, `medium` on partial resolution or capped traversal. Informational interface↔impl expansion notices do **not** lower it, so a clean resolved query stays `high`. |
|
|
441
|
+
| `metadata.blind_spots` | `framework_di` and/or `value_type` when an empty result is unmodeled-edge driven, not real dead code |
|
|
442
|
+
|
|
443
|
+
**Framework/DI blind spot (CH-005).** An empty blast radius is ambiguous: genuinely unused, or invoked through an edge the static graph does not model. When the target class implements/extends an **external** framework type (e.g. Spring Security's `RedirectStrategy`, a servlet `Filter`) it is typically wired by framework DI/config and invoked polymorphically — no in-repo edge names its methods, so `direct_callers` is `0`. Rather than report that as `risk:low` at high confidence (a dangerous false negative that reads as "safe to change"), `impact-chain` detects the external supertype, drops `confidence` to `low`, lists it in `metadata.external_supertypes`, and emits a `CH-005` warning telling you to search the DI/security/config wiring for the supertype. Inert markers (`Serializable`, `Cloneable`) are excluded.
|
|
444
|
+
|
|
445
|
+
**Caller precision (CH-006).** `implements`/`extends` are structural type declarations, not calls — so they are excluded from the caller graph. Querying a class that implements a high-fanout interface (e.g. a 40-implementor `CustomEndpoint` or a shared `Mapper<E,D>` base) does **not** report its sibling implementors as callers; only real `injects`/`calls` edges count. This prevents a leaf class from being inflated to a large false blast radius.
|
|
438
446
|
|
|
439
447
|
**Event topology** — query the publisher/consumer graph for a Spring event class:
|
|
440
448
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|

|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -330,6 +330,8 @@ sourcecode endpoints /path/to/repo --output endpoints.json
|
|
|
330
330
|
|
|
331
331
|
Extracts all Spring MVC (`@GetMapping`, `@PostMapping`, `@RequestMapping`, etc.) and JAX-RS (`@GET`, `@POST`, `@Path`) endpoint methods. Returns HTTP method, path, controller class, and handler method.
|
|
332
332
|
|
|
333
|
+
**Functional / WebFlux routing (honest limitation).** Routes registered via the functional DSL — `route().GET("/path", handler)` / `RouterFunction` / `CustomEndpoint`, common in reactive Spring apps — are **not** modeled (their real paths depend on `nest()`/group-version prefixes that can't be resolved statically). Rather than emit partial paths that would mislead, the output reports a `functional_routing` block (`files`, `route_registrations`, `modeled: false`) plus a warning. When the annotation surface is empty but functional routes exist, the warning explicitly tells you not to read it as "no endpoints". Annotation-based (MVC/JAX-RS) repos are unaffected.
|
|
334
|
+
|
|
333
335
|
**Custom security annotations.** Enterprise repos often guard endpoints with a bespoke annotation instead of `@PreAuthorize`/`@Secured`. Drop a `sourcecode.config.json` at the repo root to teach the scanner about it — otherwise those endpoints report `policy: "none_detected"`:
|
|
334
336
|
|
|
335
337
|
```json
|
|
@@ -397,6 +399,12 @@ Unlike `impact` (which traces the caller graph), `impact-chain` builds on the Sp
|
|
|
397
399
|
| `security_surfaces` | Per-endpoint security policy + SEC finding IDs |
|
|
398
400
|
| `impact_findings` | TX-001..005 and SEC-001..003 findings that touch the call chain |
|
|
399
401
|
| `risk_level` | `critical` \| `high` \| `medium` \| `low` |
|
|
402
|
+
| `confidence` | `high` \| `medium` \| `low` — `low` on a detected blind spot, `medium` on partial resolution or capped traversal. Informational interface↔impl expansion notices do **not** lower it, so a clean resolved query stays `high`. |
|
|
403
|
+
| `metadata.blind_spots` | `framework_di` and/or `value_type` when an empty result is unmodeled-edge driven, not real dead code |
|
|
404
|
+
|
|
405
|
+
**Framework/DI blind spot (CH-005).** An empty blast radius is ambiguous: genuinely unused, or invoked through an edge the static graph does not model. When the target class implements/extends an **external** framework type (e.g. Spring Security's `RedirectStrategy`, a servlet `Filter`) it is typically wired by framework DI/config and invoked polymorphically — no in-repo edge names its methods, so `direct_callers` is `0`. Rather than report that as `risk:low` at high confidence (a dangerous false negative that reads as "safe to change"), `impact-chain` detects the external supertype, drops `confidence` to `low`, lists it in `metadata.external_supertypes`, and emits a `CH-005` warning telling you to search the DI/security/config wiring for the supertype. Inert markers (`Serializable`, `Cloneable`) are excluded.
|
|
406
|
+
|
|
407
|
+
**Caller precision (CH-006).** `implements`/`extends` are structural type declarations, not calls — so they are excluded from the caller graph. Querying a class that implements a high-fanout interface (e.g. a 40-implementor `CustomEndpoint` or a shared `Mapper<E,D>` base) does **not** report its sibling implementors as callers; only real `injects`/`calls` edges count. This prevents a leaf class from being inflated to a large false blast radius.
|
|
400
408
|
|
|
401
409
|
**Event topology** — query the publisher/consumer graph for a Spring event class:
|
|
402
410
|
|
|
@@ -6,29 +6,20 @@ Converts the MCP from a flat tool collection into a guided agent operating syste
|
|
|
6
6
|
- run_pr_review_flow: auto-chains delta + review_pr + blast radius
|
|
7
7
|
- run_bug_investigation_flow: auto-chains fix_bug + impact + IR context
|
|
8
8
|
- run_feature_flow: auto-chains context + endpoints + delta + structural awareness
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
1.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
repo carries custom security annotations, emit a fallback WARNING +
|
|
24
|
-
hint to add sourcecode.config.json (customAnnotations) rather than
|
|
25
|
-
returning a misleading 100% none_detected result.
|
|
26
|
-
|
|
27
|
-
3. TODO: implement later — extend R2 orchestration rule (apply_orchestration_rules)
|
|
28
|
-
Inject preset (1)/(2) when detected intent maps to migration or
|
|
29
|
-
security audit, mirroring the existing R2 java_no_endpoints rule.
|
|
30
|
-
Requires new INTENT_MIGRATION / INTENT_SECURITY_AUDIT constants +
|
|
31
|
-
_INTENT_PATTERNS entries + WORKFLOW_SEQUENCES / FLOW_RUNNERS wiring.
|
|
9
|
+
- run_migrate_flow: wraps migrate-check; Spring Boot 2→3 readiness in one call
|
|
10
|
+
- run_security_audit_flow: wraps spring-audit + endpoints; config-less blind-spot warning
|
|
11
|
+
|
|
12
|
+
High-value Java/Spring flow presets (audit 2026-06-16, repo SAINT) — implemented:
|
|
13
|
+
1. run_migrate_flow — preset over `migrate-check`, the primary entry point for
|
|
14
|
+
Spring Boot 2→3 planning. Lifts readiness_score, blocking_count,
|
|
15
|
+
estimated_effort_days and the per-target breakdown to a top-level headline.
|
|
16
|
+
2. run_security_audit_flow — preset over `spring-audit` + `endpoints`. Detects the
|
|
17
|
+
config-less case (no sourcecode.config.json + every endpoint none_detected) and
|
|
18
|
+
emits a warning plus a ready-to-paste config hint instead of a misleading 100%
|
|
19
|
+
unsecured surface.
|
|
20
|
+
3. apply_orchestration_rules R5/R6 — inject these presets when the detected intent
|
|
21
|
+
maps to migration (INTENT_MIGRATION) or security audit (INTENT_SECURITY_AUDIT),
|
|
22
|
+
mirroring the existing R2 java_no_endpoints rule.
|
|
32
23
|
"""
|
|
33
24
|
from __future__ import annotations
|
|
34
25
|
|
|
@@ -55,6 +46,8 @@ SESSION_READY_FOR_REVIEW = "READY_FOR_REVIEW" # flow complete
|
|
|
55
46
|
# ---------------------------------------------------------------------------
|
|
56
47
|
|
|
57
48
|
INTENT_PR_REVIEW = "pr_review"
|
|
49
|
+
INTENT_MIGRATION = "migration"
|
|
50
|
+
INTENT_SECURITY_AUDIT = "security_audit"
|
|
58
51
|
INTENT_BUG_INVESTIGATION = "bug_investigation"
|
|
59
52
|
INTENT_FEATURE_IMPLEMENTATION = "feature_implementation"
|
|
60
53
|
INTENT_REFACTOR = "refactor"
|
|
@@ -67,6 +60,8 @@ INTENT_ORIENTATION = "orientation"
|
|
|
67
60
|
|
|
68
61
|
WORKFLOW_SEQUENCES: dict[str, list[str]] = {
|
|
69
62
|
INTENT_PR_REVIEW: ["get_delta", "review_pr_context", "get_impact_context"],
|
|
63
|
+
INTENT_MIGRATION: ["get_migration_readiness"],
|
|
64
|
+
INTENT_SECURITY_AUDIT: ["get_spring_audit", "get_endpoints"],
|
|
70
65
|
INTENT_BUG_INVESTIGATION: ["fix_bug_context", "get_impact_context"],
|
|
71
66
|
INTENT_FEATURE_IMPLEMENTATION: ["get_compact_context", "get_endpoints", "get_delta"],
|
|
72
67
|
INTENT_REFACTOR: ["get_agent_context", "modernize_context", "get_ir_summary"],
|
|
@@ -76,6 +71,8 @@ WORKFLOW_SEQUENCES: dict[str, list[str]] = {
|
|
|
76
71
|
|
|
77
72
|
WORKFLOW_DESCRIPTIONS: dict[str, str] = {
|
|
78
73
|
INTENT_PR_REVIEW: "PR review: delta → execution paths → blast radius of changed classes",
|
|
74
|
+
INTENT_MIGRATION: "Spring Boot 2→3 migration: readiness score → javax→jakarta blockers → effort estimate",
|
|
75
|
+
INTENT_SECURITY_AUDIT: "Security audit: Spring TX/security findings → endpoint authorization surface",
|
|
79
76
|
INTENT_BUG_INVESTIGATION: "Bug investigation: risk-ranked files → impact of suspect class",
|
|
80
77
|
INTENT_FEATURE_IMPLEMENTATION: "Feature implementation: context → API surface → recent changes",
|
|
81
78
|
INTENT_REFACTOR: "Refactor: deep context → modernization opportunities → IR coupling",
|
|
@@ -85,6 +82,8 @@ WORKFLOW_DESCRIPTIONS: dict[str, str] = {
|
|
|
85
82
|
|
|
86
83
|
FLOW_RUNNERS: dict[str, str] = {
|
|
87
84
|
INTENT_PR_REVIEW: "run_pr_review_flow",
|
|
85
|
+
INTENT_MIGRATION: "run_migrate_flow",
|
|
86
|
+
INTENT_SECURITY_AUDIT: "run_security_audit_flow",
|
|
88
87
|
INTENT_BUG_INVESTIGATION: "run_bug_investigation_flow",
|
|
89
88
|
INTENT_FEATURE_IMPLEMENTATION: "run_feature_flow",
|
|
90
89
|
INTENT_REFACTOR: "run_feature_flow",
|
|
@@ -101,6 +100,16 @@ _INTENT_PATTERNS: list[tuple[str, list[str]]] = [
|
|
|
101
100
|
r"\bpr\b", r"pull request", r"review pr", r"\bdiff\b", r"merge request",
|
|
102
101
|
r"code review", r"changes in branch", r"review.*branch", r"branch.*review",
|
|
103
102
|
]),
|
|
103
|
+
(INTENT_MIGRATION, [
|
|
104
|
+
r"\bmigrat", r"spring.?boot.?[23]", r"spring boot 2.*3", r"boot 2.*3",
|
|
105
|
+
r"\bjakarta\b", r"\bjavax\b", r"javax.*jakarta", r"namespace.*(migrat|change)",
|
|
106
|
+
r"2\s*(?:->|to|→|–|—)\s*3", r"\bupgrade\b.*(spring|boot|jakarta|java)",
|
|
107
|
+
]),
|
|
108
|
+
(INTENT_SECURITY_AUDIT, [
|
|
109
|
+
r"security audit", r"audit.*security", r"security.*surface",
|
|
110
|
+
r"\bauthoriz", r"\bpreauthorize\b", r"\bsecured\b", r"access control",
|
|
111
|
+
r"who can (?:call|access)", r"endpoint.*(secur|auth)", r"(secur|auth).*endpoint",
|
|
112
|
+
]),
|
|
104
113
|
(INTENT_BUG_INVESTIGATION, [
|
|
105
114
|
r"\bbug\b", r"\berror\b", r"\bexception\b", r"\bcrash\b", r"\bnpe\b",
|
|
106
115
|
r"\bfix\b", r"\bbroken\b", r"\bfail(s|ing)?\b", r"stack.?trace",
|
|
@@ -168,6 +177,9 @@ def apply_orchestration_rules(
|
|
|
168
177
|
R2 java_no_endpoints → prepend get_endpoints (api_surface must exist first)
|
|
169
178
|
R3 large_repo (>1000) → note RIS path preferred (informational, no seq change)
|
|
170
179
|
R4 no_symptom_bug_flow → quality warning issued by caller (not a seq change)
|
|
180
|
+
R5 migration_intent (Java) → ensure get_migration_readiness leads the sequence
|
|
181
|
+
R6 security_audit_intent (Java) → ensure get_endpoints precedes the audit so the
|
|
182
|
+
authorization surface is available when findings are interpreted
|
|
171
183
|
"""
|
|
172
184
|
seq = list(sequence)
|
|
173
185
|
rules: list[str] = []
|
|
@@ -186,6 +198,17 @@ def apply_orchestration_rules(
|
|
|
186
198
|
if repo_class_count > 1000:
|
|
187
199
|
rules.append("R3:large_repo→RIS_path_preferred")
|
|
188
200
|
|
|
201
|
+
# R5: migration intent on a Java repo → migration readiness is the entry point.
|
|
202
|
+
if intent == INTENT_MIGRATION and is_java and "get_migration_readiness" not in seq:
|
|
203
|
+
seq.insert(0, "get_migration_readiness")
|
|
204
|
+
rules.append("R5:migration_intent→prepend_migration_readiness")
|
|
205
|
+
|
|
206
|
+
# R6: security-audit intent on a Java repo → endpoints surface must precede the
|
|
207
|
+
# audit (mirrors R2: the authorization surface has to exist first).
|
|
208
|
+
if intent == INTENT_SECURITY_AUDIT and is_java and "get_endpoints" not in seq:
|
|
209
|
+
seq.insert(0, "get_endpoints")
|
|
210
|
+
rules.append("R6:security_audit_intent→prepend_get_endpoints")
|
|
211
|
+
|
|
189
212
|
return seq, rules
|
|
190
213
|
|
|
191
214
|
|
|
@@ -729,3 +752,166 @@ def run_feature_flow_impl(repo_path: str, feature_description: str = "") -> dict
|
|
|
729
752
|
"tools_suggested_to_agent": 0,
|
|
730
753
|
},
|
|
731
754
|
}
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
# ---------------------------------------------------------------------------
|
|
758
|
+
# Flow: Spring Boot 2→3 Migration
|
|
759
|
+
# ---------------------------------------------------------------------------
|
|
760
|
+
|
|
761
|
+
def run_migrate_flow_impl(repo_path: str, min_severity: str = "low") -> dict[str, Any]:
|
|
762
|
+
"""Migration Flow: Spring Boot 2→3 readiness in one call.
|
|
763
|
+
|
|
764
|
+
Primary high-value entry point for migration planning. Wraps ``migrate-check``
|
|
765
|
+
and lifts the headline numbers (readiness_score, blocking_count,
|
|
766
|
+
estimated_effort_days, per-target breakdown) to the top level so the agent
|
|
767
|
+
can plan a 2→3 upgrade without parsing the full report.
|
|
768
|
+
"""
|
|
769
|
+
t0 = time.monotonic()
|
|
770
|
+
steps: list[str] = []
|
|
771
|
+
quality_warnings: list[str] = []
|
|
772
|
+
output: dict[str, Any] = {}
|
|
773
|
+
|
|
774
|
+
if not _is_java_repo(repo_path):
|
|
775
|
+
quality_warnings.append(
|
|
776
|
+
"not_a_java_repo: migration analysis targets Spring Boot / Java repos. "
|
|
777
|
+
"Result will be empty (readiness_score=100, no findings)."
|
|
778
|
+
)
|
|
779
|
+
|
|
780
|
+
report = _exec(["migrate-check", repo_path, "--min-severity", min_severity])
|
|
781
|
+
steps.append(f"get_migration_readiness(min_severity={min_severity})")
|
|
782
|
+
headline: dict[str, Any] = {}
|
|
783
|
+
if "_exec_error" not in report:
|
|
784
|
+
output["migration_readiness"] = report
|
|
785
|
+
# Lift the planning-relevant headline numbers to the top level.
|
|
786
|
+
for key in (
|
|
787
|
+
"readiness_score", "blocking_count", "estimated_effort_days",
|
|
788
|
+
"spring_boot_2_detected",
|
|
789
|
+
):
|
|
790
|
+
if key in report:
|
|
791
|
+
headline[key] = report[key]
|
|
792
|
+
_summary = report.get("summary", {})
|
|
793
|
+
if isinstance(_summary, dict):
|
|
794
|
+
headline["total_findings"] = _summary.get("total_findings")
|
|
795
|
+
headline["affected_files"] = _summary.get("affected_files")
|
|
796
|
+
headline["by_severity"] = _summary.get("by_severity")
|
|
797
|
+
headline["by_target"] = _summary.get("by_target") or _summary.get("by_rule")
|
|
798
|
+
else:
|
|
799
|
+
quality_warnings.append(f"migrate_check_failed: {report['_exec_error']}")
|
|
800
|
+
|
|
801
|
+
ttfca_ms = int((time.monotonic() - t0) * 1000)
|
|
802
|
+
|
|
803
|
+
return {
|
|
804
|
+
"session_state": SESSION_READY_FOR_REVIEW,
|
|
805
|
+
"flow": "migration",
|
|
806
|
+
"min_severity": min_severity,
|
|
807
|
+
"headline": headline,
|
|
808
|
+
"steps_executed": steps,
|
|
809
|
+
"quality_warnings": quality_warnings,
|
|
810
|
+
"consolidated_output": output,
|
|
811
|
+
"session_meta": {
|
|
812
|
+
"ttfca_ms": ttfca_ms,
|
|
813
|
+
"steps_auto_executed": len(steps),
|
|
814
|
+
"tools_suggested_to_agent": 0,
|
|
815
|
+
},
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
# ---------------------------------------------------------------------------
|
|
820
|
+
# Flow: Security Audit
|
|
821
|
+
# ---------------------------------------------------------------------------
|
|
822
|
+
|
|
823
|
+
def _endpoint_security_coverage(endpoints: dict[str, Any]) -> tuple[int, int]:
|
|
824
|
+
"""Return (total_endpoints, none_detected) from an endpoints result."""
|
|
825
|
+
if "_exec_error" in endpoints:
|
|
826
|
+
return 0, 0
|
|
827
|
+
data = endpoints.get("data", endpoints) if isinstance(endpoints, dict) else {}
|
|
828
|
+
total = int(data.get("total", 0) or 0)
|
|
829
|
+
none_detected = int(data.get("no_security_signal", 0) or 0)
|
|
830
|
+
return total, none_detected
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
def run_security_audit_flow_impl(repo_path: str, scope: str = "all") -> dict[str, Any]:
|
|
834
|
+
"""Security Audit Flow: Spring findings + endpoint authorization surface.
|
|
835
|
+
|
|
836
|
+
Wraps ``spring-audit`` (TX + security findings) and ``endpoints`` (authorization
|
|
837
|
+
surface) in one call. Auto-handles the config-less case: when no
|
|
838
|
+
``sourcecode.config.json`` is present and every endpoint reads as
|
|
839
|
+
``none_detected``, the all-zero security surface is almost certainly a
|
|
840
|
+
custom-annotation blind spot rather than a truly unsecured API — so the flow
|
|
841
|
+
emits a warning plus a ready-to-paste config hint instead of letting the
|
|
842
|
+
misleading result stand.
|
|
843
|
+
"""
|
|
844
|
+
from sourcecode.security_config import CONFIG_FILENAME
|
|
845
|
+
|
|
846
|
+
t0 = time.monotonic()
|
|
847
|
+
steps: list[str] = []
|
|
848
|
+
quality_warnings: list[str] = []
|
|
849
|
+
output: dict[str, Any] = {}
|
|
850
|
+
|
|
851
|
+
if not _is_java_repo(repo_path):
|
|
852
|
+
quality_warnings.append(
|
|
853
|
+
"not_a_java_repo: spring-audit targets Java/Spring repos and will "
|
|
854
|
+
"report spring_detected=false."
|
|
855
|
+
)
|
|
856
|
+
|
|
857
|
+
# Step 1: Spring semantic audit (TX anomalies + security findings).
|
|
858
|
+
audit = _exec(["spring-audit", repo_path, "--scope", scope])
|
|
859
|
+
steps.append(f"get_spring_audit(scope={scope})")
|
|
860
|
+
if "_exec_error" not in audit:
|
|
861
|
+
output["spring_audit"] = audit
|
|
862
|
+
else:
|
|
863
|
+
quality_warnings.append(f"spring_audit_failed: {audit['_exec_error']}")
|
|
864
|
+
|
|
865
|
+
# Step 2: endpoint authorization surface.
|
|
866
|
+
endpoints = _exec(["endpoints", repo_path])
|
|
867
|
+
steps.append("get_endpoints")
|
|
868
|
+
if "_exec_error" not in endpoints:
|
|
869
|
+
output["endpoint_security_surface"] = endpoints
|
|
870
|
+
else:
|
|
871
|
+
quality_warnings.append(f"endpoints_failed: {endpoints['_exec_error']}")
|
|
872
|
+
|
|
873
|
+
# Config-less blind-spot detection: no sourcecode.config.json + every endpoint
|
|
874
|
+
# none_detected → warn rather than report a misleading 100% unsecured surface.
|
|
875
|
+
has_config = (Path(repo_path) / CONFIG_FILENAME).exists()
|
|
876
|
+
total, none_detected = _endpoint_security_coverage(endpoints)
|
|
877
|
+
if not has_config and total > 0 and none_detected == total:
|
|
878
|
+
quality_warnings.append(
|
|
879
|
+
"config_less_security_blind_spot: no sourcecode.config.json found and "
|
|
880
|
+
f"all {total} endpoints report none_detected. If this repo uses a custom "
|
|
881
|
+
"authorization annotation, the surface is a false negative — add the "
|
|
882
|
+
"config below and re-run."
|
|
883
|
+
)
|
|
884
|
+
output["security_config_hint"] = {
|
|
885
|
+
"reason": "no_custom_security_annotations_configured",
|
|
886
|
+
"file": CONFIG_FILENAME,
|
|
887
|
+
"example": {
|
|
888
|
+
"customSecurityAnnotations": [
|
|
889
|
+
{
|
|
890
|
+
"shortName": "YourSecurityAnnotation",
|
|
891
|
+
"resourceParam": "resourceName",
|
|
892
|
+
"levelParam": "requiredLevel",
|
|
893
|
+
}
|
|
894
|
+
]
|
|
895
|
+
},
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
ttfca_ms = int((time.monotonic() - t0) * 1000)
|
|
899
|
+
|
|
900
|
+
return {
|
|
901
|
+
"session_state": SESSION_READY_FOR_REVIEW,
|
|
902
|
+
"flow": "security_audit",
|
|
903
|
+
"scope": scope,
|
|
904
|
+
"endpoint_security_coverage": {
|
|
905
|
+
"total_endpoints": total,
|
|
906
|
+
"none_detected": none_detected,
|
|
907
|
+
"config_present": has_config,
|
|
908
|
+
},
|
|
909
|
+
"steps_executed": steps,
|
|
910
|
+
"quality_warnings": quality_warnings,
|
|
911
|
+
"consolidated_output": output,
|
|
912
|
+
"session_meta": {
|
|
913
|
+
"ttfca_ms": ttfca_ms,
|
|
914
|
+
"steps_auto_executed": len(steps),
|
|
915
|
+
"tools_suggested_to_agent": 0,
|
|
916
|
+
},
|
|
917
|
+
}
|
|
@@ -510,6 +510,75 @@ def run_feature_flow(repo_path: str = ".", feature_description: str = "") -> dic
|
|
|
510
510
|
)
|
|
511
511
|
|
|
512
512
|
|
|
513
|
+
@mcp.tool()
|
|
514
|
+
def run_migrate_flow(repo_path: str = ".", min_severity: str = "low") -> dict:
|
|
515
|
+
"""Migration Flow — Spring Boot 2→3 readiness in one call. JAVA/SPRING ONLY.
|
|
516
|
+
|
|
517
|
+
Primary high-value entry point for migration planning. Wraps migrate-check and
|
|
518
|
+
lifts the headline numbers to the top level so the agent can plan a 2→3 upgrade
|
|
519
|
+
without parsing the full report:
|
|
520
|
+
- readiness_score (0–100; 100 = ready), blocking_count, estimated_effort_days
|
|
521
|
+
- by_severity and by_target breakdown (jakarta / spring_security_6 / java_11)
|
|
522
|
+
|
|
523
|
+
Use this instead of calling get_migration_readiness and interpreting it by hand.
|
|
524
|
+
Returns headline + consolidated_output.migration_readiness (full report).
|
|
525
|
+
|
|
526
|
+
repo_path: absolute path to the Java repository (default: current working directory).
|
|
527
|
+
min_severity: "critical" | "high" | "medium" | "low" (default — include everything).
|
|
528
|
+
"""
|
|
529
|
+
_raw = repo_path
|
|
530
|
+
try:
|
|
531
|
+
if not isinstance(repo_path, str):
|
|
532
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
533
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
534
|
+
_path_err = _check_repo_path(repo_path)
|
|
535
|
+
if _path_err is not None:
|
|
536
|
+
return _path_err
|
|
537
|
+
from sourcecode.mcp.orchestrator import run_migrate_flow_impl
|
|
538
|
+
return _ok(run_migrate_flow_impl(repo_path, min_severity or "low"))
|
|
539
|
+
except Exception as exc:
|
|
540
|
+
return _err(
|
|
541
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path: {_raw}",
|
|
542
|
+
"INTERNAL_ERROR",
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
@mcp.tool()
|
|
547
|
+
def run_security_audit_flow(repo_path: str = ".", scope: str = "all") -> dict:
|
|
548
|
+
"""Security Audit Flow — Spring findings + endpoint authorization surface. JAVA/SPRING ONLY.
|
|
549
|
+
|
|
550
|
+
Auto-chains in one call:
|
|
551
|
+
1. get_spring_audit(scope) — TX anomalies + security-surface findings
|
|
552
|
+
2. get_endpoints — endpoint authorization surface
|
|
553
|
+
|
|
554
|
+
Config-less safeguard: when no sourcecode.config.json is present and every
|
|
555
|
+
endpoint reads none_detected, the flow flags a likely custom-annotation blind
|
|
556
|
+
spot (quality_warnings) and returns a ready-to-paste security_config_hint —
|
|
557
|
+
rather than letting a misleading 100% none_detected surface stand.
|
|
558
|
+
|
|
559
|
+
Returns endpoint_security_coverage + consolidated_output (spring_audit,
|
|
560
|
+
endpoint_security_surface, optional security_config_hint).
|
|
561
|
+
|
|
562
|
+
repo_path: absolute path to the Java repository (default: current working directory).
|
|
563
|
+
scope: "all" (default) | "tx" | "security".
|
|
564
|
+
"""
|
|
565
|
+
_raw = repo_path
|
|
566
|
+
try:
|
|
567
|
+
if not isinstance(repo_path, str):
|
|
568
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
569
|
+
repo_path = _normalize_repo_path(repo_path)
|
|
570
|
+
_path_err = _check_repo_path(repo_path)
|
|
571
|
+
if _path_err is not None:
|
|
572
|
+
return _path_err
|
|
573
|
+
from sourcecode.mcp.orchestrator import run_security_audit_flow_impl
|
|
574
|
+
return _ok(run_security_audit_flow_impl(repo_path, scope or "all"))
|
|
575
|
+
except Exception as exc:
|
|
576
|
+
return _err(
|
|
577
|
+
f"Internal error: {type(exc).__name__}: {exc} — repo_path: {_raw}",
|
|
578
|
+
"INTERNAL_ERROR",
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
|
|
513
582
|
@mcp.tool()
|
|
514
583
|
def get_cold_start_context(repo_path: str = ".") -> dict:
|
|
515
584
|
"""Instant session bootstrap from persisted Repository Intelligence Snapshot (RIS).
|
|
@@ -1333,16 +1402,19 @@ _NATIVE_MCP_TOOLS: frozenset[str] = frozenset({
|
|
|
1333
1402
|
"run_pr_review_flow",
|
|
1334
1403
|
"run_bug_investigation_flow",
|
|
1335
1404
|
"run_feature_flow",
|
|
1405
|
+
"run_migrate_flow",
|
|
1406
|
+
"run_security_audit_flow",
|
|
1336
1407
|
})
|
|
1337
1408
|
|
|
1338
1409
|
|
|
1339
1410
|
def _finalize_mcp_registry() -> None:
|
|
1340
1411
|
"""Sync the MCP server with the runtime-generated registry.
|
|
1341
1412
|
|
|
1342
|
-
Removes every tool except the
|
|
1343
|
-
analyze_task, flow runners
|
|
1344
|
-
module and have no backing CLI command.
|
|
1345
|
-
runtime-derived registry
|
|
1413
|
+
Removes every tool except the native orchestration tools (start_session,
|
|
1414
|
+
analyze_task, and the flow runners — see _NATIVE_MCP_TOOLS) which are
|
|
1415
|
+
registered via @mcp.tool() in this module and have no backing CLI command.
|
|
1416
|
+
All other tools are rebuilt from the runtime-derived registry
|
|
1417
|
+
(build_mcp_tool_specs).
|
|
1346
1418
|
"""
|
|
1347
1419
|
from sourcecode.mcp.registry import build_mcp_tool_specs, make_tool_callable, validate_registry
|
|
1348
1420
|
|