sourcecode 2.5.11__py3-none-any.whl → 2.5.13__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
@@ -4,4 +4,4 @@ ASK Engine is the product. ``ask`` is the canonical CLI command; ``sourcecode``
4
4
  the legacy compatibility alias and the Python/PyPI package name. See
5
5
  docs/PRODUCT_IDENTITY.md (normative)."""
6
6
 
7
- __version__ = "2.5.11"
7
+ __version__ = "2.5.13"
@@ -286,6 +286,11 @@ class ArchitectureAnalyzer:
286
286
  # architecture claim". `_detect_layers` and its helpers append the
287
287
  # findings' sink-neutral detail here; drained into `limitations` below.
288
288
  self._refutations: list[str] = []
289
+ # Gap #2 (fail-open): a reconciliation rule that could not be evaluated
290
+ # is NOT a clean result — surfaced here as its own limitation, never as a
291
+ # refutation and never silently, so an unverified pattern cannot ship as
292
+ # confirmed.
293
+ self._reconciliation_errors: list[str] = []
289
294
 
290
295
  # Step 1: filter paths
291
296
  filtered = self._filter_paths(sm.file_paths)
@@ -498,6 +503,14 @@ class ArchitectureAnalyzer:
498
503
  continue
499
504
  _seen.add(_detail)
500
505
  limitations.append(f"Architecture claim refuted on evidence: {_detail}")
506
+ # Fail-open sink: a rule that could not run is surfaced distinctly — the
507
+ # pattern it would have checked is reported UNVERIFIED, never certified.
508
+ _seen_err: set[str] = set()
509
+ for _detail in self._reconciliation_errors:
510
+ if _detail in _seen_err:
511
+ continue
512
+ _seen_err.add(_detail)
513
+ limitations.append(f"Architecture reconciliation could not run: {_detail}")
501
514
 
502
515
  return ArchitectureAnalysis(
503
516
  requested=True,
@@ -644,6 +657,24 @@ class ArchitectureAnalyzer:
644
657
  return domains
645
658
 
646
659
  def _detect_layers(self, paths: list[str]) -> tuple[str, list[ArchitectureLayer]]:
660
+ # Drop directory ENTRIES. `flatten_file_tree` emits every node — files AND
661
+ # the directories above them — so `sm.file_paths` carries `src`, `src/main`,
662
+ # `src/main/java/.../model` … A directory is not a file: counting it as a
663
+ # layer/module member makes it read as jvm-less in `runtime_census` (it holds
664
+ # no file OF ITS OWN, only children), and a directory that transitively holds
665
+ # all the Java is then refuted as "no jvm code" — the demonstrated
666
+ # contamination (petclinic phantom `orchestration` = `src/main`; the
667
+ # directory half of `data` = `.../model`). A path that is an ancestor of
668
+ # another path in the set is such an entry; leaf files (incl. template
669
+ # view-layers) are never ancestors and are kept untouched.
670
+ _norm = [p.replace("\\", "/") for p in paths]
671
+ _ancestors: set[str] = set()
672
+ for _p in _norm:
673
+ _segs = _p.split("/")
674
+ for _k in range(1, len(_segs)):
675
+ _ancestors.add("/".join(_segs[:_k]))
676
+ paths = [p for p, n in zip(paths, _norm) if n not in _ancestors]
677
+
647
678
  # Exclude non-source paths (tests, benchmarks, docs, tooling, vendored assets)
648
679
  # from layer scoring. Also exclude anything under a `resources/` segment: in
649
680
  # Maven/Gradle layouts `src/main/resources/**` is bundled config/assets — e.g.
@@ -721,6 +752,11 @@ class ArchitectureAnalyzer:
721
752
  for _f in reconcile_architecture_evidence(
722
753
  pattern=pattern_name, layer_files=_layer_paths, repo_files=source_paths
723
754
  ):
755
+ if _f.error:
756
+ # A rule that could not run is surfaced always (not tied to
757
+ # whether this candidate wins) and never as a refutation.
758
+ self._reconciliation_errors.append(_f.detail)
759
+ continue
724
760
  _refs.append(_f.detail)
725
761
  if _f.rule == "composition_runtime_split":
726
762
  # A claim that IS a runtime split must show one: JobRunr, a JVM
@@ -922,6 +958,9 @@ class ArchitectureAnalyzer:
922
958
  # pruning here directly shapes the returned result (fewer layers, or a
923
959
  # drop below 2 that falls through to flat/unknown) — surface it.
924
960
  for _f in reconcile_code_modules(layer_files=non_empty, repo_files=paths):
961
+ if _f.error:
962
+ self._reconciliation_errors.append(_f.detail)
963
+ continue
925
964
  for _bad in _f.symbols:
926
965
  non_empty.pop(_bad, None)
927
966
  self._refutations.append(_f.detail)
@@ -957,6 +996,9 @@ class ArchitectureAnalyzer:
957
996
  # eureka "images" (five PNGs). The real modules survive untouched.
958
997
  # INV-F1-4: same as the layered path — surface each refuted module.
959
998
  for _f in reconcile_code_modules(layer_files=meaningful, repo_files=paths):
999
+ if _f.error:
1000
+ self._reconciliation_errors.append(_f.detail)
1001
+ continue
960
1002
  for _bad in _f.symbols:
961
1003
  meaningful.pop(_bad, None)
962
1004
  self._refutations.append(_f.detail)
@@ -210,23 +210,38 @@ class ArchitectureSummarizer:
210
210
  # welds a foreign runtime's framework onto the primary's label. Drop what
211
211
  # the claimed stack does not attest, and never let the TYPE label — which
212
212
  # those same frameworks drove — survive its own evidence (P2-F).
213
- from sourcecode.reconciliation import type_claim_unattested, unattested_frameworks
213
+ from sourcecode.reconciliation import (
214
+ has_execution_error,
215
+ reconcile_headline_evidence,
216
+ type_claim_unattested,
217
+ )
214
218
 
215
- foreign = unattested_frameworks(primary.stack, sm.project_type, sm.stacks)
216
- attested = [name for name in fw_names if name not in foreign]
217
- dropped = [name for name in fw_names if name in foreign]
218
- if dropped and not attested and type_claim_unattested(
219
+ _headline_findings = reconcile_headline_evidence(
219
220
  claimed_stack=primary.stack, project_type=sm.project_type, stacks=sm.stacks
220
- ):
221
- return self._describe_foreign_evidence(sm, stack_label, runtime, dropped)
222
- fw_names = attested
221
+ )
222
+ if has_execution_error(_headline_findings):
223
+ # Fail-open guard (gap #2): the attestation rule could not run, and
224
+ # this prose surface has no gap channel to warn on. Rather than assert
225
+ # an unverified weld ("C#/.NET … using JAX-RS"), withhold the framework
226
+ # clause — the claim is not made when its check did not execute.
227
+ fw_names = []
228
+ else:
229
+ foreign = frozenset(s for f in _headline_findings for s in f.symbols)
230
+ attested = [name for name in fw_names if name not in foreign]
231
+ dropped = [name for name in fw_names if name in foreign]
232
+ if dropped and not attested and type_claim_unattested(
233
+ claimed_stack=primary.stack, project_type=sm.project_type, stacks=sm.stacks
234
+ ):
235
+ return self._describe_foreign_evidence(sm, stack_label, runtime, dropped)
236
+ fw_names = attested
223
237
 
224
238
  fw_str = f" using {', '.join(fw_names)}" if fw_names else ""
225
239
  if runtime:
226
240
  # BUG #4: never assert "rest api" in the headline unless the endpoints
227
- # command actually backs it (Java/Kotlin only — that is where we have an
228
- # authoritative extractor). Degrade to a qualified, consistent phrasing.
229
- if sm.project_type == "api" and primary.stack in {"java", "kotlin"}:
241
+ # command actually backs it. Authority is Java ONLY: extract_java_endpoints
242
+ # reads *.java and is blind to .kt — a Kotlin REST API would otherwise
243
+ # degrade to "only 0 endpoints detected" over routes it cannot see.
244
+ if sm.project_type == "api" and primary.stack == "java":
230
245
  total, high = self._endpoint_support()
231
246
  if high < _MIN_REST_ENDPOINTS_FOR_LABEL:
232
247
  plural = "s" if total != 1 else ""
@@ -310,23 +325,62 @@ class ArchitectureSummarizer:
310
325
 
311
326
  def _qualify_web_pattern(self, arch: Any, arch_line: str, sm: SourceMap) -> str:
312
327
  """BUG #1 (Jenkins field test): the "mvc" pattern is a directory-name
