sourcecode 1.50.0__py3-none-any.whl → 1.52.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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "1.50.0"
3
+ __version__ = "1.52.0"
@@ -536,6 +536,11 @@ _ALL_RULES: list[_Rule] = (
536
536
 
537
537
  SEVERITY_ORDER: dict[str, int] = {"critical": 0, "high": 1, "medium": 2, "low": 3}
538
538
 
539
+ # G-1: cap on total readiness deduction from low-severity (advisory, non-blocking)
540
+ # findings, so optional modernization cleanups cannot collapse the migration-readiness
541
+ # headline on a repo with zero blockers. See MigrationReport.finalize.
542
+ _LOW_SEVERITY_DEDUCTION_CAP: int = 15
543
+
539
544
 
540
545
  # ---------------------------------------------------------------------------
541
546
  # XML config rules (applied to Spring XML config files)
@@ -1022,11 +1027,17 @@ class MigrationReport:
1022
1027
  else:
1023
1028
  low_files.add(f.source_file)
1024
1029
 
1030
+ # G-1: low-severity findings are advisory (e.g. java.time modernization),
1031
+ # not Boot-2→3 blockers. Cap their total contribution so a repo with zero
1032
+ # blockers cannot score near zero on optional cleanups alone (mall: 96 low
1033
+ # findings dragged readiness to 4/100 despite 0 blocking, already on Boot 3).
1034
+ # Blockers (critical/high) stay uncapped — a genuinely blocked repo still
1035
+ # floors at 0 (shopizer: 301 blocking → 0/100, unchanged).
1025
1036
  deduction = (
1026
1037
  len(critical_files) * 15
1027
1038
  + len(high_files) * 8
1028
1039
  + len(medium_files) * 3
1029
- + len(low_files) * 1
1040
+ + min(len(low_files) * 1, _LOW_SEVERITY_DEDUCTION_CAP)
1030
1041
  )
1031
1042
  self.readiness_score = max(0, 100 - deduction)
1032
1043
 
@@ -1514,6 +1514,34 @@ def _build_relations(
1514
1514
  evidence={"type": "method_call", "value": f"new {_tgt.split('.')[-1]}(...)"},
1515
1515
  ))
1516
1516
 
1517
+ # ── Static-utility calls: `Type.method(...)` edges (G-2 / static-call gap) ──
1518
+ # A static call `AnnotationHelper.foo(...)` couples the calling class to the
1519
+ # utility type, but the call/DI graph misses it: only an `imports` edge is
1520
+ # recorded (and impact-chain skips imports), so a static helper showed 0
1521
+ # callers and impact-chain reported a false-confident "no blast radius".
1522
+ # Mirror the instantiation scan: regex over comment-stripped source, resolve the
1523
+ # receiver type via the import map (so JDK/unresolved receivers like Math/LOGGER
1524
+ # yield None and are skipped), attribute at class level. Unlike `instantiates`
1525
+ # this INCLUDES controllers — a controller statically calling a utility is a real
1526
+ # caller and has no `returns`-edge overlap. Emitted as `calls` (traversed by the
1527
+ # caller BFS). Instance calls go through lower-case variables and never match.
1528
+ if _class_syms:
1529
+ _call_targets: set[str] = set()
1530
+ for _m in re.finditer(r'\b([A-Z]\w*)\.\w+\s*\(', _source_no_comments):
1531
+ _ct_fqn = _resolve_dep_type(_m.group(1))
1532
+ if _ct_fqn:
1533
+ _call_targets.add(_ct_fqn)
1534
+ for cls_sym in _class_syms:
1535
+ for _tgt in sorted(_call_targets):
1536
+ if _tgt != cls_sym.symbol:
1537
+ edges.append(RelationEdge(
1538
+ from_symbol=cls_sym.symbol,
1539
+ to_symbol=_tgt,
1540
+ type="calls",
1541
+ confidence="medium",
1542
+ evidence={"type": "method_call", "value": f"{_tgt.split('.')[-1]}.…(…)"},
1543
+ ))
1544
+
1517
1545
  seen: set[tuple[str, str, str]] = set()
1518
1546
  unique: list[RelationEdge] = []
1519
1547
  for e in edges:
@@ -872,10 +872,35 @@ class ImpactOrchestrator:
872
872
  "An empty result is NOT proof the type is unused."
873
873
  )
874
874
 
