sourcecode 3.1.0__py3-none-any.whl → 3.2.0__py3-none-any.whl
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.
Potentially problematic release.
This version of sourcecode might be problematic. Click here for more details.
- sourcecode/__init__.py +1 -1
- sourcecode/cli.py +128 -9
- sourcecode/detectors/java.py +10 -5
- sourcecode/integration_coordinates.py +175 -0
- sourcecode/mcp/registry.py +3 -1
- sourcecode/mcp/server.py +5 -2
- sourcecode/migrate_check.py +22 -1
- sourcecode/migration_blast.py +232 -0
- sourcecode/repository_ir.py +25 -17
- sourcecode/retrieval/steps_graph.py +10 -8
- sourcecode/retrieval/steps_intf.py +23 -1
- sourcecode/security_posture.py +165 -19
- sourcecode/semantic_integration_engine.py +54 -2
- sourcecode/spring_impact.py +124 -7
- sourcecode/spring_semantic.py +56 -4
- {sourcecode-3.1.0.dist-info → sourcecode-3.2.0.dist-info}/METADATA +3 -3
- {sourcecode-3.1.0.dist-info → sourcecode-3.2.0.dist-info}/RECORD +20 -18
- {sourcecode-3.1.0.dist-info → sourcecode-3.2.0.dist-info}/WHEEL +0 -0
- {sourcecode-3.1.0.dist-info → sourcecode-3.2.0.dist-info}/entry_points.txt +0 -0
- {sourcecode-3.1.0.dist-info → sourcecode-3.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -288,9 +288,23 @@ class SemanticIntegrationEngine:
|
|
|
288
288
|
"runtime/DI-wired clients remain out of static scope."
|
|
289
289
|
)
|
|
290
290
|
|
|
291
|
-
def as_report(
|
|
291
|
+
def as_report(
|
|
292
|
+
self,
|
|
293
|
+
n_files: Optional[int] = None,
|
|
294
|
+
*,
|
|
295
|
+
declared: "Optional[list]" = None,
|
|
296
|
+
) -> dict:
|
|
292
297
|
"""Same output contract as integration_detector.detect_integrations, so the
|
|
293
|
-
`export --integrations` / C4 external_systems consumer is unchanged.
|
|
298
|
+
`export --integrations` / C4 external_systems consumer is unchanged.
|
|
299
|
+
|
|
300
|
+
``declared`` — the build's declared client libraries from the coordinate
|
|
301
|
+
authority (:mod:`sourcecode.integration_coordinates`). They are reported in
|
|
302
|
+
their own block and never counted as integrations: a coordinate says the build
|
|
303
|
+
declares a client, not that the code uses one. Passing them is what stops this
|
|
304
|
+
report from answering `count: 0` in a document whose stack block lists a client
|
|
305
|
+
library for the same external system (ADR-0008 R12/R14) — a contradiction that
|
|
306
|
+
survived two releases on the LDAP surface.
|
|
307
|
+
"""
|
|
294
308
|
recs = self.detect()
|
|
295
309
|
by_kind: dict[str, int] = {}
|
|
296
310
|
for r in recs:
|
|
@@ -306,11 +320,49 @@ class SemanticIntegrationEngine:
|
|
|
306
320
|
if r.confidence is not None:
|
|
307
321
|
rec["confidence"] = r.confidence
|
|
308
322
|
integrations.append(rec)
|
|
323
|
+
|
|
324
|
+
declared_list = list(declared or [])
|
|
325
|
+
observed_kinds = set(by_kind)
|
|
326
|
+
declared_block = [
|
|
327
|
+
{**d.to_dict(), "observed_in_source": d.kind in observed_kinds}
|
|
328
|
+
for d in declared_list
|
|
329
|
+
]
|
|
330
|
+
declared_not_observed = sorted(
|
|
331
|
+
{d.kind for d in declared_list} - observed_kinds
|
|
332
|
+
)
|
|
333
|
+
if declared_not_observed:
|
|
334
|
+
_cites = "; ".join(
|
|
335
|
+
f"{d.kind} ({d.coordinate} at {d.evidence})"
|
|
336
|
+
for d in declared_list if d.kind in set(declared_not_observed)
|
|
337
|
+
)
|
|
338
|
+
coverage_reason = (
|
|
339
|
+
f"{coverage_reason} The build DECLARES a client library for "
|
|
340
|
+
f"{len(declared_not_observed)} kind(s) with no client construct in "
|
|
341
|
+
f"source — {_cites}. A configuration-wired client (properties, YAML, "
|
|
342
|
+
"XML, or a starter's auto-configuration) is not statically visible as a "
|
|
343
|
+
"construct, so read these as declared-not-observed, neither as an "
|
|
344
|
+
"integration record nor as their absence."
|
|
345
|
+
)
|
|
346
|
+
|
|
309
347
|
return {
|
|
310
348
|
"integrations": integrations,
|
|
311
349
|
"by_kind": {k: by_kind[k] for k in sorted(by_kind)},
|
|
312
350
|
"count": len(recs),
|
|
313
351
|
"confidence": "observed" if recs else "not_analyzed",
|
|
352
|
+
# Declared ≠ observed. Two facts about the same external system, each under
|
|
353
|
+
# its own name and its own evidence, so no reader has to reconcile a count of
|
|
354
|
+
# 0 here against a client library named in the stack block.
|
|
355
|
+
"declared_clients": declared_block,
|
|
356
|
+
"declared_clients_count": len(declared_block),
|
|
357
|
+
"kinds_declared_not_observed": declared_not_observed,
|
|
358
|
+
"declared_clients_note": (
|
|
359
|
+
"Client libraries the BUILD declares (Maven/Gradle coordinates, "
|
|
360
|
+
"test/provided scope excluded). Evidence of a declared client, never of "
|
|
361
|
+
"an observed integration: a coordinate does not prove the code uses it, "
|
|
362
|
+
"and its absence from `integrations` does not prove the system does not "
|
|
363
|
+
"talk to that party — a properties/YAML/XML-wired client has no construct "
|
|
364
|
+
"to find."
|
|
365
|
+
),
|
|
314
366
|
"coverage_confidence": coverage_confidence,
|
|
315
367
|
"coverage_confidence_reason": coverage_reason,
|
|
316
368
|
"coverage_note": (
|
sourcecode/spring_impact.py
CHANGED
|
@@ -96,7 +96,13 @@ class AffectedEndpoint:
|
|
|
96
96
|
controller_class: str
|
|
97
97
|
handler_symbol: str
|
|
98
98
|
source_file: str
|
|
99
|
-
|
|
99
|
+
# Guard vocabulary DECLARED on the handler ("spring_pre_authorize",
|
|
100
|
+
# "none_detected", "unknown"). Evidence about an annotation — never a statement
|
|
101
|
+
# about whether the route is covered. The coverage answer is `security_verdict`,
|
|
102
|
+
# bound from the one authority (security_posture.endpoint_security_surface).
|
|
103
|
+
security_policy: str
|
|
104
|
+
security_verdict: str = "coverage_unknown"
|
|
105
|
+
security_confidence: str = "Unknown"
|
|
100
106
|
|
|
101
107
|
def to_dict(self) -> dict:
|
|
102
108
|
return {
|
|
@@ -106,6 +112,9 @@ class AffectedEndpoint:
|
|
|
106
112
|
"controller_class": self.controller_class,
|
|
107
113
|
"handler_symbol": self.handler_symbol,
|
|
108
114
|
"source_file": self.source_file,
|
|
115
|
+
"security_verdict": self.security_verdict,
|
|
116
|
+
"security_confidence": self.security_confidence,
|
|
117
|
+
# kept for consumers reading the declared annotation; see the field note
|
|
109
118
|
"security_policy": self.security_policy,
|
|
110
119
|
}
|
|
111
120
|
|
|
@@ -863,11 +872,43 @@ def _build_chain_explanation(
|
|
|
863
872
|
# Security surface aggregation
|
|
864
873
|
# ---------------------------------------------------------------------------
|
|
865
874
|
|
|
875
|
+
def _bind_security_surface(
|
|
876
|
+
endpoints_affected: list[AffectedEndpoint],
|
|
877
|
+
surface: dict[str, dict],
|
|
878
|
+
*,
|
|
879
|
+
unavailable_reason: str = "",
|
|
880
|
+
) -> None:
|
|
881
|
+
"""Bind each affected endpoint to the one security authority, in place.
|
|
882
|
+
|
|
883
|
+
An endpoint the authority does not speak about (or an authority that could not
|
|
884
|
+
be built) is `coverage_unknown` with its reason — never the declared policy
|
|
885
|
+
reused as a coverage claim (ADR-0008 R9).
|
|
886
|
+
"""
|
|
887
|
+
from sourcecode.security_posture import unknown_endpoint_surface
|
|
888
|
+
|
|
889
|
+
for ep in endpoints_affected:
|
|
890
|
+
answer = surface.get(ep.endpoint_id)
|
|
891
|
+
if answer is None:
|
|
892
|
+
answer = unknown_endpoint_surface(
|
|
893
|
+
unavailable_reason
|
|
894
|
+
or "endpoint is not present in the security-posture projection",
|
|
895
|
+
declared_policy=ep.security_policy,
|
|
896
|
+
)
|
|
897
|
+
ep.security_verdict = answer.get("verdict", "coverage_unknown")
|
|
898
|
+
ep.security_confidence = answer.get("confidence", "Unknown")
|
|
899
|
+
|
|
900
|
+
|
|
866
901
|
def _build_security_surfaces(
|
|
867
902
|
endpoints_affected: list[AffectedEndpoint],
|
|
868
903
|
impact_findings: list[SpringFinding],
|
|
869
904
|
) -> list[dict]:
|
|
870
|
-
"""Per-endpoint security surface with associated finding IDs.
|
|
905
|
+
"""Per-endpoint security surface with associated finding IDs.
|
|
906
|
+
|
|
907
|
+
The surface reports the authority's verdict (`security_posture`); the declared
|
|
908
|
+
annotation policy travels with it as evidence. Emitting the declared policy
|
|
909
|
+
alone is what made this command answer "none_detected" for 324 Keycloak
|
|
910
|
+
endpoints the same run classified `protected_custom`.
|
|
911
|
+
"""
|
|
871
912
|
finding_by_ep: dict[str, list[str]] = {}
|
|
872
913
|
for f in impact_findings:
|
|
873
914
|
if f.category == "security":
|
|
@@ -881,6 +922,11 @@ def _build_security_surfaces(
|
|
|
881
922
|
"endpoint_id": ep.endpoint_id,
|
|
882
923
|
"method": ep.method,
|
|
883
924
|
"path": ep.path,
|
|
925
|
+
"verdict": ep.security_verdict,
|
|
926
|
+
"confidence": ep.security_confidence,
|
|
927
|
+
"declared_policy": ep.security_policy,
|
|
928
|
+
# deprecated alias of `declared_policy` (two-minor window): it always
|
|
929
|
+
# carried the declared annotation, never a coverage verdict.
|
|
884
930
|
"security_policy": ep.security_policy,
|
|
885
931
|
"security_findings": finding_by_ep.get(ep.endpoint_id, []),
|
|
886
932
|
})
|
|
@@ -1102,10 +1148,13 @@ class ImpactOrchestrator:
|
|
|
1102
1148
|
pass
|
|
1103
1149
|
|
|
1104
1150
|
# ── 5. TX + SEC audit findings, filtered to call chain ────────────
|
|
1151
|
+
# The security audit already derives the posture projection; take it from
|
|
1152
|
+
# there rather than deriving the same answer a second time (ADR-0008 R1).
|
|
1153
|
+
posture_dict: dict = {}
|
|
1105
1154
|
if prebuilt_findings is not None:
|
|
1106
1155
|
all_findings = prebuilt_findings
|
|
1107
1156
|
else:
|
|
1108
|
-
all_findings = _run_audit_for_chain(cir, model, root)
|
|
1157
|
+
all_findings, posture_dict = _run_audit_for_chain(cir, model, root)
|
|
1109
1158
|
|
|
1110
1159
|
impact_findings_raw = _filter_findings(
|
|
1111
1160
|
all_findings, seed_fqns, direct_callers_raw, indirect_callers_raw,
|
|
@@ -1118,6 +1167,18 @@ class ImpactOrchestrator:
|
|
|
1118
1167
|
impact_findings = [f.to_dict() for f in impact_findings_raw]
|
|
1119
1168
|
|
|
1120
1169
|
# ── 6. Security surfaces ──────────────────────────────────────────
|
|
1170
|
+
security_surface_index, surface_gap = _endpoint_security_authority(
|
|
1171
|
+
cir, root, posture_dict,
|
|
1172
|
+
)
|
|
1173
|
+
# The gap is only worth a warning when it actually withheld an answer:
|
|
1174
|
+
# a repo with no affected endpoints is not missing a security surface.
|
|
1175
|
+
surface_gap_warned = False
|
|
1176
|
+
if surface_gap and endpoints_affected:
|
|
1177
|
+
warnings.append(surface_gap)
|
|
1178
|
+
surface_gap_warned = True
|
|
1179
|
+
_bind_security_surface(
|
|
1180
|
+
endpoints_affected, security_surface_index, unavailable_reason=surface_gap,
|
|
1181
|
+
)
|
|
1121
1182
|
security_surfaces = _build_security_surfaces(endpoints_affected, impact_findings_raw)
|
|
1122
1183
|
|
|
1123
1184
|
# ── 7. Risk ───────────────────────────────────────────────────────
|
|
@@ -1176,6 +1237,13 @@ class ImpactOrchestrator:
|
|
|
1176
1237
|
key=lambda f: (SEVERITY_ORDER.get(f.severity, 9), f.symbol)
|
|
1177
1238
|
)
|
|
1178
1239
|
impact_findings = [f.to_dict() for f in impact_findings_raw]
|
|
1240
|
+
_bind_security_surface(
|
|
1241
|
+
endpoints_affected, security_surface_index,
|
|
1242
|
+
unavailable_reason=surface_gap,
|
|
1243
|
+
)
|
|
1244
|
+
if surface_gap and endpoints_affected and not surface_gap_warned:
|
|
1245
|
+
warnings.append(surface_gap)
|
|
1246
|
+
surface_gap_warned = True
|
|
1179
1247
|
security_surfaces = _build_security_surfaces(endpoints_affected, impact_findings_raw)
|
|
1180
1248
|
risk_level, risk_score, risk_score_raw = _compute_risk(
|
|
1181
1249
|
len(direct_callers), len(indirect_callers),
|
|
@@ -1456,16 +1524,21 @@ def _run_audit_for_chain(
|
|
|
1456
1524
|
cir: CanonicalRepositoryIR,
|
|
1457
1525
|
model: SpringSemanticModel,
|
|
1458
1526
|
root: Optional[Path],
|
|
1459
|
-
) -> list[SpringFinding]:
|
|
1460
|
-
"""Run TX + SEC audit
|
|
1527
|
+
) -> tuple[list[SpringFinding], dict]:
|
|
1528
|
+
"""Run TX + SEC audit; return (combined findings, security-posture projection).
|
|
1529
|
+
|
|
1530
|
+
The posture travels out with the findings because the security audit already
|
|
1531
|
+
derives it — the impact chain binds to that derivation instead of computing a
|
|
1532
|
+
second answer to the same question (ADR-0008 R1).
|
|
1461
1533
|
|
|
1462
1534
|
Uses the pre-built model — no duplicate CIR traversal.
|
|
1463
|
-
Never raises; returns [] on any error.
|
|
1535
|
+
Never raises; returns ([], {}) on any error.
|
|
1464
1536
|
"""
|
|
1465
1537
|
from sourcecode.spring_security_audit import run_security_audit
|
|
1466
1538
|
from sourcecode.spring_tx_analyzer import run_tx_audit
|
|
1467
1539
|
|
|
1468
1540
|
findings: list[SpringFinding] = []
|
|
1541
|
+
posture: dict = {}
|
|
1469
1542
|
try:
|
|
1470
1543
|
tx_result = run_tx_audit(cir, root=root, model=model)
|
|
1471
1544
|
findings.extend(tx_result.findings)
|
|
@@ -1474,9 +1547,53 @@ def _run_audit_for_chain(
|
|
|
1474
1547
|
try:
|
|
1475
1548
|
sec_result = run_security_audit(cir, root=root, model=model)
|
|
1476
1549
|
findings.extend(sec_result.findings)
|
|
1550
|
+
posture = getattr(sec_result, "security_posture", None) or {}
|
|
1551
|
+
except Exception:
|
|
1552
|
+
pass
|
|
1553
|
+
return findings, posture
|
|
1554
|
+
|
|
1555
|
+
|
|
1556
|
+
# Per-CIR memo of the endpoint security authority's answer (one derivation per repo).
|
|
1557
|
+
_SURFACE_CACHE_ATTR = "_impact_endpoint_security_surface"
|
|
1558
|
+
|
|
1559
|
+
|
|
1560
|
+
def _endpoint_security_authority(
|
|
1561
|
+
cir: CanonicalRepositoryIR,
|
|
1562
|
+
root: Optional[Path],
|
|
1563
|
+
posture: dict,
|
|
1564
|
+
) -> tuple[dict[str, dict], str]:
|
|
1565
|
+
"""(endpoint_id → security answer, reason it is unavailable).
|
|
1566
|
+
|
|
1567
|
+
Reuses ``posture`` when the audit already produced it; otherwise asks the
|
|
1568
|
+
authority directly (the `prebuilt_findings` path, where no audit ran here).
|
|
1569
|
+
Memoized on the CIR because `pr-impact` issues one query per changed class
|
|
1570
|
+
against the same repository — reuse is what keeps "one authority" from meaning
|
|
1571
|
+
"one full projection per query".
|
|
1572
|
+
"""
|
|
1573
|
+
from sourcecode.security_posture import endpoint_security_surface
|
|
1574
|
+
|
|
1575
|
+
cached = getattr(cir, _SURFACE_CACHE_ATTR, None)
|
|
1576
|
+
if cached is not None:
|
|
1577
|
+
return cached
|
|
1578
|
+
|
|
1579
|
+
try:
|
|
1580
|
+
surface = endpoint_security_surface(
|
|
1581
|
+
cir, root=root, posture=posture or None,
|
|
1582
|
+
)
|
|
1583
|
+
except Exception:
|
|
1584
|
+
surface = {}
|
|
1585
|
+
if not surface:
|
|
1586
|
+
answer = ({}, (
|
|
1587
|
+
"security-posture projection unavailable for this repository; every "
|
|
1588
|
+
"endpoint security answer is coverage_unknown (absence of evidence)"
|
|
1589
|
+
))
|
|
1590
|
+
else:
|
|
1591
|
+
answer = (surface, "")
|
|
1592
|
+
try:
|
|
1593
|
+
setattr(cir, _SURFACE_CACHE_ATTR, answer)
|
|
1477
1594
|
except Exception:
|
|
1478
1595
|
pass
|
|
1479
|
-
return
|
|
1596
|
+
return answer
|
|
1480
1597
|
|
|
1481
1598
|
|
|
1482
1599
|
# ---------------------------------------------------------------------------
|
sourcecode/spring_semantic.py
CHANGED
|
@@ -16,7 +16,7 @@ from __future__ import annotations
|
|
|
16
16
|
import re
|
|
17
17
|
import time
|
|
18
18
|
from dataclasses import dataclass, field
|
|
19
|
-
from typing import TYPE_CHECKING, Optional
|
|
19
|
+
from typing import TYPE_CHECKING, Iterable, Optional
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
22
22
|
from sourcecode.canonical_ir import CanonicalRepositoryIR
|
|
@@ -295,12 +295,24 @@ def build_tx_index(cir: "CanonicalRepositoryIR") -> TransactionBoundaryIndex:
|
|
|
295
295
|
3. Method-level boundaries override class-level for the same method.
|
|
296
296
|
4. Class-level boundaries are inherited by methods that lack their own.
|
|
297
297
|
"""
|
|
298
|
+
return build_tx_index_from_ir(
|
|
299
|
+
getattr(cir, "_raw_ir", {}) or {}, repo_id=getattr(cir, "cir_hash", "")[:16]
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def build_tx_index_from_ir(raw_ir: dict, repo_id: str = "") -> TransactionBoundaryIndex:
|
|
304
|
+
"""Same index, built straight from a repo IR dict.
|
|
305
|
+
|
|
306
|
+
THE authority for "where are the @Transactional boundaries" (ADR-0008 R1).
|
|
307
|
+
`build_tx_index` is the CanonicalRepositoryIR-shaped entry point; consumers that
|
|
308
|
+
hold the raw IR dict (blast radius) call this one instead of re-deriving
|
|
309
|
+
boundaries from graph node roles. Both produce the same index from the same nodes.
|
|
310
|
+
"""
|
|
298
311
|
t0 = time.monotonic()
|
|
299
|
-
index = TransactionBoundaryIndex(repo_id=
|
|
312
|
+
index = TransactionBoundaryIndex(repo_id=repo_id)
|
|
300
313
|
|
|
301
314
|
try:
|
|
302
|
-
|
|
303
|
-
graph = raw_ir.get("graph") or {}
|
|
315
|
+
graph = (raw_ir or {}).get("graph") or {}
|
|
304
316
|
nodes = graph.get("nodes") or []
|
|
305
317
|
|
|
306
318
|
# Pass 1: build meta-@Transactional map from annotation-type nodes.
|
|
@@ -378,3 +390,43 @@ def build_tx_index(cir: "CanonicalRepositoryIR") -> TransactionBoundaryIndex:
|
|
|
378
390
|
|
|
379
391
|
index.build_time_ms = round((time.monotonic() - t0) * 1000, 2)
|
|
380
392
|
return index
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def boundaries_declared_within(
|
|
396
|
+
index: TransactionBoundaryIndex, symbols: "Iterable[str]"
|
|
397
|
+
) -> list[TransactionBoundary]:
|
|
398
|
+
"""The @Transactional declaration sites carried by *symbols*, ordered by symbol.
|
|
399
|
+
|
|
400
|
+
THE authority for "which transaction boundaries belong to this set of symbols"
|
|
401
|
+
(ADR-0008 R1). Every consumer that reports transaction boundaries over a set of
|
|
402
|
+
symbols — a blast radius, a caller closure, a module — calls this. A consumer that
|
|
403
|
+
re-derives the answer from graph node roles, annotation strings or naming is in
|
|
404
|
+
violation of the ADR, and is how `impact` came to report 0 boundaries where
|
|
405
|
+
`retrieve transactions-reaching` reported 47 on the same symbol.
|
|
406
|
+
|
|
407
|
+
Membership follows Spring's own inheritance, in both directions and no further:
|
|
408
|
+
|
|
409
|
+
- the declaration site itself is in the set;
|
|
410
|
+
- a **class**-level boundary when a method of that class is in the set (the method
|
|
411
|
+
runs inside it);
|
|
412
|
+
- a **method**-level boundary when its class is in the set (asking about a class asks
|
|
413
|
+
about the methods it declares).
|
|
414
|
+
|
|
415
|
+
Asking about one method does *not* pull in a sibling method's own boundary — that
|
|
416
|
+
sibling is a different code path, and reporting it would be the kind of quietly
|
|
417
|
+
inflated count this authority exists to prevent.
|
|
418
|
+
"""
|
|
419
|
+
wanted = {s for s in symbols if s}
|
|
420
|
+
if not wanted:
|
|
421
|
+
return []
|
|
422
|
+
declaring_classes = {s.split("#", 1)[0] for s in wanted if "#" in s}
|
|
423
|
+
|
|
424
|
+
def _belongs(b: TransactionBoundary) -> bool:
|
|
425
|
+
if b.symbol in wanted:
|
|
426
|
+
return True
|
|
427
|
+
if b.scope == "class":
|
|
428
|
+
return b.symbol in declaring_classes
|
|
429
|
+
return _enclosing_class(b.symbol) in wanted
|
|
430
|
+
|
|
431
|
+
found = [b for b in index.all_declared if _belongs(b)]
|
|
432
|
+
return sorted(found, key=lambda b: (b.symbol, b.scope, b.source_file))
|
|
@@ -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 |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=4hwV87XY8dgmsv4RaWkZzBMSpB_T8EQ4A9_-xB5CzmI,308
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=yJBKjNpkY6bpueYJ2YnRezen3sYZDecEt7WaaNWdqug,9466
|
|
3
3
|
sourcecode/archetype.py,sha256=qWMCyL2UzSGXJhCIZj38JjS7wI15EE3eILsWiaoCZXQ,35779
|
|
4
4
|
sourcecode/architectural_baseline.py,sha256=7QzJri4pbL3nzAn9gNutZW6mZR8HHD6H2C2H1EEwYPE,17904
|
|
@@ -14,7 +14,7 @@ sourcecode/chain_rules.py,sha256=1kLHv1t7g5rMmycIYSpm3az8SjCr01Xw7LtyV2tNhSA,106
|
|
|
14
14
|
sourcecode/change_plan.py,sha256=MNgNyu4zrLGvBBaXPwyCh-T2ZGaUQX3Hm4W87uuhZ3w,7750
|
|
15
15
|
sourcecode/cir_graphs.py,sha256=9G0HHj1kw2325IDyzo2OpX73BNswEckecf4MZUXB4JM,12078
|
|
16
16
|
sourcecode/classifier.py,sha256=JBzPwSSrDG-tUHAbcKB678HRbjLpD-ohzbzzO62mgpo,20114
|
|
17
|
-
sourcecode/cli.py,sha256=
|
|
17
|
+
sourcecode/cli.py,sha256=F7HXBOhTrzLppk81GDazz6sIvowkZomei1MCRXaTutc,400663
|
|
18
18
|
sourcecode/code_notes_analyzer.py,sha256=EJemNCNc9Dn-1RZYu-aNbK0ELzmsyC4s6FdHi3XyNEI,9392
|
|
19
19
|
sourcecode/compare.py,sha256=jdePg0dNCxFpysiTGMsROL5XABLjy_zVUgVeySvdb0o,7514
|
|
20
20
|
sourcecode/confidence_analyzer.py,sha256=vnbPI-20FnHdjO6STxHW8fbaxmB4A7y58io63ibFZjc,21586
|
|
@@ -48,11 +48,13 @@ sourcecode/git_analyzer.py,sha256=irvEwddbd5_tbA_eYUZebf4mH7_nFhpjwaWAV1f7jUY,15
|
|
|
48
48
|
sourcecode/graph_analyzer.py,sha256=lp0eB1PWC20BYF-GpPhAyegRpKrUKgOmXZIcZSIX_Ks,65777
|
|
49
49
|
sourcecode/graph_evidence.py,sha256=rENNsYRZeNstX_ExNCLlbHJAruFQwxo5d00x6wO3xwI,15030
|
|
50
50
|
sourcecode/hibernate_strat.py,sha256=5GmHiB865HxKdvaxeFV07ojglUgxyOP41AY2xiHfV30,61700
|
|
51
|
+
sourcecode/integration_coordinates.py,sha256=7vGFxN4tCn_KTisYFJuixa8XkyHBoasEFB3fqM62OBI,7407
|
|
51
52
|
sourcecode/jdk_exports.py,sha256=fCrlwNAXUT9gge_joq6kMnY3zJxYB2pxqy-0w3o3MJI,874
|
|
52
53
|
sourcecode/license.py,sha256=keFuwNxdAtvK2Ds91Wl79GMYxuxWYnN5Wbw1qBpaoUI,24896
|
|
53
54
|
sourcecode/mcp_nudge.py,sha256=lKemOqK_wny2u7Ymcr2Idi5Kx8pXY02jCi-_nJYLGMg,2992
|
|
54
55
|
sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7c,22750
|
|
55
|
-
sourcecode/migrate_check.py,sha256=
|
|
56
|
+
sourcecode/migrate_check.py,sha256=TJvaShbglENOr8ikvCodnlXQy54TtT05N2ZVe02b3lc,110421
|
|
57
|
+
sourcecode/migration_blast.py,sha256=5qUsUi4n0cwBKltdJYyvyfhBkGENAYjB-d_7LZtnVks,9282
|
|
56
58
|
sourcecode/openapi_surface.py,sha256=BTt0K-woZbkbWTN77IkqeBm_Okag9owR0848fmot8sk,16207
|
|
57
59
|
sourcecode/output_budget.py,sha256=Js9yUlfQtPhqBl9R6wn_9UHVjjJc3GtLcqyfjf5t50Q,9869
|
|
58
60
|
sourcecode/parse_cache.py,sha256=IIsWhtMYpBLNyRCDVoiVpUZa62QIk1Q1sZBDNG4G4V0,6668
|
|
@@ -70,26 +72,26 @@ sourcecode/redactor.py,sha256=SB4hwIvg8h-hvcqKcDWaZvA-aSyn-at-BIRwa0tUv5E,3227
|
|
|
70
72
|
sourcecode/relevance_scorer.py,sha256=0AgEt4KrV73nioMqBgjhGjtY7L2C7L7cSyKtj3IKcrw,9408
|
|
71
73
|
sourcecode/rename_refactor.py,sha256=h6dNFlB9aZ_3q6heeHBkgXQeXaT03nvPSsYH6P8qxFg,12965
|
|
72
74
|
sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
|
|
73
|
-
sourcecode/repository_ir.py,sha256=
|
|
75
|
+
sourcecode/repository_ir.py,sha256=2S14aUMY5vpZePObd3F87kz71KUsVXQllurXjufa6A0,344648
|
|
74
76
|
sourcecode/ris.py,sha256=Hw8TakTQ6hku-Abf2k8954NwkrH_sP73_8wVH8x5khc,22079
|
|
75
77
|
sourcecode/runtime_classifier.py,sha256=uTAD6BDCiBLUZEDRfqk718kM4RTT_vAbfkcOI2_Xx58,18432
|
|
76
78
|
sourcecode/scanner.py,sha256=z3CV0rcGunu0Y8mpNgp07wI7nxT0pxw1BkXRRtI0Rpo,9609
|
|
77
79
|
sourcecode/schema.py,sha256=aHNXDf8LGyUC8ZDE_VS9kiskC2-Oswhi_WnpdGy6HDw,24897
|
|
78
80
|
sourcecode/security_config.py,sha256=KblMEoRiEjrIE68YsPaUAFebxFp8UM7MS7lAk5CGD8U,3531
|
|
79
|
-
sourcecode/security_posture.py,sha256=
|
|
81
|
+
sourcecode/security_posture.py,sha256=0tKL_RafS0dYY5W__JTeJIfopd8f-DyKEerHrvA3-Mg,40243
|
|
80
82
|
sourcecode/semantic_analyzer.py,sha256=bpgdC6m0_ftVtRf3rSdwhbhWjnZnGxRXaZVcfe4BbcQ,95414
|
|
81
83
|
sourcecode/semantic_impact_engine.py,sha256=t09IirGC3JjQDy33JZd1_WKzQVKXkoNl3-XEUr5kjis,20563
|
|
82
|
-
sourcecode/semantic_integration_engine.py,sha256=
|
|
84
|
+
sourcecode/semantic_integration_engine.py,sha256=7a0WqAInOv39f0Yr_94TYo_JP_8QpeI9KGaknpzAyQU,18899
|
|
83
85
|
sourcecode/semantic_services.py,sha256=nbUuPv-F01USTt_9CHT8iy_ucCIw3fz4W3Aquea_pd4,10782
|
|
84
86
|
sourcecode/serializer.py,sha256=BAGL2J3G3bHvJS60OcxekPvTJg8x6b1LzeLKFbGaots,131497
|
|
85
87
|
sourcecode/spring_event_topology.py,sha256=5_ON_21Le5zbG-1GRc5GLIi5HJfy_QjcXLVPC5WeUGQ,18055
|
|
86
88
|
sourcecode/spring_findings.py,sha256=ICcR2qEZgYzWypZzsoaEmVeMYZ0YkuZ8AvTwA-rcVOs,7604
|
|
87
|
-
sourcecode/spring_impact.py,sha256=
|
|
89
|
+
sourcecode/spring_impact.py,sha256=Cm2KZplpQHjdjUC8jPX1SNnLHxOdM3HsUBNFGwgTP18,80145
|
|
88
90
|
sourcecode/spring_model.py,sha256=zOAgFmrRbG4a6KLm1TJl55aWMyPNsz3OS3FSczqPG6A,16594
|
|
89
91
|
sourcecode/spring_profiles.py,sha256=YPgBH3RuML-mtid7YwUMfA9sfSXPZwknYL7UvRMoYbE,7946
|
|
90
92
|
sourcecode/spring_properties.py,sha256=bQSOVrtj7YENsCjMQTTlVh-Gm_K0n0DdWzjPRUP38C4,8394
|
|
91
93
|
sourcecode/spring_security_audit.py,sha256=FteXv5GH-iE07GLEqPpoX7sVXf_dxRns8zxh9_m6By0,23015
|
|
92
|
-
sourcecode/spring_semantic.py,sha256=
|
|
94
|
+
sourcecode/spring_semantic.py,sha256=UrmLg_4gmBVmMqvSnUMgSReVEERVGv3X7LYDBEs6k_o,16598
|
|
93
95
|
sourcecode/spring_tx_analyzer.py,sha256=lp0h5Pzzd3fPxHAAagMmG8PqR1-v2uVpFIG1o7tCWQs,41148
|
|
94
96
|
sourcecode/summarizer.py,sha256=sr0-tfecFKCr-fSkPPWbl-t9HC7SY2ZxkjnnXX8DB2A,26621
|
|
95
97
|
sourcecode/token_estimate.py,sha256=ZP3C54aQLExuf32Yvrjok2TAS2oSSyNVRA_HjhsD6Eg,7165
|
|
@@ -111,7 +113,7 @@ sourcecode/detectors/elixir.py,sha256=jCpvt5Yi6jvplc80ovRtWh17q-11ZGo9qX7o8b57TJ
|
|
|
111
113
|
sourcecode/detectors/go.py,sha256=2r66uRQfeTWsqxr4HDhT6vExZErby0t46QXLHVBRv9w,2782
|
|
112
114
|
sourcecode/detectors/heuristic.py,sha256=7cRxrip4yIaggYzZJB6ef8yHKh-gHgiH_pXMFcjlyFU,3723
|
|
113
115
|
sourcecode/detectors/hybrid.py,sha256=IGFRUVsAZ1ooRlFdznCeJAV6vy1yVDx-VyghvLtddXc,9101
|
|
114
|
-
sourcecode/detectors/java.py,sha256=
|
|
116
|
+
sourcecode/detectors/java.py,sha256=v47yvL4mftcK35cVBpO7gru6O5AcEaKml-OoNyVU0Bg,45246
|
|
115
117
|
sourcecode/detectors/jvm_ext.py,sha256=EgHJ5W8EE-ZTN9V607mVzohyKgZE8Mc2jCi-DF8RAZU,2616
|
|
116
118
|
sourcecode/detectors/nodejs.py,sha256=Hg3Gmr7yIMJFiLoDwOTk2wtu00wxIs6kZf-oQujTFUA,13187
|
|
117
119
|
sourcecode/detectors/parsers.py,sha256=ug9K31tyHqinmv0HkIVQVjdTZpBv67FYKAEf52YXOSM,3178
|
|
@@ -125,9 +127,9 @@ sourcecode/detectors/terraform.py,sha256=cxORPR_zVLOJpHlh4e9JnFpkQsn_UnqMMom5yG6
|
|
|
125
127
|
sourcecode/detectors/tooling.py,sha256=8CKbtxwQoABP-WyBRNmdAmHDOvAH57AR1cF4UKuWEdQ,2074
|
|
126
128
|
sourcecode/mcp/__init__.py,sha256=XU4HfRGbdid8wdUA0x_4f7uKZD1z3mv_XUY_WU_T9Mw,179
|
|
127
129
|
sourcecode/mcp/orchestrator.py,sha256=diVoQgn24QmgPL3Ev8Sp6hsvh02OqY3MktHXOzrlodo,36525
|
|
128
|
-
sourcecode/mcp/registry.py,sha256=
|
|
130
|
+
sourcecode/mcp/registry.py,sha256=1qIcwtl01AGjEM3yMOwYgr_Ck3y-LZeRvh8i70Nhl_w,68258
|
|
129
131
|
sourcecode/mcp/runner.py,sha256=RGimCwhLyHxjCvH7q72y12HWEnATXZLC-NBWhkzlu0I,2852
|
|
130
|
-
sourcecode/mcp/server.py,sha256=
|
|
132
|
+
sourcecode/mcp/server.py,sha256=GH7bgJRVVkBJf-orj_Zmn_mEqMENG76-KNREJS7E5FY,65354
|
|
131
133
|
sourcecode/mcp/onboarding/__init__.py,sha256=sj2PWqEBmMc4zBNkomg89WtL0M6S7A9yb7_wAuSWNP4,66
|
|
132
134
|
sourcecode/mcp/onboarding/applier.py,sha256=Sx9vHTaXr_M1Bs4uMjO7qa_a2_p4J9oz93oJsKQ7ds4,3298
|
|
133
135
|
sourcecode/mcp/onboarding/backup.py,sha256=ihqGOR8QTX8HASRSEDyfFyXr5bkXrygPHamv4p9KTmk,1452
|
|
@@ -146,9 +148,9 @@ sourcecode/retrieval/retriever.py,sha256=YdnbaXaWhFKyShuuE6fj36GfgpuGetqOFzx7_D2
|
|
|
146
148
|
sourcecode/retrieval/runtime.py,sha256=ODQlPqyS4OfMtCb1oEA_0t7vK0uKPBLhB4K2CnytPUU,10822
|
|
147
149
|
sourcecode/retrieval/steps.py,sha256=wyOiO84fuDgdd-HdugzGSEriN-lN891PTc3gU7lgcmI,9959
|
|
148
150
|
sourcecode/retrieval/steps_endpoint.py,sha256=H_o4YsqgM198ZHtJg-UzhcmnFzbBeaYXrPNIDoc2IjI,13526
|
|
149
|
-
sourcecode/retrieval/steps_graph.py,sha256=
|
|
151
|
+
sourcecode/retrieval/steps_graph.py,sha256=E45vN9pA1lpKQ4pO2JP9-1cwIXoy1hWW6jsBDryeHGM,10684
|
|
150
152
|
sourcecode/retrieval/steps_impact.py,sha256=M0AwU5Ue7nkj-A7DYgQI2BP0BV1b0TFZRrYIydF-Ajw,11605
|
|
151
|
-
sourcecode/retrieval/steps_intf.py,sha256=
|
|
153
|
+
sourcecode/retrieval/steps_intf.py,sha256=BIopGUhxwutV51r8-1bTCC_VTYENJiHbREF13pB5mSo,11697
|
|
152
154
|
sourcecode/retrieval/steps_struct.py,sha256=jO9L49hziTlS_xhuhCGUavgyAL3ZAnJSOZB-UA4GaRM,13893
|
|
153
155
|
sourcecode/retrieval/steps_txsec.py,sha256=LKRzALzRw_0aQ3_tpbd34mt8u7o690g5tCnp871j7P4,17804
|
|
154
156
|
sourcecode/schemas/envelope-v1.schema.json,sha256=lABkyKqXND039JUY2R6KK47OCDco5mq5PLgkcCXFhOQ,3477
|
|
@@ -158,8 +160,8 @@ sourcecode/telemetry/consent.py,sha256=GuhCiA_x2n3LwGBkZnvyIwtFcf5Zwa8O0DdLqkATI
|
|
|
158
160
|
sourcecode/telemetry/events.py,sha256=4_yeO58U-Cwc1Qb27VB0_EjhmroY0k91n3_VGxeALB8,2776
|
|
159
161
|
sourcecode/telemetry/filters.py,sha256=RzxauTz8HliO4BllQnXEXc7zTeqdCZi5MgqGEDuW7OQ,6570
|
|
160
162
|
sourcecode/telemetry/transport.py,sha256=4gGHsq0WeY9VywEZXA3vUxykfiYnw9uuqfjAAec7F8o,1681
|
|
161
|
-
sourcecode-3.
|
|
162
|
-
sourcecode-3.
|
|
163
|
-
sourcecode-3.
|
|
164
|
-
sourcecode-3.
|
|
165
|
-
sourcecode-3.
|
|
163
|
+
sourcecode-3.2.0.dist-info/METADATA,sha256=O9_rjaiou-XB3P9QZsqnkmS1OAKcBCiEsxJOjjN2D9A,11286
|
|
164
|
+
sourcecode-3.2.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
165
|
+
sourcecode-3.2.0.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
|
|
166
|
+
sourcecode-3.2.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
167
|
+
sourcecode-3.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|