313
- heuristic (dirs matching controller/model/view keywords). On a Java/Kotlin
314
- repo, do not assert "MVC pattern with ... view layers" in prose when the
328
+ heuristic (dirs matching controller/model/view keywords). On a JVM repo,
329
+ do not assert "MVC pattern with ... view layers" in prose when the
315
330
  canonical endpoint extractor finds no HTTP controllers in the same run —
316
331
  Jenkins (Stapler web framework) matches model/view-ish dirs but declares
317
- zero Spring MVC controllers. Cross-check against `endpoints` and degrade to
318
- a consistent, non-committal phrasing instead of a categorical MVC claim."""
332
+ zero Spring MVC controllers.
333
+
334
+ This is a reconciliation rule (layer table × endpoint index), so the
335
+ judgement lives in F-1 (`reconcile_web_pattern_evidence`), not as an
336
+ ad-hoc `if` here (ADR-0005 §6.2/§8.4). The honest predicate is ZERO
337
+ modeled controllers, not `< 5`: with the old threshold, 1–4 real
338
+ controllers still degraded to the categorical "no HTTP controllers
339
+ detected" — a false statement. One controller backs the MVC claim; only
340
+ an empty index contradicts it.
341
+
342
+ **Authority = Java only.** The extractor (`extract_java_endpoints`) reads
343
+ `*.java`; it is blind to `.kt`/`.scala`/`.groovy`. So zero controllers is
344
+ a contradiction ONLY for a Java-primary repo. On Kotlin/Scala/Groovy an
345
+ empty index is the extractor's blindness, not absence of controllers —
346
+ degrading there asserted "no HTTP controllers detected" over real `.kt`
347
+ routes (INV-F1-1: never fire on absence of evidence). Gating the rule on
348
+ the JVM *family* was wrong; the boundary is the extractor's file coverage."""
349
+ from sourcecode.reconciliation import (
350
+ has_execution_error,
351
+ reconcile_web_pattern_evidence,
352
+ )
353
+
319
354
  if not arch_line or arch.pattern not in ("mvc", "spring_mvc_layered"):
320
355
  return arch_line
321
356
  primary = next((s for s in sm.stacks if s.primary), sm.stacks[0] if sm.stacks else None)
322
- if primary is None or primary.stack not in {"java", "kotlin"}:
323
- return arch_line
324
357
  _total, high = self._endpoint_support()
325
- if high >= _MIN_REST_ENDPOINTS_FOR_LABEL:
326
- return arch_line
327
- # No controller backing → the MVC/view claim is unverified. Report the
328
- # directory-derived layers without the framework label or the "view" claim.
358
+ # The endpoint extractor reads .java only — it is authoritative for a
359
+ # Java-primary repo, blind elsewhere.
360
+ _authoritative = bool(primary) and primary.stack == "java"
361
+ _findings = reconcile_web_pattern_evidence(
362
+ pattern=arch.pattern,
363
+ endpoint_evidence_authoritative=_authoritative,
364
+ modeled_endpoint_count=high,
365
+ )
329
366
  layer_names = [l.name for l in arch.layers[:4] if l.name != "view"] if arch.layers else []
367
+ if has_execution_error(_findings):
368
+ # Fail-open guard (gap #2): the cross-check could not run. Withhold the
369
+ # categorical MVC label AND the "no controllers" claim (both unverified);
370
+ # report only the directory-derived layers, or nothing.
371
+ if layer_names:
372
+ return (
373
+ f"Layered code organization ({', '.join(layer_names)}; "
374
+ "directory-based — endpoint cross-check unavailable)."
375
+ )
376
+ return ""
377
+ if not _findings:
378
+ # Non-Java (extractor blind), not a web pattern, or ≥1 controller backs
379
+ # the claim → keep. On Kotlin/Scala/Groovy the MVC label stays
380
+ # unverified rather than falsely negated.
381
+ return arch_line
382
+ # Contradiction: zero modeled controllers. Degrade — the claim is
383
+ # directory names, not routes; the "no HTTP controllers" wording is TRUE.
330
384
  if layer_names:
331
385
  return (
332
386
  f"Layered code organization ({', '.join(layer_names)}; "
sourcecode/classifier.py CHANGED
@@ -207,7 +207,7 @@ class TypeClassifier:
207
207
  # structured codebase (e.g. JobRunr: core + per-framework adapter modules).
208
208
  # This is checked BEFORE the weak `bin/`-directory CLI heuristic so a build
209
209
  # output / wrapper `bin/` dir does not mislabel a library as a CLI.
210
- if stack_names & {"java", "kotlin", "scala"} and self._is_multi_module(file_tree):
210
+ if stack_names & _JVM_STACKS and self._is_multi_module(file_tree):
211
211
  return "library"
212
212
 
213
213
  # Weak CLI heuristic: a top-level bin/ directory (only when nothing stronger).
@@ -120,6 +120,20 @@ class ConfidenceAnalyzer:
120
120
  project_type=sm.project_type,
121
121
  stacks=sm.stacks,
122
122
  ):