875
+ # G-2 residual guard: something imports this symbol but no call/DI/instantiation
876
+ # edge resolved to it. With static-call edges now extracted, the common static
877
+ # utility case is covered; this catches what remains (static imports invoked
878
+ # without a qualifier, reflection, method references) — usages the call-graph
879
+ # cannot bind. An empty blast radius here is NOT proof of dead code, so it must
880
+ # not be reported as a high-confidence "safe to change".
881
+ unresolved_ref_blind_spot = False
882
+ if (
883
+ empty_blast
884
+ and class_level_seed
885
+ and not framework_di_blind_spot
886
+ and not value_type_blind_spot
887
+ ):
888
+ _rev = cir.reverse_graph.get(resolved_symbol) or {}
889
+ _importers = sorted(set(_rev.get("imports") or []))
890
+ if _importers:
891
+ unresolved_ref_blind_spot = True
892
+ warnings.append(
893
+ f"Unresolved inbound references (G-2): {len(_importers)} in-repo "
894
+ "file(s) import this symbol but no call/DI/instantiation edge "
895
+ "resolves to it — the usage may be a static import, reflection, or "
896
+ "method reference the call-graph does not model. 0 callers is NOT "
897
+ "proof this symbol is unused."
898
+ )
899
+
875
900
  confidence: str
876
901
  if resolution == "not_found":
877
902
  confidence = "low"
878
- elif framework_di_blind_spot or value_type_blind_spot:
903
+ elif framework_di_blind_spot or value_type_blind_spot or unresolved_ref_blind_spot:
879
904
  confidence = "low"
880
905
  elif resolution == "partial" or confidence_reducing:
881
906
  confidence = "medium"
@@ -908,6 +933,7 @@ class ImpactOrchestrator:
908
933
  "blind_spots": (
909
934
  (["framework_di"] if framework_di_blind_spot else [])
910
935
  + (["value_type"] if value_type_blind_spot else [])
936
+ + (["unresolved_refs"] if unresolved_ref_blind_spot else [])
911
937
  ),
912
938
  "external_supertypes": external_supertypes,
