feat(three-stage-scan): emit ordered complexity map artifact

This commit is contained in:
Marius Ciepluch
2026-09-03 20:16:23 +02:00
parent 3d643040bd
commit b05fbbc3a5
4 changed files with 256 additions and 3 deletions
+10 -2
View File
@@ -1,5 +1,13 @@
"""Heuristic complexity signals for the stage-2 map."""
"""Heuristic complexity map for stage 2."""
from .map import ComplexityMap, Hotspot, build_complexity_map, write_complexity_map
from .signals import ComplexitySignals, collect_signals
__all__ = ["ComplexitySignals", "collect_signals"]
__all__ = [
"ComplexityMap",
"ComplexitySignals",
"Hotspot",
"build_complexity_map",
"collect_signals",
"write_complexity_map",
]
+112
View File
@@ -0,0 +1,112 @@
from __future__ import annotations
import json
from collections.abc import Iterable, Mapping, Sequence
from dataclasses import asdict, dataclass
from pathlib import Path
from ..findings import StaticFinding
from ..preprocess import FileTarget
from .signals import ComplexitySignals, collect_signals
# Score cutoffs for hotspot bands; inventory density is not the rank driver.
_HIGH_SCORE = 6.0
_MEDIUM_SCORE = 3.0
@dataclass(frozen=True)
class TestHint:
path: str
function_name: str | None
gap: str
test_kind: str | None
reason: str
@dataclass(frozen=True)
class Hotspot:
path: str
function_name: str | None
score: float
band: str # high | medium | low
signals: dict[str, float | int | bool | str]
rationale: str
test_hint: TestHint | None
inventory_finding_ids: tuple[str, ...]
@dataclass(frozen=True)
class ComplexityMap:
hotspots: tuple[Hotspot, ...]
heuristic_only: bool
def build_complexity_map(
targets: Sequence[FileTarget],
findings: Sequence[StaticFinding],
output_path: Path,
*,
repo_files: Iterable[str] = (),
sources: Mapping[str, str] | None = None,
) -> ComplexityMap:
collected = collect_signals(targets, findings, repo_files=repo_files, sources=sources)
ids_by_path = _inventory_ids_by_path(findings)
hotspots = tuple(_hotspot_from_signals(item, ids_by_path.get(item.path, ())) for item in collected)
complexity_map = ComplexityMap(hotspots=hotspots, heuristic_only=True)
write_complexity_map(complexity_map, output_path)
return complexity_map
def write_complexity_map(complexity_map: ComplexityMap, path: Path) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(asdict(complexity_map), indent=2, sort_keys=True) + "\n")
def _hotspot_from_signals(item: ComplexitySignals, inventory_finding_ids: tuple[str, ...]) -> Hotspot:
band = _band(item.score)
return Hotspot(
path=item.path,
function_name=item.function_name,
score=item.score,
band=band,
signals=_signal_payload(item),
rationale=_rationale(item, band),
test_hint=None,
inventory_finding_ids=inventory_finding_ids,
)
def _band(score: float) -> str:
if score >= _HIGH_SCORE:
return "high"
if score >= _MEDIUM_SCORE:
return "medium"
return "low"
def _signal_payload(item: ComplexitySignals) -> dict[str, float | int | bool | str]:
return {
"loc": item.loc,
"nesting": item.nesting,
"reachability": item.reachability,
"inventory_hit_count": item.inventory_hit_count,
"has_adjacent_test": item.has_adjacent_test,
"tags": ",".join(item.tags),
}
def _rationale(item: ComplexitySignals, band: str) -> str:
tags = ",".join(item.tags) or "none"
return (
f"{band} band: score {item.score} against cutoffs high>={_HIGH_SCORE}, medium>={_MEDIUM_SCORE}; "
f"loc={item.loc}, nesting={item.nesting}, tags={tags}, reachability={item.reachability}, "
f"inventory_hit_count={item.inventory_hit_count} (density is a feature not the rank driver), "
f"has_adjacent_test={str(item.has_adjacent_test).lower()}."
)
def _inventory_ids_by_path(findings: Sequence[StaticFinding]) -> dict[str, tuple[str, ...]]:
grouped: dict[str, list[str]] = {}
for finding in findings:
grouped.setdefault(finding.path, []).append(finding.finding_id)
return {path: tuple(ids) for path, ids in grouped.items()}