123
+ if _rf.error:
124
+ # Fail-open guard (gap #2): the stack-attestation rule could
125
+ # not run. Never let that read as "primary attests every
126
+ # framework" — record it as an anomaly + gap, not silence.
127
+ anomalies.append(
128
+ "Stack/framework attestation could not be evaluated — "
129
+ "primary-stack attribution is UNVERIFIED"
130
+ )
131
+ gaps.append(AnalysisGap(
132
+ area="framework_stack_attestation",
133
+ reason=_rf.detail,
134
+ impact="medium",
135
+ ))
136
+ continue
123
137
  anomalies.append(
124
138
  f"{', '.join(_rf.symbols)} declared by another runtime, not by the "
125
139
  f"{_primary.stack} primary stack"
@@ -120,6 +120,54 @@ def is_test_or_fixture_path(path: str) -> bool:
120
120
  return False
121
121
 
122
122
 
123
+ # Infrastructure words that, combined with a delimited "test" token in the same
124
+ # directory segment, identify a module whose code is test scaffolding even though
125
+ # it lives under src/main (e.g. "server-test-utils", "foo-testkit"). Kept narrow
126
+ # on purpose: the "test" token must be present in the SAME segment, so packages
127
+ # named "utils"/"support"/"tools" alone never match.
128
+ _TEST_INFRA_TOKENS = frozenset({
129
+ "util", "utils", "fixture", "fixtures", "support",
130
+ "framework", "kit", "harness", "tools", "tool",
131
+ "helper", "helpers", "mock", "mocks", "stub", "stubs", "dummy",
132
+ })
133
+
134
+ # Glued (non-hyphenated) test-fixture module suffixes.
135
+ _TEST_INFRA_SUFFIXES = (
136
+ "testutils", "testutil", "testkit", "testfixtures", "testfixture",
137
+ "testsupport", "testframework", "testharness", "testtools",
138
+ "testhelpers", "testmocks", "teststubs",
139
+ )
140
+
141
+
142
+ def is_test_fixture_module_path(path: str) -> bool:
143
+ """Return True when *path* belongs to a test-fixture / test-utility MODULE.
144
+
145
+ Structural, name-agnostic: a directory segment must contain a hyphen/underscore
146
+ -delimited ``test`` token together with an infrastructure word (util(s),
147
+ fixture(s), support, framework, kit, harness, tools, helper(s), mock(s),
148
+ stub(s), dummy) — e.g. ``server-test-utils``, ``foo-testkit``,
149
+ ``bar-test-support`` — or match a known test-module segment outright.
150
+
151
+ This is deliberately NARROWER than :func:`is_test_or_fixture_path`: it targets
152
+ modules that ship HTTP resources, JAX-RS ``@Path`` fixtures, or dummy
153
+ controllers under ``src/main`` so they are not misreported as production API
154
+ surface. Requiring both tokens in one segment keeps legitimate ``utils`` /
155
+ ``support`` / ``tools`` packages from ever matching.
156
+ """
157
+ norm = path.replace("\\", "/").lower()
158
+ parts = norm.split("/")
159
+ for part in parts[:-1]: # skip the filename
160
+ bare = part.rstrip("/")
161
+ if bare in _TEST_MODULE_SEGMENTS:
162
+ return True
163
+ if bare.endswith(_TEST_INFRA_SUFFIXES):
164
+ return True
165
+ toks = bare.replace("_", "-").split("-")
166
+ if "test" in toks and any(t in _TEST_INFRA_TOKENS for t in toks):
167
+ return True
168
+ return False
169
+
170
+
123
171
  def is_vendor_path(path: str) -> bool:
124
172
  """Return True when *path* is inside a vendored / third-party directory.
125
173
 
@@ -1249,6 +1249,17 @@ class TaskContextBuilder:
1249
1249
  [ws.path for ws in workspace_analysis.workspaces],
1250
1250
  )
1251
1251
 
1252
+ # F-1 single-ownership (ADR-0005 §INV-F1-4): run the architecture analysis
1253
+ # ONCE, here, and keep it on sm.architecture — the single owned analysis.
1254
+ # ArchitectureSummarizer/ContextSummarizer reuse it instead of each computing
1255
+ # (and discarding) a private copy, and its refutations are transported into
1256
+ # `limitations` below (surface #3, F1-SURFACE-INTEGRATION-2026-07-18).
1257
+ from sourcecode.architecture_analyzer import ArchitectureAnalyzer as _ArchAnalyzer
1258
+ try:
1259
+ sm.architecture = _ArchAnalyzer().analyze(self.root, sm)
1260
+ except Exception:
1261
+ sm.architecture = None
1262
+
1252
1263
  project_summary = ProjectSummarizer(self.root).generate(sm)
1253
1264
  architecture_summary = ArchitectureSummarizer(self.root).generate(sm)
1254
1265
 
@@ -1306,6 +1317,19 @@ class TaskContextBuilder:
1306
1317
  key_dependencies: list[dict[str, Any]] = []
1307
1318
  limitations: list[str] = []
1308
1319
 
1320
+ # INV-F1-4 transport: this surface narrates the architecture, so it must
1321
+ # carry the owner's refutations — the same lines surface #1 (serializer)
1322
+ # emits. Only the F-1 refutation / execution-error lines (their fixed
1323
+ # prefixes); pattern-inference notes stay out of the onboard surface. The
1324
+ # analysis is the single owned one (sm.architecture) — no re-run, no re-judge.
1325
+ if sm.architecture is not None:
1326
+ for _arch_lim in (sm.architecture.limitations or []):
1327
+ if _arch_lim.startswith((
1328
+ "Architecture claim refuted on evidence:",
1329
+ "Architecture reconciliation could not run:",
1330
+ )):
1331
+ limitations.append(_arch_lim)
1332
+
1309
1333
  if spec.enable_dependencies:
1310
1334
  from dataclasses import asdict
1311
1335
  from sourcecode.dependency_analyzer import DependencyAnalyzer
@@ -62,17 +62,75 @@ from pathlib import Path
62
62
 
63
63
  @dataclass(frozen=True)
64
64
  class ReconciliationFinding:
65
- """One positively-detected contradiction between two evidence views."""
65
+ """One reconciliation outcome that a consumer must not narrate away.
66
+
67
+ Two kinds ride this one type, distinguished by `error`:
68
+
69
+ - a **detected contradiction** (`error=False`, `symbols` non-empty): two
70
+ views positively disagree; the consumer degrades the claim and names it.
71
+ - an **execution error** (`error=True`, `symbols` empty): a rule could not be
72
+ evaluated. It exists so that a rule that raises is NEVER indistinguishable
73
+ from a rule that ran and found nothing — the fail-open that let an
74
+ exception silently certify the very claim the rule was meant to check.
75
+
76
+ A rule that is *not applicable*, or that ran and found the views consistent,
77
+ yields NO finding: both legitimately mean "no contradiction, the claim may
78
+ stand", and no consumer acts differently on them. Only the error case needed
79
+ a physical state of its own (ADR-0005 §8.2).
80
+ """
66
81
  rule: str # stable rule id, e.g. "endpoint_coverage"
67
82
  symbols: tuple[str, ...] # symbols the contradiction is anchored to
68
83
  detail: str # one-sentence human/agent-readable statement
84
+ error: bool = False # True iff this marks a rule that could not run
69
85
 
70
86
  def to_dict(self) -> dict:
71
- return {
87
+ d = {
72
88
  "rule": self.rule,
73
89
  "symbols": list(self.symbols),
74
90
  "detail": self.detail,
75
91
  }
92
+ # Healthy findings serialize byte-identically to before; the flag appears
93
+ # only when set, so no existing consumer/snapshot shifts.
94
+ if self.error:
95
+ d["error"] = True
96
+ return d
97
+
98
+
99
+ def _execution_error(rule_id: str, exc: Exception) -> ReconciliationFinding:
100
+ """Build the finding that marks a rule which raised instead of running."""
101
+ return ReconciliationFinding(
102
+ rule=rule_id,
103
+ symbols=(),
104
+ error=True,
105
+ detail=(
106
+ f"the {rule_id} reconciliation rule could not be evaluated "
107
+ f"({type(exc).__name__}) — its consistency check did NOT run, so the "
108
+ "claim it guards is UNVERIFIED, not confirmed."
109
+ ),
110
+ )
111
+
112
+
113
+ def _run_rule(rule_id: str, thunk) -> list[ReconciliationFinding]:
114
+ """Run one rule in isolation. On any exception, return an execution-error
115
+ finding instead of swallowing it — the core of the fail-open fix. Per-rule
116
+ (not per-surface) so one rule raising never blanks a sibling rule's finding.
117
+ """
118
+ try:
119
+ return list(thunk())
120
+ except Exception as exc: # noqa: BLE001 — deliberate: convert, never swallow
121
+ return [_execution_error(rule_id, exc)]
122
+
123
+
124
+ def execution_errors(
125
+ findings: "list[ReconciliationFinding]",
126
+ ) -> list[ReconciliationFinding]:
127
+ """The execution-error findings in a batch (empty if every rule ran)."""
128
+ return [f for f in findings if getattr(f, "error", False)]
129
+
130
+
131
+ def has_execution_error(findings: "list[ReconciliationFinding]") -> bool:
132
+ """True iff any rule in the batch could not be evaluated."""
133
+ return any(getattr(f, "error", False) for f in findings)
76
134
 
77
135
 
78
136
  def http_handler_classes(cir) -> frozenset[str]:
@@ -142,14 +200,12 @@ def reconcile_impact_evidence(
142
200
  modeled_classes: classes the endpoint index has ≥1 route for
143
201
  (endpoint_index.controller_fqns).
144
202
  """
145
- findings: list[ReconciliationFinding] = []
146
- try:
147
- findings.extend(
148
- _rule_endpoint_coverage(chain_classes, handler_classes, modeled_classes)
149
- )
150
- except Exception:
151
- pass
152
- return findings
203
+ return _run_rule(
204
+ "endpoint_coverage",
205
+ lambda: _rule_endpoint_coverage(
206
+ chain_classes, handler_classes, modeled_classes
207
+ ),
208
+ )
153
209
 
154
210
 
155
211
  # ---------------------------------------------------------------------------
@@ -387,11 +443,11 @@ def reconcile_code_modules(
387
443
  layer_files: {claimed module -> its files}.
388
444
  repo_files: every source file, supplying the repo's own runtime.
389
445
  """
390
- try:
446
+ def _run() -> list[ReconciliationFinding]:
391
447
  repo_runtimes = runtime_census({"_repo": list(repo_files)})["_repo"]
392
448
  return _rule_layer_code_attestation(runtime_census(layer_files), repo_runtimes)
393
- except Exception:
394
- return []
449
+
450
+ return _run_rule("layer_code_attestation", _run)
395
451
 
396
452
 
397
453
  def unattested_layers(
@@ -422,16 +478,22 @@ def reconcile_architecture_evidence(
422
478
  repo_files: every source file considered — supplies the repo's own
423
479
  runtime, the arbiter of which layers belong to the claim.
424
480
  """
425
- findings: list[ReconciliationFinding] = []
426
481
  try:
427
482
  census = runtime_census(layer_files)
428
483
  repo_runtimes = runtime_census({"_repo": list(repo_files)})["_repo"]
429
- findings.extend(
430
- _rule_layer_language_coherence(pattern, census, repo_runtimes)
431
- )
432
- findings.extend(_rule_composition_runtime_split(pattern, census))
433
- except Exception:
434
- pass
484
+ except Exception as exc:
485
+ # Shared view prep failed — neither rule could run. One error finding,
486
+ # attributed to the primary architecture rule, carries that to the sink.
487
+ return [_execution_error("layer_language_coherence", exc)]
488
+ findings: list[ReconciliationFinding] = []
489
+ findings += _run_rule(
490
+ "layer_language_coherence",
491
+ lambda: _rule_layer_language_coherence(pattern, census, repo_runtimes),
492
+ )
493
+ findings += _run_rule(
494
+ "composition_runtime_split",
495
+ lambda: _rule_composition_runtime_split(pattern, census),
496
+ )
435
497
  return findings
436
498
 
437
499
 
@@ -487,6 +549,19 @@ _STACK_FAMILY: dict[str, str] = {
487
549
  "elixir": "beam", "erlang": "beam",
488
550
  }
489
551
 
552
+
553
+ def stack_runtime_family(stack_id: str) -> str | None:
554
+ """Runtime family of a stack id (``jvm``/``js``/``python``/…), or None.
555
+
556
+ The single source of truth for "which stacks share a runtime". A consumer
557
+ that gates on JVM-ness (a Kotlin, Scala or Groovy repo is the SAME runtime as
558
+ a Java one) reads this instead of hardcoding ``{"java", "kotlin"}`` — which
559
+ silently dropped Scala and Groovy and let an unverified claim ship on them.
560
+ None means "unjudgeable", never a contradiction (INV-F1-1).
561
+ """
562
+ return _STACK_FAMILY.get(stack_id or "")
563
+
564
+
490
565
  # Project types that are SUPPOSED to span runtimes — their headline names a tier
491
566
  # split, so a framework from another runtime is the point, not a contradiction.
492
567
  # Same carve-out (and same names) as `_CROSS_RUNTIME_PATTERNS` for layer claims.
@@ -594,17 +669,13 @@ def reconcile_headline_evidence(
594
669
  """
595
670
  if project_type in _CROSS_RUNTIME_TYPES:
596
671
  return []
597
- findings: list[ReconciliationFinding] = []
598
- try:
599
- findings.extend(
600
- _rule_framework_stack_attestation(
601
- _STACK_FAMILY.get(claimed_stack or ""),
602
- framework_owner_families(stacks),
603
- )
604
- )
605
- except Exception:
606
- pass
607
- return findings
672
+ return _run_rule(
673
+ "framework_stack_attestation",
674
+ lambda: _rule_framework_stack_attestation(
675
+ _STACK_FAMILY.get(claimed_stack or ""),
676
+ framework_owner_families(stacks),
677
+ ),
678
+ )
608
679
 
609
680
 
610
681
  def type_claim_unattested(
@@ -631,6 +702,12 @@ def type_claim_unattested(
631
702
  return False
632
703
  return not any(family in fams for fams in owners.values())
633
704
  except Exception:
705
+ # A bool predicate cannot carry an execution-error finding. It never
706
+ # decides alone: every caller runs `reconcile_headline_evidence` on the
707
+ # SAME stacks first (same `framework_owner_families` call), so an
708
+ # exception here has already surfaced as an execution-error finding
709
+ # there. Returning False adds NO extra clause — it does not certify a
710
+ # claim the paired findings call left unguarded.
634
711
  return False
635
712
 
636
713
 
@@ -646,6 +723,81 @@ def unattested_frameworks(
646
723
  return frozenset(out)
647
724
 
648
725
 
726
+ # ---------------------------------------------------------------------------
727
+ # Narrative reconciliation (web-pattern claim × endpoint index)
728
+ # ---------------------------------------------------------------------------
729
+
730
+ # Patterns that assert an HTTP/view tier. The claim is directory-derived (dirs
731
+ # named controller/model/view); the endpoint index is the authoritative
732
+ # controller evidence for the SAME run. They contradict when the pattern names a
733
+ # web tier but the index modeled NO controller at all — Jenkins (Stapler)
734
+ # matched model/view dirs and declared zero Spring MVC controllers.
735
+ _WEB_PATTERNS: frozenset[str] = frozenset({"mvc", "spring_mvc_layered"})
736
+
737
+
738
+ def _rule_web_pattern_attestation(
739
+ pattern: str, endpoint_evidence_authoritative: bool, modeled_endpoint_count: int
740
+ ) -> list[ReconciliationFinding]:
741
+ """A web/MVC pattern claim that the endpoint index does not back at all.
742
+
743
+ Provable ABSENCE, not a threshold: the rule fires only at ZERO modeled
744
+ controllers (`== 0`), never at "few". One controller backs the claim — the
745
+ pattern has real HTTP evidence and is not contradicted. The "< N is not
746
+ really a web app" judgement is a LABEL heuristic and stays in the consumer
747
+ (ADR-0005 limit 2: a rule may not carry a threshold).
748
+
749
+ **Authority gate (INV-F1-1).** Zero controllers is a CONTRADICTION only where
750
+ the endpoint extractor can actually see the code. The canonical extractor
751
+ reads `*.java` only — so on a Kotlin/Scala/Groovy repo an empty index is
752
+ *absence of evidence* (the extractor is blind to `.kt`/`.scala`/`.groovy`),
753
+ NOT a contradiction, and firing there is a false negative (measured: a Kotlin
754
+ `@RestController` with real routes read "no HTTP controllers detected"). The
755
+ caller passes `endpoint_evidence_authoritative`; when false, the rule stays
756
+ silent. The consumer decides authority (it owns the extractor fact), keeping
757
+ this rule free of any language/extractor coupling.
758
+ """
759
+ if pattern not in _WEB_PATTERNS or not endpoint_evidence_authoritative:
760
+ return []
761
+ if modeled_endpoint_count > 0:
762
+ return []
763
+ return [
764
+ ReconciliationFinding(
765
+ rule="web_pattern_endpoint_attestation",
766
+ symbols=(pattern,),
767
+ detail=(
768
+ f"the '{pattern}' claim names an HTTP/view tier but the endpoint "
769
+ "index modeled no controller — the layers are directory names, "
770
+ "not routes."
771
+ ),
772
+ )
773
+ ]
774
+
775
+
776
+ def reconcile_web_pattern_evidence(
777
+ *,
778
+ pattern: str,
779
+ endpoint_evidence_authoritative: bool,
780
+ modeled_endpoint_count: int,
781
+ ) -> list[ReconciliationFinding]:
782
+ """Run the web-pattern attestation rule. Never raises.
783
+
784
+ Args:
785
+ pattern: the claimed architecture pattern.
786
+ endpoint_evidence_authoritative: True iff the endpoint extractor can see
787
+ this repo's controllers (it reads .java
788
+ only — the consumer owns this fact). When
789
+ False, zero endpoints is absence, not a
790
+ contradiction (INV-F1-1), and no finding.
791
+ modeled_endpoint_count: high-confidence controllers modeled.
792
+ """
793
+ return _run_rule(
794
+ "web_pattern_endpoint_attestation",
795
+ lambda: _rule_web_pattern_attestation(
796
+ pattern, endpoint_evidence_authoritative, modeled_endpoint_count
797
+ ),
798
+ )
799
+
800
+
649
801
  def reconcile_extraction_evidence(
650
802
  *,
651
803
  type_decl_files: frozenset[str],
@@ -657,9 +809,7 @@ def reconcile_extraction_evidence(
657
809
  type_decl_files: files whose masked source declares a Java type.
658
810
  extracted_files: files symbol extraction produced ≥1 symbol for.
659
811
  """
660
- findings: list[ReconciliationFinding] = []
661
- try:
662
- findings.extend(_rule_parse_coverage(type_decl_files, extracted_files))
663
- except Exception:
664
- pass
665
- return findings
812
+ return _run_rule(
813
+ "parse_coverage",
814
+ lambda: _rule_parse_coverage(type_decl_files, extracted_files),
815
+ )
@@ -5584,7 +5584,7 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
5584
5584
  """
5585
5585
  import re as _re
5586
5586
  from typing import Any as _Any
5587
- from sourcecode.path_filters import is_test_path
5587
+ from sourcecode.path_filters import is_test_path, is_test_fixture_module_path
5588
5588
 
5589
5589
  _EXTENDS_FROM_SIG = _re.compile(r'\bextends\s+(\w+)')
5590
5590
 
@@ -5753,6 +5753,16 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
5753
5753
  endpoint_warnings = _recovery["warnings"]
5754
5754
  _openapi_spec_path = _recovery["spec_path"]
5755
5755
 
5756
+ # Map each declared class/interface FQN to its source file so an endpoint can be
5757
+ # traced back to the MODULE that ships it. Used to flag endpoints that originate
5758
+ # in a test-fixture / test-utility module (dummy web services, JAX-RS @Path
5759
+ # fixtures) living under src/main — these are not production API surface.
5760
+ _fqn_to_file: dict[str, str] = {
5761
+ s.symbol: (s.source_file or s.declaring_file)
5762
+ for s in all_symbols
5763
+ if s.type in ("class", "interface")
5764
+ }
5765
+
5756
5766
  endpoints: list[dict] = []
5757
5767
  for route in routes:
5758
5768
  handler = (
@@ -5769,6 +5779,9 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
5769
5779
  "handler": handler,
5770
5780
  "return_type": route.get("return_type", "void"),
5771
5781
  }
5782
+ _src = _fqn_to_file.get(route.get("effective_class", ""), "")
5783
+ if _src and is_test_fixture_module_path(_src):
5784
+ entry["scope"] = "test_util"
5772
5785
  # Use security_annotations already extracted by _build_route_surface
5773
5786
  # via the canonical _route_security_from_sym extractor.
5774
5787
  security_info = route.get("security_annotations")
@@ -5791,6 +5804,14 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
5791
5804
  )
5792
5805
  endpoints = [e for e in endpoints if not _FQN_PATH_RE.search(e.get("path", ""))]
5793
5806
 
5807
+ # Partition test-fixture/test-utility endpoints out of the production surface.
5808
+ # They ship under src/main of a *-test-utils / *-testkit / test-framework module
5809
+ # (dummy web services, JAX-RS @Path fixtures) and must not be reported as, or
5810
+ # counted with, the real API surface — but they are preserved in their own bucket
5811
+ # so nothing is silently dropped and the exclusion is auditable.
5812
+ _test_util_endpoints = [e for e in endpoints if e.get("scope") == "test_util"]
5813
+ endpoints = [e for e in endpoints if e.get("scope") != "test_util"]
5814
+
5794
5815
  # "no_security_signal" = no recognized security annotation at method OR class level.
5795
5816
  # Note: repos may use framework-level security (e.g. Keycloak itself) with no
5796
5817
  # per-endpoint annotations — this count reflects annotation-based coverage only.
@@ -5919,6 +5940,16 @@ def extract_java_endpoints(root: Path) -> "dict[str, Any]":
5919
5940
  # Keep legacy field name for backward compat, now means same as no_security_signal
5920
5941
  "undocumented": no_security_signal,
5921
5942
  }
5943
+ # Surface test-fixture endpoints excluded from the production surface above.
5944
+ # Reported separately (not hidden) so the exclusion is transparent and auditable.
5945
+ if _test_util_endpoints:
5946
+ result["test_util_endpoints"] = _test_util_endpoints
5947
+ result["test_util_excluded"] = len(_test_util_endpoints)
5948
+ result.setdefault("notes", []).append(
5949
+ f"{len(_test_util_endpoints)} endpoint(s) from test-fixture/test-utility "
5950
+ f"module(s) under src/main were excluded from the production surface "
5951
+ f"(see test_util_endpoints)."
5952
+ )
5922
5953
  # Surface incomplete-endpoint warnings (interface-defined controllers) only when
5923
5954
  # present, to keep output backward-compatible for the common case.
5924
5955
  if endpoint_warnings:
@@ -1265,6 +1265,7 @@ class ImpactOrchestrator:
1265
1265
  # provably declares HTTP handlers but has no modeled route is a
1266
1266
  # positively-detected under-report of endpoints_affected.
1267
1267
  endpoint_coverage_blind_spot = False
1268
+ reconciliation_error_blind_spot = False
1268
1269
  reconciliation_findings: list[dict] = []
1269
1270
  try:
1270
1271
  _chain_classes = frozenset(
@@ -1277,9 +1278,23 @@ class ImpactOrchestrator:
1277
1278
  modeled_classes=frozenset(model.endpoint_index.controller_fqns),
1278
1279
  )
1279
1280
  reconciliation_findings = [f.to_dict() for f in _rec]
1281
+ # Fail-open guard (gap #2): an execution-error finding means a rule
1282
+ # could not run. It must NEVER be read as "no contradiction" — surface
1283
+ # it as its own blind spot so an unevaluated endpoint check can't pass
1284
+ # for a clean one. (The error finding carries rule="endpoint_coverage"
1285
+ # too, hence the `not _f.error` on the contradiction branch below.)
1286
+ _rec_errors = [f for f in _rec if f.error]
1287
+ if _rec_errors:
1288
+ reconciliation_error_blind_spot = True
1289
+ warnings.append(
1290
+ "Evidence reconciliation could not run for this query — "
1291
+ + _rec_errors[0].detail
1292
+ + " The endpoint-coverage check did NOT execute; treat the "
1293
+ "endpoint list as unverified, not confirmed complete."
1294
+ )
1280
1295
  _unmapped: list[str] = []
1281
1296
  for _f in _rec:
1282
- if _f.rule == "endpoint_coverage":
1297
+ if _f.rule == "endpoint_coverage" and not _f.error:
1283
1298
  endpoint_coverage_blind_spot = True
1284
1299
  _unmapped = list(_f.symbols)
1285
1300
  if endpoint_coverage_blind_spot:
@@ -1294,8 +1309,16 @@ class ImpactOrchestrator:
1294
1309
  "locator chains). Callers remain reliable; verify these classes' "
1295
1310
  "exposure before treating the endpoint list as complete."
1296
1311
  )
1297
- except Exception:
1298
- pass
1312
+ except Exception as _rec_exc:
1313
+ # Even assembling the reconciliation inputs failed (e.g.
1314
+ # http_handler_classes traversing a malformed IR). Same invariant: an
1315
+ # exception is not evidence of consistency — record a blind spot.
1316
+ reconciliation_error_blind_spot = True
1317
+ warnings.append(
1318
+ "Evidence reconciliation could not run for this query "
1319
+ f"({type(_rec_exc).__name__}) — the endpoint-coverage check did "
1320
+ "NOT execute; treat the endpoint list as unverified."
1321
+ )
1299
1322
 
1300
1323
  blind_spots = (
1301
1324
  # framework_di stops being a blind spot once CH-007 recovers callers
@@ -1305,6 +1328,7 @@ class ImpactOrchestrator:
1305
1328
  + (["method_scope_unproven"] if method_scope_blind_spot else [])
1306
1329
  + (["parse_coverage"] if parse_coverage_blind_spot else [])
1307
1330
  + (["endpoint_coverage_partial"] if endpoint_coverage_blind_spot else [])
1331
+ + (["reconciliation_error"] if reconciliation_error_blind_spot else [])
1308
1332
  )
1309
1333
 
1310
1334
  confidence: str
@@ -1322,9 +1346,16 @@ class ImpactOrchestrator:
1322
1346
  or parse_coverage_blind_spot
1323
1347
  ):
1324
1348
  confidence = "low"
1325
- elif resolution == "partial" or confidence_reducing or endpoint_coverage_blind_spot:
1349
+ elif (
1350
+ resolution == "partial"
1351
+ or confidence_reducing
1352
+ or endpoint_coverage_blind_spot
1353
+ or reconciliation_error_blind_spot
1354
+ ):
1326
1355
  # endpoint_coverage: the callers are still trustworthy — only the
1327
1356
  # endpoint axis is provably incomplete, so cap at medium, not low.
1357
+ # reconciliation_error: the endpoint check did not run at all, so we
1358
+ # cannot claim "high" — same medium cap, callers still stand.
1328
1359
  confidence = "medium"
1329
1360
  else:
1330
1361
  confidence = "high"
sourcecode/summarizer.py CHANGED
@@ -9,7 +9,10 @@ from pathlib import Path
9
9
  from typing import Any
10
10
 
11
11
  from sourcecode.architecture_analyzer import _LAYER_MIN_SHARE, _PATTERN_REQUIRED_KEYS
12
- from sourcecode.reconciliation import incoherent_layers, pattern_contradicted
12
+ from sourcecode.reconciliation import (
13
+ has_execution_error,
14
+ reconcile_architecture_evidence,
15
+ )
13
16
  from sourcecode.detectors.parsers import load_json_file, load_toml_file
14
17
  from sourcecode.tree_utils import safe_read_text
15
18
  from sourcecode.entrypoint_classifier import is_production_entry_point
@@ -437,9 +440,22 @@ class ProjectSummarizer:
437
440
  # SIM-3: same coherence rule the architecture prose applies — this
438
441
  # table is the headline's copy, and a fix applied to only one of the
439
442
  # two duplicated tables leaves the other one lying.
440
- for _bad in incoherent_layers(pattern_name, layer_files, file_paths):
441
- layer_files.pop(_bad, None)
442
- if pattern_contradicted(pattern_name, layer_files, file_paths):
443
+ _arch_findings = reconcile_architecture_evidence(
444
+ pattern=pattern_name, layer_files=layer_files, repo_files=file_paths
445
+ )
446
+ # Fail-open guard (gap #2): this headline word has no gap channel. If
447
+ # the coherence/split check could not run, do not headline an
448
+ # unverified pattern — skip it, exactly as a contradiction would.
449
+ if has_execution_error(_arch_findings):
450
+ continue
451
+ _split_contradicted = False
452
+ for _f in _arch_findings:
453
+ if _f.rule == "composition_runtime_split":
454
+ _split_contradicted = True
455
+ else:
456
+ for _bad in _f.symbols:
457
+ layer_files.pop(_bad, None)
458
+ if _split_contradicted:
443
459
  continue
444
460
  # A pattern must show its defining trait here too, or the headline
445
461
  # contradicts the architecture prose that already enforces this.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 2.5.11
3
+ Version: 2.5.13
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
@@ -1,18 +1,18 @@
1
- sourcecode/__init__.py,sha256=dNBAjvpTNkiFYDXqXdJjx0saZlNREwHXUuF1drsfGuo,309
1
+ sourcecode/__init__.py,sha256=M8eYjrmWCXZgccRJ042BhY9QS6VmNX4SDwD_gMX9cvo,309
2
2
  sourcecode/adaptive_scanner.py,sha256=XffluXKzJUXrMtjEiAOnSNPZnztdIcts17T9ouHeID0,10521
3
3
  sourcecode/archetype.py,sha256=d7yoN6Tj4OBj-VgSMPEKg4WF9H8FypnZ5A6AkUh5oIE,34484
4
- sourcecode/architecture_analyzer.py,sha256=Bv6OYeAdK88PvHZuryO6CkR6NLJMUqvLy9WkGsw1Fa4,59072
5
- sourcecode/architecture_summary.py,sha256=oBkjx7hZxmpdKLt4EracjRKHJFOGuPTd6SadTyPxY7Q,29568
4
+ sourcecode/architecture_analyzer.py,sha256=GFc4ek-s1IHWM7pl-0L32WahZ93AmDgrAMcOuBKA5Dk,61463
5
+ sourcecode/architecture_summary.py,sha256=BVVRHd952cjRhjHnR6CPrvKgaa-tdM16l-pBi1yDCPs,32395
6
6
  sourcecode/ast_extractor.py,sha256=FEsYTsMCXbJfSYQZRk1k-I-PjUNGnKqO1IxMftsgA5U,51351
7
7
  sourcecode/cache.py,sha256=1V3vsaODAa2UBJAC0xpvxpmRdriCezQx5Q8JCcfgziE,31892
8
8
  sourcecode/call_surface.py,sha256=fiqYfHooxN1fX9oQoysq1LS3LoZcobhyUNjAGEZKpwk,4148
9
9
  sourcecode/caller_metrics.py,sha256=HG8RCOkpzzsEGdnMRob6byrwjoj5__t0TEsBj4yR7ks,2574
10
10
  sourcecode/canonical_ir.py,sha256=5hLAhhSpXNOdZAvPt-J-rXccJE6M3A6gVhmhU0Tym6Y,29269
11
11
  sourcecode/cir_graphs.py,sha256=9G0HHj1kw2325IDyzo2OpX73BNswEckecf4MZUXB4JM,12078
12
- sourcecode/classifier.py,sha256=9olDN5joeKHcwG9vf73X-t_son16hLVNib0rBbqk1vc,18865
12
+ sourcecode/classifier.py,sha256=rkMapdklDuIv3rXrNEwwL65UldkvTn8VuKa73GPoSDo,18849
13
13
  sourcecode/cli.py,sha256=mlMgyRukRoHXYdAYHBN4z06LszwdRo0QLdhs0KYtup8,323804
14
14
  sourcecode/code_notes_analyzer.py,sha256=EJemNCNc9Dn-1RZYu-aNbK0ELzmsyC4s6FdHi3XyNEI,9392
15
- sourcecode/confidence_analyzer.py,sha256=KCRg8B4WH15LpOB7oYhE4dD5nou9Xi48RT3vpaO6Ar4,20868
15
+ sourcecode/confidence_analyzer.py,sha256=vnbPI-20FnHdjO6STxHW8fbaxmB4A7y58io63ibFZjc,21586
16
16
  sourcecode/context_cache.py,sha256=maws79rLKDwyZ7c9Q2LwlIxqZHRFlaKzErtO3mglWoA,25036
17
17
  sourcecode/context_graph.py,sha256=WFtUvfxoE8XtQxtRJLvaF4SJuS00ICjNoTk0sH2_V5U,44454
18
18
  sourcecode/context_scorer.py,sha256=QpChSpsmaAYz91rXA4Ue5xzQmNz_ZboZN09YOHScq1U,14679
@@ -45,18 +45,18 @@ sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7
45
45
  sourcecode/migrate_check.py,sha256=HJhvHuvXDGTJSRQU9_AMx0L94h2TImC1DCGs3m3RTu0,108199
46
46
  sourcecode/openapi_surface.py,sha256=BTt0K-woZbkbWTN77IkqeBm_Okag9owR0848fmot8sk,16207
47
47
  sourcecode/output_budget.py,sha256=Js9yUlfQtPhqBl9R6wn_9UHVjjJc3GtLcqyfjf5t50Q,9869
48
- sourcecode/path_filters.py,sha256=T1y0SJsmUJBju2Hh_-z40kURAowUEq42FpPgWLbOaY0,6931
48
+ sourcecode/path_filters.py,sha256=809P9hj_QChNLg8wk4HuKcFUZmnDKNz0sKIMbmt_FNE,9138
49
49
  sourcecode/pr_comment_renderer.py,sha256=KmcjMruhR44gjzMDJwjBSkWP9QEvh8xWBLyxzxoRbj0,14542
50
50
  sourcecode/pr_impact.py,sha256=dCDVw83EDbyVf6F9ZmEQmsFz8ruVH7d4mpeKQCIZHM0,16805
51
- sourcecode/prepare_context.py,sha256=PYygAKL_jwz8gGElgM9mroUSih18UKSSjv08ndfl4aA,222877
51
+ sourcecode/prepare_context.py,sha256=-Xu33qCETuqw-OFfT3ukvAtxde7krCjQpn7wR3pbdOg,224290
52
52
  sourcecode/progress.py,sha256=qn30sWaHOkjTgXsSBmiPkz7Rsbwc5oSlIe6JNEMYp_k,3149
53
53
  sourcecode/ranking_engine.py,sha256=ZAucq_YX2KkWUuAZf4P0lhtQ_38vEFnUhuGtSZd1S0E,12970
54
- sourcecode/reconciliation.py,sha256=NSGWsRQ-5l50aO_6YErDlu1kEbQB8zDtVf58h_Ro15c,26984
54
+ sourcecode/reconciliation.py,sha256=GU-1PTcVr8zcbtC7BASfpHcZndP9AdboXPNQiBc0fzo,34251
55
55
  sourcecode/redactor.py,sha256=SB4hwIvg8h-hvcqKcDWaZvA-aSyn-at-BIRwa0tUv5E,3227
56
56
  sourcecode/relevance_scorer.py,sha256=0AgEt4KrV73nioMqBgjhGjtY7L2C7L7cSyKtj3IKcrw,9408
57
57
  sourcecode/rename_refactor.py,sha256=h6dNFlB9aZ_3q6heeHBkgXQeXaT03nvPSsYH6P8qxFg,12965
58
58
  sourcecode/repo_classifier.py,sha256=FG1vaWKdWXsWdl-S8hjVMiTqcwgaRXkDyvK4rPcOGtQ,22681
59
- sourcecode/repository_ir.py,sha256=F6lRgPoP8oTtnGpRgjoLw-dlxZRXH7XOcehtcNwufEY,309550
59
+ sourcecode/repository_ir.py,sha256=bNSUKkWQEvFQ2wSZPRdn_9cZfx6TRcPzGtM2EgMm49M,311400
60
60
  sourcecode/ris.py,sha256=Hw8TakTQ6hku-Abf2k8954NwkrH_sP73_8wVH8x5khc,22079
61
61
  sourcecode/runtime_classifier.py,sha256=uTAD6BDCiBLUZEDRfqk718kM4RTT_vAbfkcOI2_Xx58,18432
62
62
  sourcecode/scanner.py,sha256=WdOQ78mMzjR1NjmKTlbxdgwinnCTfAhxCVLBEFQiFHU,8899
@@ -70,12 +70,12 @@ sourcecode/semantic_services.py,sha256=nbUuPv-F01USTt_9CHT8iy_ucCIw3fz4W3Aquea_p
70
70
  sourcecode/serializer.py,sha256=FmQo7eojA_z3ptLW-Vgze5mrEJ6FabcZqiwbQWWTgFI,130056
71
71
  sourcecode/spring_event_topology.py,sha256=5_ON_21Le5zbG-1GRc5GLIi5HJfy_QjcXLVPC5WeUGQ,18055
72
72
  sourcecode/spring_findings.py,sha256=EX7kLZLN74CFyR9iZPm3CI115BfFKNd4WRPuErNljZM,5729
73
- sourcecode/spring_impact.py,sha256=ayZtHZ2YCRSlZpKBACoM9M7tW0K1W_C0r8SGj3R-4vU,70603
73
+ sourcecode/spring_impact.py,sha256=MkUI27OCOhkjvBoA4f9X1eDBIY0SmyPuLVCWIfZgOcE,72430
74
74
  sourcecode/spring_model.py,sha256=zOAgFmrRbG4a6KLm1TJl55aWMyPNsz3OS3FSczqPG6A,16594
75
75
  sourcecode/spring_security_audit.py,sha256=Rk-aSohezdc7YDYbSoJquVnwpkDB8ty1BCD-4Hc4R5A,22832
76
76
  sourcecode/spring_semantic.py,sha256=jteQ1PkY9ArFJv0embg_jBIdbOxqrk9mQ2Xz8OF_FKA,14214
77
77
  sourcecode/spring_tx_analyzer.py,sha256=QMXkxa_hrqDVN3D8WJsd0nT7vfdZCT1E8ZWNoFFbvgU,34692
78
- sourcecode/summarizer.py,sha256=E70QILQ8cCNIH485c7lVMv5dXOpUn8qx1habi1_cn6o,26340
78
+ sourcecode/summarizer.py,sha256=SWj-y5BJ_Qw89ljLg-KXaLsvU87usOw-raTckPutCk4,26976
79
79
  sourcecode/tree_utils.py,sha256=8GAkIfQAsvtEudIeW1l4ooH_oRtrWR8cpJQJsEa_Pfw,2093
80
80
  sourcecode/type_usage_surface.py,sha256=51IrKRQoIoRnlsiDjHnqpJBn2rc6E59aRhgS0HTzAF0,4428
81
81
  sourcecode/validation_inference.py,sha256=UtmYv-d4TEzT1ihGEipAhtXDrwq0XtVtjMpb3i7NsNc,10815
@@ -137,8 +137,8 @@ sourcecode/telemetry/consent.py,sha256=LIAO9ohJZF8OuZwM4u1VWtALlYfTCCKq4wV3Vwc7i
137
137
  sourcecode/telemetry/events.py,sha256=LtzYfaX9Ilckj5PTvAcTpDa9mLqDsYPDUiDkRa58piY,2580
138
138
  sourcecode/telemetry/filters.py,sha256=NHa5T-6DaZduQPFuC34jOqHWQgSizM-Ygq8aZ4j19ng,5834
139
139
  sourcecode/telemetry/transport.py,sha256=4gGHsq0WeY9VywEZXA3vUxykfiYnw9uuqfjAAec7F8o,1681
140
- sourcecode-2.5.11.dist-info/METADATA,sha256=6j_zA5-pJ01V4qnmyhPDb3FtyvH5bqAccNhHVP6tO5s,10852
141
- sourcecode-2.5.11.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
142
- sourcecode-2.5.11.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
143
- sourcecode-2.5.11.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
144
- sourcecode-2.5.11.dist-info/RECORD,,
140
+ sourcecode-2.5.13.dist-info/METADATA,sha256=hAqH0Dxo8V6o4vDdpqalzX3njpQNvHIO8-t5zglFo68,10852
141
+ sourcecode-2.5.13.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
142
+ sourcecode-2.5.13.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
143
+ sourcecode-2.5.13.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
144
+ sourcecode-2.5.13.dist-info/RECORD,,