913
939
  },
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.50.0
3
+ Version: 1.52.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
- ![Version](https://img.shields.io/badge/version-1.50.0-blue)
43
+ ![Version](https://img.shields.io/badge/version-1.52.0-blue)
44
44
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
45
45
 
46
46
  ---
@@ -114,7 +114,7 @@ pipx install sourcecode
114
114
 
115
115
  ```bash
116
116
  sourcecode version
117
- # sourcecode 1.44.0
117
+ # sourcecode 1.52.0
118
118
  ```
119
119
 
120
120
  ---
@@ -1,4 +1,4 @@
1
- sourcecode/__init__.py,sha256=pCkOXPgXyXxjAbBGF1i4NrzrWef2vm102RXTjjOE8go,103
1
+ sourcecode/__init__.py,sha256=8XptYH2PiICP1nTdw2tTbKDEi4obFtDz4W6P8xYYcOw,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
@@ -31,7 +31,7 @@ sourcecode/graph_analyzer.py,sha256=DHR8fY69oU_Pi4SYaWboX6EoEFrctQKB9dsjpqwGMzw,
31
31
  sourcecode/license.py,sha256=i_X1bYdobL_z9OVuLiycnWEFSaaNhcKKuTd6G55U3_k,20747
32
32
  sourcecode/mcp_nudge.py,sha256=5ELU_ixzh6uA83NXLOZT8h00OhL53okfQdji3jyKOjg,2917
33
33
  sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7c,22750
34
- sourcecode/migrate_check.py,sha256=vowVIAxVaHU8vhZUEt-HrWrWM38m6a5INHJQGjEg5E0,55390
34
+ sourcecode/migrate_check.py,sha256=H8iy7Vk8cGL0dnR3ZkFPS20CtfF5LJWuzQVQE4awQ9s,56192
35
35
  sourcecode/openapi_surface.py,sha256=BTt0K-woZbkbWTN77IkqeBm_Okag9owR0848fmot8sk,16207
36
36
  sourcecode/output_budget.py,sha256=Js9yUlfQtPhqBl9R6wn_9UHVjjJc3GtLcqyfjf5t50Q,9869
37
37
  sourcecode/path_filters.py,sha256=EN1RGZRvLq5EcPgpjYV_IyCKVlAQQn2bbpEisQ5LpGg,3780
@@ -44,7 +44,7 @@ sourcecode/redactor.py,sha256=SB4hwIvg8h-hvcqKcDWaZvA-aSyn-at-BIRwa0tUv5E,3227
44
44
  sourcecode/relevance_scorer.py,sha256=0AgEt4KrV73nioMqBgjhGjtY7L2C7L7cSyKtj3IKcrw,9408
45
45
  sourcecode/rename_refactor.py,sha256=h6dNFlB9aZ_3q6heeHBkgXQeXaT03nvPSsYH6P8qxFg,12965
46
46
  sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
47
- sourcecode/repository_ir.py,sha256=eBC8Jh1rBH8xE46atHmdGNQxcjuROyUCr0iCuhqviUc,210340
47
+ sourcecode/repository_ir.py,sha256=3phYLSMp0_hUDdXLBpE7J64H2yNhTSR_s3-rGqsToWk,211987
48
48
  sourcecode/ris.py,sha256=RcqLVwC-doFcKKViYDkCjZLBqf_wzLES7-F6vHEeWzE,20419
49
49
  sourcecode/runtime_classifier.py,sha256=uTAD6BDCiBLUZEDRfqk718kM4RTT_vAbfkcOI2_Xx58,18432
50
50
  sourcecode/scanner.py,sha256=WdOQ78mMzjR1NjmKTlbxdgwinnCTfAhxCVLBEFQiFHU,8899
@@ -54,7 +54,7 @@ sourcecode/semantic_analyzer.py,sha256=4OdG6tTSnTvq3_dSWMbQu8Ad1ndSCKeG-b9qM4hIx
54
54
  sourcecode/serializer.py,sha256=TGzftrSKitZrtl6Hh-R05s4KdTOxwTmph_lGDbo2Wzg,125015
55
55
  sourcecode/spring_event_topology.py,sha256=5_ON_21Le5zbG-1GRc5GLIi5HJfy_QjcXLVPC5WeUGQ,18055
56
56
  sourcecode/spring_findings.py,sha256=G7Or2lKBUQbcTDqudLvSs9XvNg_YoAa-_lBOG_ULs8E,5457
57
- sourcecode/spring_impact.py,sha256=WUbBw6Ne9esN_KczIs9BCJdRAmjDKtU6E2_auo-771s,44865
57
+ sourcecode/spring_impact.py,sha256=5ooAVO-gg1rL-wRaT9_V8ra8edq5TAKu-kp4sAEoS_U,46343
58
58
  sourcecode/spring_model.py,sha256=zOAgFmrRbG4a6KLm1TJl55aWMyPNsz3OS3FSczqPG6A,16594
59
59
  sourcecode/spring_security_audit.py,sha256=XtPJ1SXlZJ8k6VYmaWuAp7Bbir4UmreAL7doIGQ5I7o,20595
60
60
  sourcecode/spring_semantic.py,sha256=O1nKSGVzlukuxLHQVuCPxc-XrcrMFxwlHA20_dmEGgM,13307
@@ -101,8 +101,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
101
101
  sourcecode/telemetry/events.py,sha256=LtzYfaX9Ilckj5PTvAcTpDa9mLqDsYPDUiDkRa58piY,2580
102
102
  sourcecode/telemetry/filters.py,sha256=NHa5T-6DaZduQPFuC34jOqHWQgSizM-Ygq8aZ4j19ng,5834
103
103
  sourcecode/telemetry/transport.py,sha256=4gGHsq0WeY9VywEZXA3vUxykfiYnw9uuqfjAAec7F8o,1681
104
- sourcecode-1.50.0.dist-info/METADATA,sha256=AMKgfVLsoGpmtnK3Vk75eHizMToSqxiiPsoMzTA-res,34684
105
- sourcecode-1.50.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
106
- sourcecode-1.50.0.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
107
- sourcecode-1.50.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
108
- sourcecode-1.50.0.dist-info/RECORD,,
104
+ sourcecode-1.52.0.dist-info/METADATA,sha256=ya3bHzj66Etk_azeGeoKctKhi1pxVxI_kKFJwoNF_pM,34684
105
+ sourcecode-1.52.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
106
+ sourcecode-1.52.0.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
107
+ sourcecode-1.52.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
108
+ sourcecode-1.52.0.dist-info/RECORD,,