sourcecode 1.62.0__py3-none-any.whl → 1.63.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.
- sourcecode/__init__.py +1 -1
- sourcecode/cli.py +6 -3
- sourcecode/hibernate_strat.py +488 -107
- sourcecode/mcp/server.py +5 -1
- sourcecode/migrate_check.py +21 -1
- {sourcecode-1.62.0.dist-info → sourcecode-1.63.0.dist-info}/METADATA +53 -1
- {sourcecode-1.62.0.dist-info → sourcecode-1.63.0.dist-info}/RECORD +10 -10
- {sourcecode-1.62.0.dist-info → sourcecode-1.63.0.dist-info}/WHEEL +0 -0
- {sourcecode-1.62.0.dist-info → sourcecode-1.63.0.dist-info}/entry_points.txt +0 -0
- {sourcecode-1.62.0.dist-info → sourcecode-1.63.0.dist-info}/licenses/LICENSE +0 -0
sourcecode/__init__.py
CHANGED
sourcecode/cli.py
CHANGED
|
@@ -4841,9 +4841,12 @@ def migrate_check_cmd(
|
|
|
4841
4841
|
\b
|
|
4842
4842
|
Hibernate 5→6 stratification (in the 'hibernate' output section):
|
|
4843
4843
|
4 independent layers (JPA annotations / Criteria / HQL / SPI), a per-layer
|
|
4844
|
-
risk matrix, a module exposure map, critical call-chain
|
|
4845
|
-
|
|
4846
|
-
|
|
4844
|
+
risk matrix with effort ranges, a module exposure map, critical call-chain
|
|
4845
|
+
detection, golden-SQL hotspots, a hibernate_readiness score, and an UPGRADE
|
|
4846
|
+
vs REWRITE verdict (dynamic Criteria, custom SPI, reflection-built queries,
|
|
4847
|
+
or concatenated query strings force the REWRITE classification).
|
|
4848
|
+
Emits actionable rewrite_targets[] (call-site line ranges → target_api +
|
|
4849
|
+
migration_kind) so a migration agent can consume the output directly.
|
|
4847
4850
|
|
|
4848
4851
|
\b
|
|
4849
4852
|
Examples:
|
sourcecode/hibernate_strat.py
CHANGED
|
@@ -10,25 +10,35 @@ independent migration domains and classifies each one on its own risk axis:
|
|
|
10
10
|
3. HQL / String-based Queries — MEDIUM, escalates to HIGH on concatenation
|
|
11
11
|
4. Hibernate SPI / Internal — CRITICAL BLOCKER (UserType, Interceptor, SPI)
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Beyond the diagnostic summary it emits **actionable, machine-readable rewrite
|
|
14
|
+
targets** at call-site granularity (`rewrite_targets[]`) so a migration agent can
|
|
15
|
+
consume the output directly instead of re-parsing the repository. It also produces:
|
|
14
16
|
- a module-level Hibernate exposure map,
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
17
|
+
- per-layer manual/assisted/mechanical sub-counts (static vs dynamic Criteria),
|
|
18
|
+
- honest effort ranges (low/high/confidence) + an auditable effort model,
|
|
19
|
+
- a hibernate_readiness score (0-100),
|
|
20
|
+
- critical call-chain detection and golden-SQL instrumentation hotspots,
|
|
21
|
+
- separation of observable code risk from inferred runtime risk,
|
|
22
|
+
- an UPGRADE-vs-REWRITE verdict via stop-condition logic.
|
|
18
23
|
|
|
19
24
|
Entry point: analyze_hibernate(file_paths, root) → HibernateStratification
|
|
20
25
|
|
|
21
|
-
This is purely additive
|
|
22
|
-
it does not assume that a Spring Boot upgrade resolves ORM risk.
|
|
26
|
+
This is purely additive and does not aggregate Hibernate into a single score.
|
|
23
27
|
"""
|
|
24
28
|
from __future__ import annotations
|
|
25
29
|
|
|
30
|
+
import bisect
|
|
31
|
+
import hashlib
|
|
26
32
|
import re
|
|
27
33
|
from dataclasses import dataclass, field
|
|
28
34
|
from pathlib import Path
|
|
29
35
|
from typing import Optional
|
|
30
36
|
|
|
31
37
|
|
|
38
|
+
# Sub-schema version for the `hibernate` output section. Bump on shape changes.
|
|
39
|
+
HIBERNATE_SCHEMA_VERSION = "2.0"
|
|
40
|
+
|
|
41
|
+
|
|
32
42
|
# ---------------------------------------------------------------------------
|
|
33
43
|
# Layer identifiers
|
|
34
44
|
# ---------------------------------------------------------------------------
|
|
@@ -44,6 +54,9 @@ _LAYER_TITLES: dict[str, str] = {
|
|
|
44
54
|
LAYER_HQL: "HQL / String-based Queries",
|
|
45
55
|
LAYER_SPI: "Hibernate SPI / Internal API",
|
|
46
56
|
}
|
|
57
|
+
_LAYER_CODE: dict[str, str] = {
|
|
58
|
+
LAYER_JPA: "JPA", LAYER_CRITERIA: "CRIT", LAYER_HQL: "HQL", LAYER_SPI: "SPI",
|
|
59
|
+
}
|
|
47
60
|
|
|
48
61
|
# Classification verdicts
|
|
49
62
|
CLASS_NONE = "none"
|
|
@@ -60,6 +73,19 @@ _CLASS_LABELS: dict[str, str] = {
|
|
|
60
73
|
|
|
61
74
|
_SEVERITY_RANK: dict[str, int] = {"critical": 0, "high": 1, "medium": 2, "low": 3}
|
|
62
75
|
|
|
76
|
+
# Migration kinds (ascending automatability)
|
|
77
|
+
KIND_MANUAL = "manual_rewrite"
|
|
78
|
+
KIND_ASSISTED = "assisted"
|
|
79
|
+
KIND_MECHANICAL = "mechanical"
|
|
80
|
+
KIND_REVIEW = "review"
|
|
81
|
+
|
|
82
|
+
# Effort range multipliers by confidence band: (low_mult, high_mult).
|
|
83
|
+
_CONF_MULT: dict[str, tuple[float, float]] = {
|
|
84
|
+
"high": (0.9, 1.2),
|
|
85
|
+
"medium": (0.7, 1.5),
|
|
86
|
+
"low": (0.5, 2.0),
|
|
87
|
+
}
|
|
88
|
+
|
|
63
89
|
|
|
64
90
|
# ---------------------------------------------------------------------------
|
|
65
91
|
# Detection patterns
|
|
@@ -73,7 +99,7 @@ _JPA_ANNOTATION_RE = re.compile(
|
|
|
73
99
|
)
|
|
74
100
|
# Hibernate / JPA annotations deprecated or reworked in Hibernate 6 (escalate).
|
|
75
101
|
_JPA_DEPRECATED_RE = re.compile(
|
|
76
|
-
r"@
|
|
102
|
+
r"@TypeDefs?\b|"
|
|
77
103
|
r"org\.hibernate\.annotations\.Type\b|"
|
|
78
104
|
r"@Type\s*\(\s*type\s*=|"
|
|
79
105
|
r"@GenericGenerator\b|"
|
|
@@ -83,8 +109,7 @@ _JPA_DEPRECATED_RE = re.compile(
|
|
|
83
109
|
# Layer 2 — JPA Criteria API + legacy Hibernate Criteria.
|
|
84
110
|
_CRITERIA_JPA_RE = re.compile(
|
|
85
111
|
r"\bCriteriaBuilder\b|\bCriteriaQuery\b|\bCriteriaUpdate\b|"
|
|
86
|
-
r"\bCriteriaDelete\b|\bRoot\s*<|\bPredicate\b
|
|
87
|
-
r"persistence\.criteria"
|
|
112
|
+
r"\bCriteriaDelete\b|\bRoot\s*<|\bPredicate\b|persistence\.criteria"
|
|
88
113
|
)
|
|
89
114
|
_CRITERIA_LEGACY_RE = re.compile(
|
|
90
115
|
r"org\.hibernate\.Criteria\b|\.createCriteria\s*\(|\bDetachedCriteria\b|"
|
|
@@ -133,21 +158,65 @@ _REFLECTION_RE = re.compile(
|
|
|
133
158
|
r"\.getMetamodel\s*\("
|
|
134
159
|
)
|
|
135
160
|
|
|
161
|
+
# Lightweight symbol indexing.
|
|
162
|
+
_CLASS_DECL_RE = re.compile(r"\b(?:class|interface|enum)\s+(\w+)")
|
|
163
|
+
_METHOD_DECL_RE = re.compile(r"\b(\w+)\s*\([^;{)]*\)\s*\{")
|
|
164
|
+
_NON_METHOD_NAMES = frozenset({
|
|
165
|
+
"if", "for", "while", "switch", "catch", "synchronized", "try", "return",
|
|
166
|
+
"new", "do", "else", "case", "super", "this",
|
|
167
|
+
})
|
|
168
|
+
|
|
136
169
|
|
|
137
170
|
# ---------------------------------------------------------------------------
|
|
138
171
|
# Data model
|
|
139
172
|
# ---------------------------------------------------------------------------
|
|
140
173
|
|
|
174
|
+
@dataclass
|
|
175
|
+
class RewriteTarget:
|
|
176
|
+
id: str
|
|
177
|
+
layer: str
|
|
178
|
+
source_file: str
|
|
179
|
+
line_start: int
|
|
180
|
+
line_end: int
|
|
181
|
+
current_pattern: str
|
|
182
|
+
current_snippet: str
|
|
183
|
+
target_api: str
|
|
184
|
+
migration_kind: str
|
|
185
|
+
auto_migratable: bool
|
|
186
|
+
blocking_reason: str
|
|
187
|
+
symbol: str
|
|
188
|
+
module: str
|
|
189
|
+
dynamic: bool = False
|
|
190
|
+
|
|
191
|
+
def to_dict(self) -> dict:
|
|
192
|
+
return {
|
|
193
|
+
"id": self.id,
|
|
194
|
+
"layer": self.layer,
|
|
195
|
+
"source_file": self.source_file,
|
|
196
|
+
"line_start": self.line_start,
|
|
197
|
+
"line_end": self.line_end,
|
|
198
|
+
"current_pattern": self.current_pattern,
|
|
199
|
+
"current_snippet": self.current_snippet,
|
|
200
|
+
"target_api": self.target_api,
|
|
201
|
+
"migration_kind": self.migration_kind,
|
|
202
|
+
"auto_migratable": self.auto_migratable,
|
|
203
|
+
"blocking_reason": self.blocking_reason,
|
|
204
|
+
"symbol": self.symbol,
|
|
205
|
+
"module": self.module,
|
|
206
|
+
"dynamic": self.dynamic,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
141
210
|
@dataclass
|
|
142
211
|
class HibernateFinding:
|
|
143
212
|
layer: str
|
|
144
213
|
severity: str
|
|
145
|
-
pattern: str
|
|
214
|
+
pattern: str
|
|
146
215
|
source_file: str
|
|
147
216
|
first_line: int
|
|
148
217
|
module: str
|
|
149
218
|
occurrences: int = 1
|
|
150
|
-
dynamic: bool = False
|
|
219
|
+
dynamic: bool = False
|
|
151
220
|
|
|
152
221
|
def to_dict(self) -> dict:
|
|
153
222
|
return {
|
|
@@ -167,20 +236,44 @@ class HibernateLayerRisk:
|
|
|
167
236
|
layer: str
|
|
168
237
|
risk: str
|
|
169
238
|
reason: str
|
|
170
|
-
estimated_effort: str
|
|
239
|
+
estimated_effort: str # legacy string "~75.8d" (compat)
|
|
171
240
|
file_count: int
|
|
172
241
|
occurrence_count: int
|
|
242
|
+
effort_range: dict = field(default_factory=dict) # {low, high, confidence}
|
|
243
|
+
# Migration-kind breakdown (derived from rewrite_targets of this layer).
|
|
244
|
+
manual_count: int = 0
|
|
245
|
+
assisted_count: int = 0
|
|
246
|
+
mechanical_count: int = 0
|
|
247
|
+
review_count: int = 0
|
|
248
|
+
# Criteria-specific.
|
|
249
|
+
static_count: Optional[int] = None
|
|
250
|
+
dynamic_count: Optional[int] = None
|
|
251
|
+
# SPI-specific.
|
|
252
|
+
userType_rewrite_count: Optional[int] = None
|
|
253
|
+
userType_resolvable_count: Optional[int] = None
|
|
173
254
|
|
|
174
255
|
def to_dict(self) -> dict:
|
|
175
|
-
|
|
256
|
+
d: dict = {
|
|
176
257
|
"layer": self.layer,
|
|
177
258
|
"layer_title": _LAYER_TITLES.get(self.layer, self.layer),
|
|
178
259
|
"risk": self.risk,
|
|
179
260
|
"reason": self.reason,
|
|
180
261
|
"estimated_effort": self.estimated_effort,
|
|
262
|
+
"effort_range": self.effort_range,
|
|
181
263
|
"file_count": self.file_count,
|
|
182
264
|
"occurrence_count": self.occurrence_count,
|
|
265
|
+
"manual_count": self.manual_count,
|
|
266
|
+
"assisted_count": self.assisted_count,
|
|
267
|
+
"mechanical_count": self.mechanical_count,
|
|
268
|
+
"review_count": self.review_count,
|
|
183
269
|
}
|
|
270
|
+
if self.static_count is not None:
|
|
271
|
+
d["static_count"] = self.static_count
|
|
272
|
+
d["dynamic_count"] = self.dynamic_count
|
|
273
|
+
if self.userType_rewrite_count is not None:
|
|
274
|
+
d["userType_rewrite_count"] = self.userType_rewrite_count
|
|
275
|
+
d["userType_resolvable_count"] = self.userType_resolvable_count
|
|
276
|
+
return d
|
|
184
277
|
|
|
185
278
|
|
|
186
279
|
@dataclass
|
|
@@ -188,26 +281,37 @@ class HibernateStratification:
|
|
|
188
281
|
detected: bool = False
|
|
189
282
|
classification: str = CLASS_NONE
|
|
190
283
|
classification_label: str = _CLASS_LABELS[CLASS_NONE]
|
|
284
|
+
readiness: int = 100
|
|
191
285
|
risk_matrix: list[HibernateLayerRisk] = field(default_factory=list)
|
|
192
286
|
module_exposure: dict[str, dict] = field(default_factory=dict)
|
|
193
287
|
incompatible_patterns: list[str] = field(default_factory=list)
|
|
194
288
|
critical_call_chains: list[dict] = field(default_factory=list)
|
|
289
|
+
rewrite_targets: list[RewriteTarget] = field(default_factory=list)
|
|
290
|
+
golden_sql_hotspots: list[dict] = field(default_factory=list)
|
|
195
291
|
stop_conditions_triggered: list[str] = field(default_factory=list)
|
|
196
292
|
observable_risk: list[str] = field(default_factory=list)
|
|
197
293
|
inferred_runtime_risk: list[str] = field(default_factory=list)
|
|
294
|
+
total_effort_range_days: dict = field(default_factory=dict)
|
|
295
|
+
effort_model: dict = field(default_factory=dict)
|
|
198
296
|
findings: list[HibernateFinding] = field(default_factory=list)
|
|
199
297
|
|
|
200
298
|
def to_dict(self) -> dict:
|
|
201
299
|
return {
|
|
300
|
+
"schema_version": HIBERNATE_SCHEMA_VERSION,
|
|
202
301
|
"detected": self.detected,
|
|
203
302
|
"classification": self.classification,
|
|
204
303
|
"classification_label": self.classification_label,
|
|
205
304
|
"stratified": True,
|
|
305
|
+
"hibernate_readiness": self.readiness,
|
|
206
306
|
"risk_matrix": [r.to_dict() for r in self.risk_matrix],
|
|
207
307
|
"module_exposure_map": self.module_exposure,
|
|
208
308
|
"incompatible_patterns": self.incompatible_patterns,
|
|
209
309
|
"critical_call_chains": self.critical_call_chains,
|
|
310
|
+
"rewrite_targets": [t.to_dict() for t in self.rewrite_targets],
|
|
311
|
+
"golden_sql_hotspots": self.golden_sql_hotspots,
|
|
210
312
|
"stop_conditions_triggered": self.stop_conditions_triggered,
|
|
313
|
+
"total_effort_range_days": self.total_effort_range_days,
|
|
314
|
+
"effort_model": self.effort_model,
|
|
211
315
|
"risk_separation": {
|
|
212
316
|
"observable_code_risk": self.observable_risk,
|
|
213
317
|
"inferred_runtime_risk": self.inferred_runtime_risk,
|
|
@@ -222,14 +326,22 @@ class HibernateStratification:
|
|
|
222
326
|
lines: list[str] = [
|
|
223
327
|
"── Hibernate 5.x → 6.x Migration Stratification ──",
|
|
224
328
|
f"Classification: {self.classification_label}",
|
|
329
|
+
f"Hibernate readiness: {self.readiness}/100 "
|
|
330
|
+
f"Rewrite targets: {len(self.rewrite_targets)} "
|
|
331
|
+
f"Effort: {self.total_effort_range_days.get('low', '?')}–"
|
|
332
|
+
f"{self.total_effort_range_days.get('high', '?')}d "
|
|
333
|
+
f"({self.total_effort_range_days.get('confidence', '?')} confidence)",
|
|
225
334
|
"",
|
|
226
335
|
"Hibernate Migration Impact Matrix:",
|
|
227
|
-
f" {'Layer':<30} {'Risk':<9} Effort
|
|
336
|
+
f" {'Layer':<30} {'Risk':<9} {'Effort':<13} Breakdown",
|
|
228
337
|
]
|
|
229
338
|
for r in self.risk_matrix:
|
|
339
|
+
bd = f"manual={r.manual_count} assisted={r.assisted_count} mech={r.mechanical_count}"
|
|
340
|
+
if r.static_count is not None:
|
|
341
|
+
bd += f" (static={r.static_count}/dyn={r.dynamic_count})"
|
|
230
342
|
lines.append(
|
|
231
343
|
f" {_LAYER_TITLES.get(r.layer, r.layer):<30} "
|
|
232
|
-
f"{r.risk.upper():<9} {r.estimated_effort:<13} {
|
|
344
|
+
f"{r.risk.upper():<9} {r.estimated_effort:<13} {bd}"
|
|
233
345
|
)
|
|
234
346
|
|
|
235
347
|
lines.append("")
|
|
@@ -249,8 +361,7 @@ class HibernateStratification:
|
|
|
249
361
|
tag_str = f" [{', '.join(tags)}]" if tags else ""
|
|
250
362
|
layers = ", ".join(sorted(info.get("layers", {}).keys()))
|
|
251
363
|
lines.append(
|
|
252
|
-
f" {mod:<40} {info.get('max_risk', 'low').upper():<9} "
|
|
253
|
-
f"{layers}{tag_str}"
|
|
364
|
+
f" {mod:<40} {info.get('max_risk', 'low').upper():<9} {layers}{tag_str}"
|
|
254
365
|
)
|
|
255
366
|
else:
|
|
256
367
|
lines.append(" (no module attribution)")
|
|
@@ -263,6 +374,24 @@ class HibernateStratification:
|
|
|
263
374
|
f" {cc['class']} ({cc['source_file']}:{cc['first_line']}) — {cc['reason']}"
|
|
264
375
|
)
|
|
265
376
|
|
|
377
|
+
if self.golden_sql_hotspots:
|
|
378
|
+
lines.append("")
|
|
379
|
+
lines.append("Golden-SQL Instrumentation Hotspots (where to pin behaviour tests):")
|
|
380
|
+
for h in self.golden_sql_hotspots:
|
|
381
|
+
lines.append(
|
|
382
|
+
f" {h['symbol']:<35} {h['dynamic_query_count']:>4} dyn-queries "
|
|
383
|
+
f"({h['source_file']})"
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
if self.rewrite_targets:
|
|
387
|
+
lines.append("")
|
|
388
|
+
lines.append(f"Rewrite Targets (first 15 of {len(self.rewrite_targets)}):")
|
|
389
|
+
for t in self.rewrite_targets[:15]:
|
|
390
|
+
lines.append(
|
|
391
|
+
f" [{t.migration_kind}] {t.source_file}:{t.line_start} "
|
|
392
|
+
f"{t.symbol} → {t.target_api}"
|
|
393
|
+
)
|
|
394
|
+
|
|
266
395
|
if self.incompatible_patterns:
|
|
267
396
|
lines.append("")
|
|
268
397
|
lines.append("Incompatible / High-risk Patterns Found:")
|
|
@@ -307,21 +436,150 @@ def _module_of(rel_path: str) -> str:
|
|
|
307
436
|
return "root"
|
|
308
437
|
|
|
309
438
|
|
|
310
|
-
def
|
|
311
|
-
|
|
439
|
+
def _line_index(source: str) -> list[int]:
|
|
440
|
+
"""Offsets of every newline — for O(log n) offset→line lookups via bisect."""
|
|
441
|
+
return [i for i, c in enumerate(source) if c == "\n"]
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _line_at(nl: list[int], pos: int) -> int:
|
|
445
|
+
return bisect.bisect_right(nl, pos) + 1
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def _index_symbols(source: str, nl: list[int]) -> tuple[list[tuple[int, str]], list[tuple[int, str]]]:
|
|
449
|
+
classes = [(_line_at(nl, m.start()), m.group(1)) for m in _CLASS_DECL_RE.finditer(source)]
|
|
450
|
+
methods = [
|
|
451
|
+
(_line_at(nl, m.start()), m.group(1))
|
|
452
|
+
for m in _METHOD_DECL_RE.finditer(source)
|
|
453
|
+
if m.group(1) not in _NON_METHOD_NAMES
|
|
454
|
+
]
|
|
455
|
+
return classes, methods
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def _nearest(decls: list[tuple[int, str]], line: int) -> Optional[str]:
|
|
459
|
+
best: Optional[str] = None
|
|
460
|
+
for dl, name in decls:
|
|
461
|
+
if dl <= line:
|
|
462
|
+
best = name
|
|
463
|
+
else:
|
|
464
|
+
break
|
|
465
|
+
return best
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def _symbol_at(line: int, classes: list[tuple[int, str]], methods: list[tuple[int, str]]) -> str:
|
|
469
|
+
cls = _nearest(classes, line)
|
|
470
|
+
meth = _nearest(methods, line)
|
|
471
|
+
if cls and meth:
|
|
472
|
+
return f"{cls}#{meth}"
|
|
473
|
+
return cls or meth or ""
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def _snippet(lines: list[str], ls: int, le: int) -> str:
|
|
477
|
+
seg = " ".join(l.strip() for l in lines[ls - 1: le])
|
|
478
|
+
seg = re.sub(r"\s+", " ", seg).strip()
|
|
479
|
+
return (seg[:157] + "...") if len(seg) > 160 else seg
|
|
312
480
|
|
|
313
481
|
|
|
314
482
|
def _count(pattern: re.Pattern, source: str) -> int:
|
|
315
483
|
return sum(1 for _ in pattern.finditer(source))
|
|
316
484
|
|
|
317
485
|
|
|
486
|
+
def _make_id(layer: str, source_file: str, line: int, pattern: str) -> str:
|
|
487
|
+
h = hashlib.sha256(f"{source_file}:{line}:{pattern}".encode()).hexdigest()[:10]
|
|
488
|
+
return f"HB6-{_LAYER_CODE.get(layer, 'GEN')}-{h}"
|
|
489
|
+
|
|
490
|
+
|
|
318
491
|
# ---------------------------------------------------------------------------
|
|
319
|
-
#
|
|
492
|
+
# Target-mapping rules (current pattern → Hibernate 6 destination)
|
|
320
493
|
# ---------------------------------------------------------------------------
|
|
321
494
|
|
|
322
|
-
def
|
|
323
|
-
|
|
324
|
-
|
|
495
|
+
def _criteria_target(legacy: bool, dynamic: bool) -> tuple[str, str, bool, str]:
|
|
496
|
+
if legacy:
|
|
497
|
+
return ("jakarta.persistence.criteria.CriteriaBuilder",
|
|
498
|
+
KIND_MANUAL, False,
|
|
499
|
+
"org.hibernate.Criteria / Restrictions / Projections removed in Hibernate 6")
|
|
500
|
+
if dynamic:
|
|
501
|
+
return ("jakarta.persistence.criteria.CriteriaBuilder (dynamic builder rewrite)",
|
|
502
|
+
KIND_MANUAL, False,
|
|
503
|
+
"dynamic Criteria construction via reflection / abstraction DAO")
|
|
504
|
+
return ("jakarta.persistence.criteria (validate Hibernate 6 semantics)",
|
|
505
|
+
KIND_ASSISTED, False,
|
|
506
|
+
"JPA Criteria metamodel/semantics changed in Hibernate 6")
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def _jpa_deprecated_target(text: str) -> tuple[str, str, bool, str]:
|
|
510
|
+
if "@TypeDef" in text:
|
|
511
|
+
return ("@Type(value=...) (Hibernate 6 — @TypeDef removed)",
|
|
512
|
+
KIND_ASSISTED, False, "@TypeDef / @TypeDefs removed in Hibernate 6")
|
|
513
|
+
if "@GenericGenerator" in text:
|
|
514
|
+
return ("@GenericGenerator (Hibernate 6 — strategy/parameters reworked)",
|
|
515
|
+
KIND_ASSISTED, False, "@GenericGenerator reworked in Hibernate 6")
|
|
516
|
+
# @Type(type="...") or org.hibernate.annotations.Type
|
|
517
|
+
return ("@Type(value=...) / @JdbcTypeCode (Hibernate 6)",
|
|
518
|
+
KIND_MECHANICAL, True,
|
|
519
|
+
"@Type(type=) attribute renamed/replaced in Hibernate 6 (1:1 mapping)")
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def _hql_target(concat: bool) -> tuple[str, str, bool, str]:
|
|
523
|
+
if concat:
|
|
524
|
+
return ("HQL (parameterize + validate Hibernate 6 parser)",
|
|
525
|
+
KIND_ASSISTED, False, "runtime-resolved query string (concatenation)")
|
|
526
|
+
return ("HQL / native query (validate against Hibernate 6 parser)",
|
|
527
|
+
KIND_REVIEW, False, "")
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
def _spi_target(text: str) -> tuple[str, str, bool, str]:
|
|
531
|
+
t = text
|
|
532
|
+
if "UserCollectionType" in t:
|
|
533
|
+
return ("org.hibernate.usertype.UserCollectionType (redesigned in H6)",
|
|
534
|
+
KIND_MANUAL, False, "UserCollectionType interface redesigned in Hibernate 6")
|
|
535
|
+
if "CompositeUserType" in t:
|
|
536
|
+
return ("org.hibernate.usertype.CompositeUserType (redesigned in H6)",
|
|
537
|
+
KIND_MANUAL, False, "CompositeUserType redesigned in Hibernate 6")
|
|
538
|
+
if "UserType" in t:
|
|
539
|
+
return ("org.hibernate.usertype.UserType (new method signatures in H6)",
|
|
540
|
+
KIND_MANUAL, False, "UserType interface redesigned in Hibernate 6")
|
|
541
|
+
if "EmptyInterceptor" in t:
|
|
542
|
+
return ("org.hibernate.Interceptor (EmptyInterceptor removed in H6)",
|
|
543
|
+
KIND_MANUAL, False, "EmptyInterceptor removed in Hibernate 6")
|
|
544
|
+
if "Interceptor" in t:
|
|
545
|
+
return ("org.hibernate.Interceptor (default methods in H6)",
|
|
546
|
+
KIND_ASSISTED, False, "Interceptor gained default methods in Hibernate 6")
|
|
547
|
+
if "EventListener" in t:
|
|
548
|
+
return ("Hibernate 6 event SPI",
|
|
549
|
+
KIND_MANUAL, False, "Hibernate event-listener SPI changed in Hibernate 6")
|
|
550
|
+
if "PropertyAccessStrategy" in t:
|
|
551
|
+
return ("org.hibernate.property.access.spi (changed in H6)",
|
|
552
|
+
KIND_MANUAL, False, "PropertyAccess SPI changed in Hibernate 6")
|
|
553
|
+
if "NamingStrategy" in t:
|
|
554
|
+
return ("Hibernate 6 naming-strategy SPI",
|
|
555
|
+
KIND_ASSISTED, False, "Naming strategy SPI adjusted in Hibernate 6")
|
|
556
|
+
if "SessionFactoryImpl" in t or "SessionImplementor" in t or "SharedSessionContractImplementor" in t:
|
|
557
|
+
return ("Hibernate 6 session SPI (SharedSessionContractImplementor)",
|
|
558
|
+
KIND_MANUAL, False, "internal session SPI changed in Hibernate 6")
|
|
559
|
+
return ("Hibernate 6 internal SPI (no stable public equivalent)",
|
|
560
|
+
KIND_MANUAL, False, "internal Hibernate SPI — no direct Hibernate 6 equivalent")
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
# Point-estimate person-days per affected file, per layer/branch — the audit-able
|
|
564
|
+
# basis for effort_model. Ranges derive from these via _CONF_MULT.
|
|
565
|
+
_EFFORT_RATES: dict[str, float] = {
|
|
566
|
+
"jpa_standard": 0.02,
|
|
567
|
+
"jpa_deprecated": 0.25,
|
|
568
|
+
"criteria_static": 0.4,
|
|
569
|
+
"criteria_dynamic": 0.75,
|
|
570
|
+
"hql_static": 0.15,
|
|
571
|
+
"hql_concat": 0.3,
|
|
572
|
+
"spi": 1.0,
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
def _effort_point(file_count: int, rate: float, floor: float = 0.5) -> float:
|
|
577
|
+
return round(max(floor, file_count * rate), 1) if file_count else 0.0
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
def _effort_range(point: float, confidence: str) -> dict:
|
|
581
|
+
lo, hi = _CONF_MULT[confidence]
|
|
582
|
+
return {"low": round(point * lo, 1), "high": round(point * hi, 1), "confidence": confidence}
|
|
325
583
|
|
|
326
584
|
|
|
327
585
|
# ---------------------------------------------------------------------------
|
|
@@ -329,11 +587,12 @@ def _effort_bucket(file_count: int, base_days_per_file: float, floor: float = 0.
|
|
|
329
587
|
# ---------------------------------------------------------------------------
|
|
330
588
|
|
|
331
589
|
def analyze_hibernate(file_paths: list[str], root: Path) -> HibernateStratification:
|
|
332
|
-
"""Scan Java sources for stratified Hibernate 5→6 migration risk."""
|
|
590
|
+
"""Scan Java sources for stratified Hibernate 5→6 migration risk + rewrite targets."""
|
|
333
591
|
findings: list[HibernateFinding] = []
|
|
334
|
-
|
|
592
|
+
targets: list[RewriteTarget] = []
|
|
335
593
|
modules: dict[str, dict] = {}
|
|
336
594
|
call_chains: list[dict] = []
|
|
595
|
+
hotspots: list[dict] = []
|
|
337
596
|
|
|
338
597
|
layer_files: dict[str, set[str]] = {
|
|
339
598
|
LAYER_JPA: set(), LAYER_CRITERIA: set(), LAYER_HQL: set(), LAYER_SPI: set()
|
|
@@ -346,10 +605,9 @@ def analyze_hibernate(file_paths: list[str], root: Path) -> HibernateStratificat
|
|
|
346
605
|
spi_global = False
|
|
347
606
|
reflection_query_global = False
|
|
348
607
|
concat_query_global = False
|
|
349
|
-
|
|
350
608
|
incompatible: set[str] = set()
|
|
351
609
|
|
|
352
|
-
for rel_path in file_paths:
|
|
610
|
+
for rel_path in sorted(file_paths):
|
|
353
611
|
if not rel_path.endswith(".java"):
|
|
354
612
|
continue
|
|
355
613
|
abs_path = root / rel_path
|
|
@@ -358,22 +616,27 @@ def analyze_hibernate(file_paths: list[str], root: Path) -> HibernateStratificat
|
|
|
358
616
|
except OSError:
|
|
359
617
|
continue
|
|
360
618
|
|
|
361
|
-
|
|
619
|
+
nl = _line_index(source)
|
|
620
|
+
src_lines = source.split("\n")
|
|
621
|
+
classes, methods = _index_symbols(source, nl)
|
|
622
|
+
module = _module_of(rel_path)
|
|
362
623
|
has_abstraction = bool(_ABSTRACTION_CLASS_RE.search(source))
|
|
363
624
|
has_reflection = bool(_REFLECTION_RE.search(source))
|
|
364
|
-
|
|
625
|
+
|
|
365
626
|
mod = modules.setdefault(
|
|
366
627
|
module,
|
|
367
628
|
{"layers": {}, "max_risk": "low", "criteria_dynamic": False,
|
|
368
629
|
"has_spi": False, "has_reflection": False, "files": set()},
|
|
369
630
|
)
|
|
631
|
+
if has_reflection:
|
|
632
|
+
mod["has_reflection"] = True
|
|
370
633
|
|
|
371
|
-
def
|
|
372
|
-
|
|
634
|
+
def _emit_finding(layer: str, severity: str, label: str, line: int,
|
|
635
|
+
occ: int, dynamic: bool = False) -> None:
|
|
373
636
|
findings.append(HibernateFinding(
|
|
374
|
-
layer=layer, severity=severity, pattern=
|
|
375
|
-
source_file=rel_path, first_line=
|
|
376
|
-
|
|
637
|
+
layer=layer, severity=severity, pattern=label,
|
|
638
|
+
source_file=rel_path, first_line=line, module=module,
|
|
639
|
+
occurrences=occ, dynamic=dynamic,
|
|
377
640
|
))
|
|
378
641
|
layer_files[layer].add(rel_path)
|
|
379
642
|
layer_occ[layer] += occ
|
|
@@ -385,71 +648,114 @@ def analyze_hibernate(file_paths: list[str], root: Path) -> HibernateStratificat
|
|
|
385
648
|
mod["max_risk"] = severity
|
|
386
649
|
mod["files"].add(rel_path)
|
|
387
650
|
|
|
388
|
-
|
|
389
|
-
|
|
651
|
+
def _emit_target(layer: str, pattern: str, ls: int, le: int,
|
|
652
|
+
target_api: str, kind: str, auto: bool, reason: str,
|
|
653
|
+
dynamic: bool = False) -> None:
|
|
654
|
+
targets.append(RewriteTarget(
|
|
655
|
+
id=_make_id(layer, rel_path, ls, pattern),
|
|
656
|
+
layer=layer, source_file=rel_path, line_start=ls, line_end=le,
|
|
657
|
+
current_pattern=pattern, current_snippet=_snippet(src_lines, ls, le),
|
|
658
|
+
target_api=target_api, migration_kind=kind, auto_migratable=auto,
|
|
659
|
+
blocking_reason=reason, symbol=_symbol_at(ls, classes, methods),
|
|
660
|
+
module=module, dynamic=dynamic,
|
|
661
|
+
))
|
|
390
662
|
|
|
391
663
|
# ── Layer 1: JPA annotations ───────────────────────────────────────
|
|
392
|
-
|
|
393
|
-
if
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
664
|
+
ann_n = _count(_JPA_ANNOTATION_RE, source)
|
|
665
|
+
if ann_n:
|
|
666
|
+
am = _JPA_ANNOTATION_RE.search(source)
|
|
667
|
+
_emit_finding(LAYER_JPA, "low", "JPA mapping annotations",
|
|
668
|
+
_line_at(nl, am.start()), ann_n)
|
|
669
|
+
# Deprecated annotations → escalate + emit rewrite targets (per occurrence).
|
|
670
|
+
dep_matches = list(_JPA_DEPRECATED_RE.finditer(source))
|
|
671
|
+
if dep_matches:
|
|
398
672
|
jpa_deprecated_seen = True
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
673
|
+
first = dep_matches[0]
|
|
674
|
+
_emit_finding(LAYER_JPA, "high",
|
|
675
|
+
"Deprecated Hibernate annotation (@Type/@TypeDef/@GenericGenerator)",
|
|
676
|
+
_line_at(nl, first.start()), len(dep_matches))
|
|
677
|
+
incompatible.add("Deprecated Hibernate annotations (@Type(type=)/@TypeDef) — "
|
|
678
|
+
"reworked in Hibernate 6")
|
|
679
|
+
for m in dep_matches:
|
|
680
|
+
ls = _line_at(nl, m.start())
|
|
681
|
+
api, kind, auto, reason = _jpa_deprecated_target(m.group(0))
|
|
682
|
+
_emit_target(LAYER_JPA, "deprecated-annotation", ls, ls,
|
|
683
|
+
api, kind, auto, reason)
|
|
402
684
|
|
|
403
685
|
# ── Layer 2: Criteria API ──────────────────────────────────────────
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
686
|
+
dynamic = has_abstraction or has_reflection
|
|
687
|
+
crit_matches: list[tuple[re.Match, bool]] = (
|
|
688
|
+
[(m, False) for m in _CRITERIA_JPA_RE.finditer(source)]
|
|
689
|
+
+ [(m, True) for m in _CRITERIA_LEGACY_RE.finditer(source)]
|
|
690
|
+
)
|
|
691
|
+
if crit_matches:
|
|
409
692
|
severity = "critical" if dynamic else "high"
|
|
410
|
-
|
|
693
|
+
first_line = min(_line_at(nl, m.start()) for m, _ in crit_matches)
|
|
411
694
|
label = ("Dynamic Criteria construction (reflection/abstraction DAO)"
|
|
412
695
|
if dynamic else "Criteria API usage")
|
|
413
|
-
|
|
696
|
+
_emit_finding(LAYER_CRITERIA, severity, label, first_line,
|
|
697
|
+
len(crit_matches), dynamic=dynamic)
|
|
414
698
|
if dynamic:
|
|
415
699
|
criteria_dynamic_global = True
|
|
416
700
|
mod["criteria_dynamic"] = True
|
|
417
701
|
incompatible.add("Criteria built dynamically via reflection/abstraction layer "
|
|
418
702
|
"(Hibernate 6 incompatible patterns likely)")
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
703
|
+
for m, legacy in crit_matches:
|
|
704
|
+
ls = _line_at(nl, m.start())
|
|
705
|
+
api, kind, auto, reason = _criteria_target(legacy, dynamic)
|
|
706
|
+
_emit_target(LAYER_CRITERIA,
|
|
707
|
+
"legacy-criteria" if legacy else "jpa-criteria",
|
|
708
|
+
ls, ls, api, kind, auto, reason, dynamic=dynamic)
|
|
709
|
+
if legacy:
|
|
710
|
+
incompatible.add("Legacy org.hibernate.Criteria / Restrictions / Projections "
|
|
711
|
+
"(removed in Hibernate 6 — move to JPA CriteriaBuilder)")
|
|
422
712
|
|
|
423
713
|
# ── Layer 3: HQL / string queries ──────────────────────────────────
|
|
424
|
-
|
|
425
|
-
if
|
|
426
|
-
|
|
427
|
-
|
|
714
|
+
hql_matches = list(_HQL_RE.finditer(source))
|
|
715
|
+
if hql_matches:
|
|
716
|
+
file_has_concat = False
|
|
717
|
+
first_line = _line_at(nl, hql_matches[0].start())
|
|
718
|
+
for m in hql_matches:
|
|
719
|
+
ls = _line_at(nl, m.start())
|
|
720
|
+
window = source[m.start(): m.start() + 400]
|
|
721
|
+
is_concat = bool(_HQL_CONCAT_RE.match("." + window) or _HQL_CONCAT_RE.search(window))
|
|
722
|
+
le = _line_at(nl, m.start() + (window.find(";") if ";" in window else 0))
|
|
723
|
+
le = max(ls, le)
|
|
724
|
+
api, kind, auto, reason = _hql_target(is_concat)
|
|
725
|
+
_emit_target(LAYER_HQL, "concat-query" if is_concat else "static-query",
|
|
726
|
+
ls, le if is_concat else ls, api, kind, auto, reason)
|
|
727
|
+
if is_concat:
|
|
728
|
+
file_has_concat = True
|
|
729
|
+
sev = "high" if file_has_concat else "medium"
|
|
730
|
+
label = ("String-concatenated HQL/native query (dynamic SQL shape)"
|
|
731
|
+
if file_has_concat else "HQL / native string query")
|
|
732
|
+
_emit_finding(LAYER_HQL, sev, label, first_line, len(hql_matches))
|
|
733
|
+
if file_has_concat:
|
|
428
734
|
concat_query_global = True
|
|
429
|
-
_record(LAYER_HQL, "high",
|
|
430
|
-
"String-concatenated HQL/native query (dynamic SQL shape)",
|
|
431
|
-
concat_m, _count(_HQL_RE, source))
|
|
432
735
|
incompatible.add("String-concatenated HQL/native queries — HIGH risk under "
|
|
433
|
-
"Hibernate 6
|
|
434
|
-
else:
|
|
435
|
-
_record(LAYER_HQL, "medium", "HQL / native string query",
|
|
436
|
-
hql_m, _count(_HQL_RE, source))
|
|
736
|
+
"Hibernate 6 HQL parser changes")
|
|
437
737
|
|
|
438
738
|
# ── Layer 4: Hibernate SPI / internal ──────────────────────────────
|
|
439
|
-
|
|
440
|
-
if
|
|
739
|
+
spi_matches = list(_SPI_RE.finditer(source))
|
|
740
|
+
if spi_matches:
|
|
441
741
|
spi_global = True
|
|
442
742
|
mod["has_spi"] = True
|
|
443
|
-
|
|
444
|
-
|
|
743
|
+
first = spi_matches[0]
|
|
744
|
+
_emit_finding(LAYER_SPI, "critical",
|
|
745
|
+
"Hibernate SPI / internal API (UserType/Interceptor/SPI)",
|
|
746
|
+
_line_at(nl, first.start()), len(spi_matches))
|
|
445
747
|
incompatible.add("Custom Hibernate SPI (UserType/CompositeUserType/Interceptor/"
|
|
446
748
|
"EventListener) — primary Hibernate 5→6 breakage source")
|
|
749
|
+
for m in spi_matches:
|
|
750
|
+
ls = _line_at(nl, m.start())
|
|
751
|
+
api, kind, auto, reason = _spi_target(m.group(0))
|
|
752
|
+
_emit_target(LAYER_SPI, "hibernate-spi", ls, ls, api, kind, auto, reason)
|
|
447
753
|
|
|
448
754
|
# ── Critical call-chain detection ──────────────────────────────────
|
|
449
|
-
if has_abstraction and (
|
|
755
|
+
if has_abstraction and (crit_matches or has_reflection):
|
|
450
756
|
am = _ABSTRACTION_CLASS_RE.search(source)
|
|
451
757
|
reason_bits = []
|
|
452
|
-
if
|
|
758
|
+
if crit_matches:
|
|
453
759
|
reason_bits.append("Criteria construction")
|
|
454
760
|
if has_reflection:
|
|
455
761
|
reason_bits.append("reflection-based entity metadata")
|
|
@@ -457,13 +763,30 @@ def analyze_hibernate(file_paths: list[str], root: Path) -> HibernateStratificat
|
|
|
457
763
|
call_chains.append({
|
|
458
764
|
"class": am.group(0),
|
|
459
765
|
"source_file": rel_path,
|
|
460
|
-
"first_line":
|
|
766
|
+
"first_line": _line_at(nl, am.start()),
|
|
461
767
|
"reason": "dynamic query generation: " + " + ".join(reason_bits),
|
|
462
768
|
"module": module,
|
|
463
769
|
})
|
|
770
|
+
# Golden-SQL hotspot: rank by dynamic query volume in this file.
|
|
771
|
+
dyn_q = len(crit_matches) + _count(_REFLECTION_RE, source)
|
|
772
|
+
hotspots.append({
|
|
773
|
+
"symbol": _symbol_at(_line_at(nl, am.start()), classes, methods) or am.group(0),
|
|
774
|
+
"source_file": rel_path,
|
|
775
|
+
"module": module,
|
|
776
|
+
"dynamic_query_count": dyn_q,
|
|
777
|
+
"reflection": has_reflection,
|
|
778
|
+
"reason": "concentrates runtime-generated queries — pin golden-SQL tests here",
|
|
779
|
+
})
|
|
464
780
|
|
|
465
781
|
strat = HibernateStratification()
|
|
466
782
|
strat.findings = findings
|
|
783
|
+
strat.rewrite_targets = sorted(
|
|
784
|
+
targets, key=lambda t: (t.source_file, t.line_start, t.layer, t.id)
|
|
785
|
+
)
|
|
786
|
+
strat.critical_call_chains = sorted(call_chains, key=lambda c: (c["source_file"], c["first_line"]))
|
|
787
|
+
strat.golden_sql_hotspots = sorted(
|
|
788
|
+
hotspots, key=lambda h: (-h["dynamic_query_count"], h["source_file"])
|
|
789
|
+
)[:25]
|
|
467
790
|
|
|
468
791
|
if not findings:
|
|
469
792
|
strat.detected = False
|
|
@@ -472,62 +795,98 @@ def analyze_hibernate(file_paths: list[str], root: Path) -> HibernateStratificat
|
|
|
472
795
|
return strat
|
|
473
796
|
|
|
474
797
|
strat.detected = True
|
|
475
|
-
strat.critical_call_chains = call_chains
|
|
476
798
|
strat.incompatible_patterns = sorted(incompatible)
|
|
477
799
|
|
|
478
|
-
#
|
|
800
|
+
# Per-layer target kind tallies.
|
|
801
|
+
def _kind_counts(layer: str) -> dict[str, int]:
|
|
802
|
+
c = {KIND_MANUAL: 0, KIND_ASSISTED: 0, KIND_MECHANICAL: 0, KIND_REVIEW: 0}
|
|
803
|
+
for t in targets:
|
|
804
|
+
if t.layer == layer:
|
|
805
|
+
c[t.migration_kind] = c.get(t.migration_kind, 0) + 1
|
|
806
|
+
return c
|
|
807
|
+
|
|
479
808
|
matrix: list[HibernateLayerRisk] = []
|
|
809
|
+
point_days: dict[str, float] = {}
|
|
480
810
|
|
|
481
811
|
# Layer 1
|
|
482
812
|
if layer_files[LAYER_JPA]:
|
|
813
|
+
kc = _kind_counts(LAYER_JPA)
|
|
483
814
|
if jpa_deprecated_seen:
|
|
484
|
-
risk,
|
|
485
|
-
|
|
486
|
-
|
|
815
|
+
risk, conf = "high", "medium"
|
|
816
|
+
reason = ("Deprecated Hibernate annotations present "
|
|
817
|
+
"(@Type(type=)/@TypeDef/@GenericGenerator reworked in H6)")
|
|
818
|
+
point = _effort_point(len(layer_files[LAYER_JPA]), _EFFORT_RATES["jpa_deprecated"])
|
|
487
819
|
else:
|
|
488
|
-
risk,
|
|
489
|
-
|
|
490
|
-
|
|
820
|
+
risk, conf = "low", "high"
|
|
821
|
+
reason = ("Standard JPA mapping only — namespace handled by jakarta "
|
|
822
|
+
"migration, no Hibernate-6 annotation breakage")
|
|
823
|
+
point = _effort_point(len(layer_files[LAYER_JPA]), _EFFORT_RATES["jpa_standard"], floor=0.2)
|
|
824
|
+
point_days[LAYER_JPA] = point
|
|
491
825
|
matrix.append(HibernateLayerRisk(
|
|
492
|
-
LAYER_JPA, risk, reason,
|
|
493
|
-
|
|
826
|
+
LAYER_JPA, risk, reason, f"~{point}d", len(layer_files[LAYER_JPA]),
|
|
827
|
+
layer_occ[LAYER_JPA], _effort_range(point, conf),
|
|
828
|
+
manual_count=kc[KIND_MANUAL], assisted_count=kc[KIND_ASSISTED],
|
|
829
|
+
mechanical_count=kc[KIND_MECHANICAL], review_count=kc[KIND_REVIEW]))
|
|
494
830
|
|
|
495
831
|
# Layer 2
|
|
496
832
|
if layer_files[LAYER_CRITERIA]:
|
|
833
|
+
kc = _kind_counts(LAYER_CRITERIA)
|
|
834
|
+
crit_targets = [t for t in targets if t.layer == LAYER_CRITERIA]
|
|
835
|
+
dyn_n = sum(1 for t in crit_targets if t.dynamic)
|
|
836
|
+
stat_n = len(crit_targets) - dyn_n
|
|
497
837
|
if criteria_dynamic_global:
|
|
498
|
-
risk,
|
|
499
|
-
|
|
500
|
-
|
|
838
|
+
risk, conf = "critical", "low"
|
|
839
|
+
reason = ("Criteria constructed dynamically via reflection / abstraction DAOs "
|
|
840
|
+
"— Hibernate 6 rewrite zone")
|
|
841
|
+
point = _effort_point(len(layer_files[LAYER_CRITERIA]), _EFFORT_RATES["criteria_dynamic"])
|
|
501
842
|
else:
|
|
502
|
-
risk,
|
|
503
|
-
|
|
504
|
-
|
|
843
|
+
risk, conf = "high", "medium"
|
|
844
|
+
reason = ("Criteria API in use — JPA Criteria semantics changed; "
|
|
845
|
+
"legacy org.hibernate.Criteria removed in H6")
|
|
846
|
+
point = _effort_point(len(layer_files[LAYER_CRITERIA]), _EFFORT_RATES["criteria_static"])
|
|
847
|
+
point_days[LAYER_CRITERIA] = point
|
|
505
848
|
matrix.append(HibernateLayerRisk(
|
|
506
|
-
LAYER_CRITERIA, risk, reason,
|
|
507
|
-
|
|
849
|
+
LAYER_CRITERIA, risk, reason, f"~{point}d", len(layer_files[LAYER_CRITERIA]),
|
|
850
|
+
layer_occ[LAYER_CRITERIA], _effort_range(point, conf),
|
|
851
|
+
manual_count=kc[KIND_MANUAL], assisted_count=kc[KIND_ASSISTED],
|
|
852
|
+
mechanical_count=kc[KIND_MECHANICAL], review_count=kc[KIND_REVIEW],
|
|
853
|
+
static_count=stat_n, dynamic_count=dyn_n))
|
|
508
854
|
|
|
509
855
|
# Layer 3
|
|
510
856
|
if layer_files[LAYER_HQL]:
|
|
857
|
+
kc = _kind_counts(LAYER_HQL)
|
|
511
858
|
if concat_query_global:
|
|
512
|
-
risk,
|
|
513
|
-
|
|
514
|
-
|
|
859
|
+
risk, conf = "high", "medium"
|
|
860
|
+
reason = ("String-concatenated / dynamic queries — HIGH risk under "
|
|
861
|
+
"Hibernate 6 HQL parser changes")
|
|
862
|
+
point = _effort_point(len(layer_files[LAYER_HQL]), _EFFORT_RATES["hql_concat"])
|
|
515
863
|
else:
|
|
516
|
-
risk,
|
|
517
|
-
|
|
518
|
-
|
|
864
|
+
risk, conf = "medium", "high"
|
|
865
|
+
reason = ("Static HQL / native queries — revalidate against Hibernate 6 "
|
|
866
|
+
"parser; mostly mechanical")
|
|
867
|
+
point = _effort_point(len(layer_files[LAYER_HQL]), _EFFORT_RATES["hql_static"])
|
|
868
|
+
point_days[LAYER_HQL] = point
|
|
519
869
|
matrix.append(HibernateLayerRisk(
|
|
520
|
-
LAYER_HQL, risk, reason,
|
|
521
|
-
|
|
870
|
+
LAYER_HQL, risk, reason, f"~{point}d", len(layer_files[LAYER_HQL]),
|
|
871
|
+
layer_occ[LAYER_HQL], _effort_range(point, conf),
|
|
872
|
+
manual_count=kc[KIND_MANUAL], assisted_count=kc[KIND_ASSISTED],
|
|
873
|
+
mechanical_count=kc[KIND_MECHANICAL], review_count=kc[KIND_REVIEW]))
|
|
522
874
|
|
|
523
875
|
# Layer 4
|
|
524
876
|
if layer_files[LAYER_SPI]:
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
877
|
+
kc = _kind_counts(LAYER_SPI)
|
|
878
|
+
point = _effort_point(len(layer_files[LAYER_SPI]), _EFFORT_RATES["spi"])
|
|
879
|
+
point_days[LAYER_SPI] = point
|
|
528
880
|
matrix.append(HibernateLayerRisk(
|
|
529
|
-
LAYER_SPI,
|
|
530
|
-
|
|
881
|
+
LAYER_SPI, "critical",
|
|
882
|
+
"Custom Hibernate SPI/internal API (UserType, Interceptor, EventListener, "
|
|
883
|
+
"engine.spi) — primary H5→H6 breakage",
|
|
884
|
+
f"~{point}d", len(layer_files[LAYER_SPI]), layer_occ[LAYER_SPI],
|
|
885
|
+
_effort_range(point, "low"),
|
|
886
|
+
manual_count=kc[KIND_MANUAL], assisted_count=kc[KIND_ASSISTED],
|
|
887
|
+
mechanical_count=kc[KIND_MECHANICAL], review_count=kc[KIND_REVIEW],
|
|
888
|
+
userType_rewrite_count=kc[KIND_MANUAL],
|
|
889
|
+
userType_resolvable_count=kc[KIND_ASSISTED]))
|
|
531
890
|
|
|
532
891
|
strat.risk_matrix = matrix
|
|
533
892
|
|
|
@@ -547,6 +906,31 @@ def analyze_hibernate(file_paths: list[str], root: Path) -> HibernateStratificat
|
|
|
547
906
|
}
|
|
548
907
|
strat.module_exposure = exposure
|
|
549
908
|
|
|
909
|
+
# ── Effort aggregation (honest: upper bound, layers may share files) ────
|
|
910
|
+
confidences = [r.effort_range.get("confidence", "low") for r in matrix]
|
|
911
|
+
worst_conf = "low" if "low" in confidences else ("medium" if "medium" in confidences else "high")
|
|
912
|
+
strat.total_effort_range_days = {
|
|
913
|
+
"low": round(sum(r.effort_range.get("low", 0) for r in matrix), 1),
|
|
914
|
+
"high": round(sum(r.effort_range.get("high", 0) for r in matrix), 1),
|
|
915
|
+
"confidence": worst_conf,
|
|
916
|
+
}
|
|
917
|
+
strat.effort_model = {
|
|
918
|
+
"unit": "person-days",
|
|
919
|
+
"point_days_per_file": dict(_EFFORT_RATES),
|
|
920
|
+
"range_multipliers_by_confidence": {k: list(v) for k, v in _CONF_MULT.items()},
|
|
921
|
+
"range_method": "low = point × low_mult, high = point × high_mult (per confidence band)",
|
|
922
|
+
"caveat": ("Per-layer efforts may share files; total_effort_range_days is an upper "
|
|
923
|
+
"bound and is NOT deduplicated across layers."),
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
# ── hibernate_readiness (0-100) ────────────────────────────────────────
|
|
927
|
+
crit_f = {f.source_file for f in findings if f.severity == "critical"}
|
|
928
|
+
high_f = {f.source_file for f in findings if f.severity == "high"}
|
|
929
|
+
med_f = {f.source_file for f in findings if f.severity == "medium"}
|
|
930
|
+
low_f = {f.source_file for f in findings if f.severity == "low"}
|
|
931
|
+
deduction = len(crit_f) * 15 + len(high_f) * 8 + len(med_f) * 3 + min(len(low_f), 15)
|
|
932
|
+
strat.readiness = max(0, 100 - deduction)
|
|
933
|
+
|
|
550
934
|
# ── Stop-condition logic → upgrade vs rewrite classification ────────────
|
|
551
935
|
stops: list[str] = []
|
|
552
936
|
if criteria_dynamic_global:
|
|
@@ -565,8 +949,6 @@ def analyze_hibernate(file_paths: list[str], root: Path) -> HibernateStratificat
|
|
|
565
949
|
strat.classification = CLASS_REWRITE
|
|
566
950
|
elif layer_files[LAYER_CRITERIA] or concat_query_global or jpa_deprecated_seen:
|
|
567
951
|
strat.classification = CLASS_UPGRADE_CARE
|
|
568
|
-
elif layer_files[LAYER_HQL] or layer_files[LAYER_JPA]:
|
|
569
|
-
strat.classification = CLASS_UPGRADE
|
|
570
952
|
else:
|
|
571
953
|
strat.classification = CLASS_UPGRADE
|
|
572
954
|
strat.classification_label = _CLASS_LABELS[strat.classification]
|
|
@@ -582,7 +964,6 @@ def analyze_hibernate(file_paths: list[str], root: Path) -> HibernateStratificat
|
|
|
582
964
|
observable.append(f"HQL/native query calls in {len(layer_files[LAYER_HQL])} file(s)")
|
|
583
965
|
if layer_files[LAYER_SPI]:
|
|
584
966
|
observable.append(f"Hibernate SPI/internal API in {len(layer_files[LAYER_SPI])} file(s)")
|
|
585
|
-
|
|
586
967
|
if criteria_dynamic_global or reflection_query_global:
|
|
587
968
|
inferred.append("Runtime-generated query shapes (reflection/metadata) — exact SQL "
|
|
588
969
|
"cannot be statically enumerated; breakage surface is larger than "
|
sourcecode/mcp/server.py
CHANGED
|
@@ -792,7 +792,11 @@ def get_migration_readiness(repo_path: str = ".", min_severity: str = "low") ->
|
|
|
792
792
|
|
|
793
793
|
Maps to: sourcecode migrate-check <repo_path> --min-severity <min_severity>
|
|
794
794
|
Returns: MigrationReport with schema_version, readiness_score (0–100; 100=ready to migrate),
|
|
795
|
-
jakarta_readiness / boot3_readiness / jdk_modernization
|
|
795
|
+
jakarta_readiness / boot3_readiness / jdk_modernization / hibernate_readiness
|
|
796
|
+
(per-dimension 0–100), headline_blocker (e.g. "hibernate_rewrite" or null),
|
|
797
|
+
hibernate (Hibernate 5→6 stratified model: 4-layer risk_matrix, rewrite_targets[]
|
|
798
|
+
with line-level call sites + target_api + migration_kind, module_exposure_map,
|
|
799
|
+
golden_sql_hotspots, total_effort_range_days, classification upgrade/rewrite),
|
|
796
800
|
blocking_count, estimated_effort_days,
|
|
797
801
|
spring_boot_2_detected (true|false|null — null=undetermined, never assumed true),
|
|
798
802
|
spring_boot_version_detected,
|
sourcecode/migrate_check.py
CHANGED
|
@@ -1055,6 +1055,12 @@ class MigrationReport:
|
|
|
1055
1055
|
jakarta_readiness: int = 100
|
|
1056
1056
|
boot3_readiness: int = 100
|
|
1057
1057
|
jdk_modernization: int = 100
|
|
1058
|
+
# 4th dimension: Hibernate 5→6 rewrite readiness (independent of jakarta/Boot3).
|
|
1059
|
+
# 100 when no Hibernate usage; sinks toward 0 in a rewrite zone.
|
|
1060
|
+
hibernate_readiness: int = 100
|
|
1061
|
+
# Names the dominant blocker class when one dimension dwarfs the headline
|
|
1062
|
+
# score (e.g. "hibernate_rewrite") so a reader of readiness_score is not misled.
|
|
1063
|
+
headline_blocker: Optional[str] = None
|
|
1058
1064
|
blocking_count: int = 0
|
|
1059
1065
|
estimated_effort_days: float = 0.0
|
|
1060
1066
|
# Tri-state: True = Boot 2 confirmed, False = Boot 3+ confirmed,
|
|
@@ -1133,6 +1139,14 @@ class MigrationReport:
|
|
|
1133
1139
|
self.boot3_readiness = _dimension_score(self.findings, _BOOT3_MIGRATION_TARGETS)
|
|
1134
1140
|
self.jdk_modernization = _dimension_score(self.findings, None)
|
|
1135
1141
|
|
|
1142
|
+
# Hibernate is its own rewrite axis (orthogonal to jakarta/Boot3); it does
|
|
1143
|
+
# NOT sink the headline readiness_score, but is surfaced as a dimension and,
|
|
1144
|
+
# in a rewrite zone, as the headline_blocker so 62/100 is not read as "easy".
|
|
1145
|
+
if self.hibernate is not None and self.hibernate.detected:
|
|
1146
|
+
self.hibernate_readiness = self.hibernate.readiness
|
|
1147
|
+
if self.hibernate.classification == "rewrite_zone":
|
|
1148
|
+
self.headline_blocker = "hibernate_rewrite"
|
|
1149
|
+
|
|
1136
1150
|
self.estimated_effort_days = round(
|
|
1137
1151
|
len(critical_files) * 0.5
|
|
1138
1152
|
+ len(high_files) * 0.25
|
|
@@ -1160,6 +1174,8 @@ class MigrationReport:
|
|
|
1160
1174
|
"jakarta_readiness": self.jakarta_readiness,
|
|
1161
1175
|
"boot3_readiness": self.boot3_readiness,
|
|
1162
1176
|
"jdk_modernization": self.jdk_modernization,
|
|
1177
|
+
"hibernate_readiness": self.hibernate_readiness,
|
|
1178
|
+
"headline_blocker": self.headline_blocker,
|
|
1163
1179
|
"blocking_count": self.blocking_count,
|
|
1164
1180
|
"estimated_effort_days": self.estimated_effort_days,
|
|
1165
1181
|
"spring_boot_2_detected": self.spring_boot_2_detected,
|
|
@@ -1185,7 +1201,11 @@ class MigrationReport:
|
|
|
1185
1201
|
f"Migration Readiness: {self.readiness_score}/100",
|
|
1186
1202
|
f" jakarta: {self.jakarta_readiness}/100 "
|
|
1187
1203
|
f"boot3: {self.boot3_readiness}/100 "
|
|
1188
|
-
f"jdk-modernization: {self.jdk_modernization}/100"
|
|
1204
|
+
f"jdk-modernization: {self.jdk_modernization}/100 "
|
|
1205
|
+
f"hibernate: {self.hibernate_readiness}/100",
|
|
1206
|
+
*([f" ⚠ Headline blocker: {self.headline_blocker} "
|
|
1207
|
+
f"(readiness_score reflects jakarta/Boot3 only — Hibernate is a separate rewrite axis)"]
|
|
1208
|
+
if self.headline_blocker else []),
|
|
1189
1209
|
f"Spring Boot 2 detected: {_boot}",
|
|
1190
1210
|
f"Blocking issues: {self.blocking_count} "
|
|
1191
1211
|
f"(critical: {self.summary.get('by_severity', {}).get('critical', 0)}, "
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.63.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
|
|
@@ -657,6 +657,58 @@ Detects migration blockers across Java source files, Spring XML config files, an
|
|
|
657
657
|
|
|
658
658
|
Each finding includes `severity`, `title`, `source_file`, `first_line`, `explanation`, `fix_hint`, `migration_target`, and `openrewrite_recipe` (when an automated recipe exists).
|
|
659
659
|
|
|
660
|
+
#### Hibernate 5.x → 6.x stratification (the `hibernate` output section)
|
|
661
|
+
|
|
662
|
+
A Hibernate major upgrade is **not** a single dependency bump for systems that use
|
|
663
|
+
dynamic persistence. `migrate-check` stratifies Hibernate exposure into four
|
|
664
|
+
independent migration domains — never one aggregated score — and emits **actionable,
|
|
665
|
+
machine-readable rewrite targets** so a migration agent can consume the output
|
|
666
|
+
directly instead of re-parsing the repo. Sub-`schema_version`: `2.0`.
|
|
667
|
+
|
|
668
|
+
**Four layers** (each on its own risk axis):
|
|
669
|
+
|
|
670
|
+
| Layer | Baseline | Escalates to |
|
|
671
|
+
|-------|----------|--------------|
|
|
672
|
+
| `jpa_annotations` | LOW (namespace handled by jakarta) | HIGH on deprecated `@Type(type=)` / `@TypeDef` / `@GenericGenerator` |
|
|
673
|
+
| `criteria_api` | HIGH (JPA Criteria semantics changed; legacy `org.hibernate.Criteria` removed) | **CRITICAL** when built via reflection / abstraction DAOs (`DynamicEntityDao`, `GenericDao`, `BasicPersistenceModule`) |
|
|
674
|
+
| `hql_string_queries` | MEDIUM (revalidate against H6 parser) | HIGH on string concatenation (SQL shape not statically inferable) |
|
|
675
|
+
| `hibernate_spi_internal` | CRITICAL blocker | `UserType`, `CompositeUserType`, `Interceptor`, `EventListener`, `org.hibernate.engine.spi` |
|
|
676
|
+
|
|
677
|
+
**Output keys (under `hibernate`):**
|
|
678
|
+
|
|
679
|
+
- `classification` — `upgrade_zone` / `upgrade_with_care` / `rewrite_zone`. Any of
|
|
680
|
+
{dynamic Criteria, custom SPI, reflection-built queries, concatenated query
|
|
681
|
+
strings} forces **`rewrite_zone`** ("HIGH RISK REWRITE ZONE, NOT UPGRADE ZONE").
|
|
682
|
+
- `risk_matrix[]` — per layer: `risk`, `reason`, `effort_range {low, high, confidence}`,
|
|
683
|
+
`file_count`, `occurrence_count`, migration-kind sub-counts (`manual_count`,
|
|
684
|
+
`assisted_count`, `mechanical_count`, `review_count`); Criteria adds
|
|
685
|
+
`static_count` vs `dynamic_count`; SPI adds `userType_rewrite_count` vs
|
|
686
|
+
`userType_resolvable_count`.
|
|
687
|
+
- `rewrite_targets[]` — one actionable target per call site: `id`, `layer`,
|
|
688
|
+
`source_file`, `line_start`/`line_end`, `current_pattern`, `current_snippet`,
|
|
689
|
+
`target_api` (the Hibernate-6 destination), `migration_kind`
|
|
690
|
+
(`manual_rewrite` / `assisted` / `mechanical` / `review`), `auto_migratable`,
|
|
691
|
+
`blocking_reason`, `symbol` (enclosing `Class#method`), `module`, `dynamic`.
|
|
692
|
+
- `module_exposure_map` — per Maven/Gradle module: `max_risk`, layers present, and
|
|
693
|
+
`dynamic-criteria` / `custom-SPI` / `reflection` tags.
|
|
694
|
+
- `critical_call_chains[]` — dynamic query-generation paths (reflection-based DAOs).
|
|
695
|
+
- `golden_sql_hotspots[]` — classes/methods ranked by dynamic-query volume — where
|
|
696
|
+
to pin golden-SQL behaviour tests before migrating.
|
|
697
|
+
- `total_effort_range_days` + `effort_model` — aggregate range plus the auditable
|
|
698
|
+
formula (and the caveat that layers may share files, so the total is an upper bound).
|
|
699
|
+
- `stop_conditions_triggered[]`, `risk_separation` (observable vs inferred runtime risk).
|
|
700
|
+
|
|
701
|
+
The report also exposes **`hibernate_readiness`** (0–100) as a fourth readiness
|
|
702
|
+
dimension alongside `jakarta_readiness` / `boot3_readiness` / `jdk_modernization`.
|
|
703
|
+
Hibernate is an orthogonal rewrite axis, so it does not sink the headline
|
|
704
|
+
`readiness_score`; instead, in a rewrite zone the top-level `headline_blocker` is set
|
|
705
|
+
to `"hibernate_rewrite"` so a reader of the headline score is not misled.
|
|
706
|
+
|
|
707
|
+
```bash
|
|
708
|
+
# inspect only the Hibernate rewrite targets
|
|
709
|
+
sourcecode migrate-check . --format json | jq '.hibernate.rewrite_targets[]'
|
|
710
|
+
```
|
|
711
|
+
|
|
660
712
|
### `rename-class` — Java class rename
|
|
661
713
|
|
|
662
714
|
```bash
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sourcecode/__init__.py,sha256=
|
|
1
|
+
sourcecode/__init__.py,sha256=7g_-jxyy4w0b0JRdZtxAZ89mML035ZUubdqOo6hl_u0,103
|
|
2
2
|
sourcecode/adaptive_scanner.py,sha256=XffluXKzJUXrMtjEiAOnSNPZnztdIcts17T9ouHeID0,10521
|
|
3
3
|
sourcecode/architecture_analyzer.py,sha256=liCwQmLgb5vplohy8arjYxs_HOIv5C9MjLh_gY6bc5Q,44115
|
|
4
4
|
sourcecode/architecture_summary.py,sha256=z34_6v7cSwy98cof2UVciGho7SCrZ93tiqMmq5WNzRQ,20405
|
|
@@ -7,7 +7,7 @@ sourcecode/cache.py,sha256=1V3vsaODAa2UBJAC0xpvxpmRdriCezQx5Q8JCcfgziE,31892
|
|
|
7
7
|
sourcecode/canonical_ir.py,sha256=DEwucOPJguLsVtg5cV8mWXNi112l5jmBhv73KGGebVk,24849
|
|
8
8
|
sourcecode/cir_graphs.py,sha256=9G0HHj1kw2325IDyzo2OpX73BNswEckecf4MZUXB4JM,12078
|
|
9
9
|
sourcecode/classifier.py,sha256=hKzg-nQ47htqqIUzSGvYxv15cXrA3KgICTwJmdqal0o,8095
|
|
10
|
-
sourcecode/cli.py,sha256=
|
|
10
|
+
sourcecode/cli.py,sha256=PwLLelj1ACLkWQBCEThD6vhn4sGMn5kEg-6gRX1IAb8,276536
|
|
11
11
|
sourcecode/code_notes_analyzer.py,sha256=EJemNCNc9Dn-1RZYu-aNbK0ELzmsyC4s6FdHi3XyNEI,9392
|
|
12
12
|
sourcecode/confidence_analyzer.py,sha256=_jckZSxksV-OU38vbkxfVNBnWCtlCq8Vwfg23x1uspA,19054
|
|
13
13
|
sourcecode/context_scorer.py,sha256=QpChSpsmaAYz91rXA4Ue5xzQmNz_ZboZN09YOHScq1U,14679
|
|
@@ -28,12 +28,12 @@ sourcecode/format_contract.py,sha256=1cTNqwP8geA2hbQoBHUPgX3_vSh3l8guJT_jmgEnFF8
|
|
|
28
28
|
sourcecode/fqn_utils.py,sha256=XLU7zDkNBXz_RZkIUNfpPmp1nekWtqP-fxV92tDV1vg,2158
|
|
29
29
|
sourcecode/git_analyzer.py,sha256=JStxTQXNjBWi_wLdwhsZs9mT-v50cSJIz4Agzn6Kh9I,13362
|
|
30
30
|
sourcecode/graph_analyzer.py,sha256=DHR8fY69oU_Pi4SYaWboX6EoEFrctQKB9dsjpqwGMzw,62403
|
|
31
|
-
sourcecode/hibernate_strat.py,sha256=
|
|
31
|
+
sourcecode/hibernate_strat.py,sha256=Xl2gs_uIpjjq4fHtuibi6wGwpeEC-iKYmDSNYC5M6yM,44143
|
|
32
32
|
sourcecode/integration_detector.py,sha256=ZJqrGwvZ4ee2JTGhlazKk67aZi173HxkhNpl8Yntpd8,6503
|
|
33
33
|
sourcecode/license.py,sha256=wckiLuiwaE35KMCStUf1gYzleJuFe6qsSyxUQJvit3s,23500
|
|
34
34
|
sourcecode/mcp_nudge.py,sha256=5ELU_ixzh6uA83NXLOZT8h00OhL53okfQdji3jyKOjg,2917
|
|
35
35
|
sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7c,22750
|
|
36
|
-
sourcecode/migrate_check.py,sha256=
|
|
36
|
+
sourcecode/migrate_check.py,sha256=Er4pr_f3_eUS4A5G87hxnPMcQee2hqV9-XaS_sdkdu4,66470
|
|
37
37
|
sourcecode/openapi_surface.py,sha256=BTt0K-woZbkbWTN77IkqeBm_Okag9owR0848fmot8sk,16207
|
|
38
38
|
sourcecode/output_budget.py,sha256=Js9yUlfQtPhqBl9R6wn_9UHVjjJc3GtLcqyfjf5t50Q,9869
|
|
39
39
|
sourcecode/path_filters.py,sha256=EN1RGZRvLq5EcPgpjYV_IyCKVlAQQn2bbpEisQ5LpGg,3780
|
|
@@ -91,7 +91,7 @@ sourcecode/mcp/__init__.py,sha256=XU4HfRGbdid8wdUA0x_4f7uKZD1z3mv_XUY_WU_T9Mw,17
|
|
|
91
91
|
sourcecode/mcp/orchestrator.py,sha256=kT7IssYNyQXVtDf2Q69qrMSTuyJlJ1Rhkp6_EHqc_38,36520
|
|
92
92
|
sourcecode/mcp/registry.py,sha256=8ot8A9_ccPNUvHgFkUO8lIlbp4XT_UcHR101nc71Gjc,65840
|
|
93
93
|
sourcecode/mcp/runner.py,sha256=-Dp2qPGRkfNTVen6bKh7WtzQqpcEtsrXoiuajvshlKk,2866
|
|
94
|
-
sourcecode/mcp/server.py,sha256=
|
|
94
|
+
sourcecode/mcp/server.py,sha256=DV7aiUObBGekShSDc7Iu67ISdesByutto3JOFbcYBFk,63693
|
|
95
95
|
sourcecode/mcp/onboarding/__init__.py,sha256=sj2PWqEBmMc4zBNkomg89WtL0M6S7A9yb7_wAuSWNP4,66
|
|
96
96
|
sourcecode/mcp/onboarding/applier.py,sha256=B9CneieWTpaDSDIyW3S5nrlRlBpvfqUcgi93-mm_ApQ,2135
|
|
97
97
|
sourcecode/mcp/onboarding/backup.py,sha256=ihqGOR8QTX8HASRSEDyfFyXr5bkXrygPHamv4p9KTmk,1452
|
|
@@ -103,8 +103,8 @@ sourcecode/telemetry/consent.py,sha256=H5z2Wu63pZqbaKucRPoJQJ0zCo4cke9ZlBrJC-MaW
|
|
|
103
103
|
sourcecode/telemetry/events.py,sha256=LtzYfaX9Ilckj5PTvAcTpDa9mLqDsYPDUiDkRa58piY,2580
|
|
104
104
|
sourcecode/telemetry/filters.py,sha256=NHa5T-6DaZduQPFuC34jOqHWQgSizM-Ygq8aZ4j19ng,5834
|
|
105
105
|
sourcecode/telemetry/transport.py,sha256=4gGHsq0WeY9VywEZXA3vUxykfiYnw9uuqfjAAec7F8o,1681
|
|
106
|
-
sourcecode-1.
|
|
107
|
-
sourcecode-1.
|
|
108
|
-
sourcecode-1.
|
|
109
|
-
sourcecode-1.
|
|
110
|
-
sourcecode-1.
|
|
106
|
+
sourcecode-1.63.0.dist-info/METADATA,sha256=P3a_x2U773iEAxs6a79XDIL1XT4cyAGfY5Y7KKmUQ_I,42339
|
|
107
|
+
sourcecode-1.63.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
108
|
+
sourcecode-1.63.0.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
|
|
109
|
+
sourcecode-1.63.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
|
|
110
|
+
sourcecode-1.63.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|