check.py python
86 lines 2.7 KB
Raw
sha256:c07f2f34a0db9f43fe866f157d1935322008545ff8940c06c7c921eb219c55ab NXP-b DONE: independent BV-r2 pass + ISR (SD-17) Human minor ⚠ breaking 2 days ago
1 """Footprint coverage gate — resolve destinations must appear in version.lock (§LT.3.1)."""
2
3 from __future__ import annotations
4
5 from dataclasses import dataclass
6 from pathlib import Path
7
8 from adapters.config import OverseerConfig
9 from cli.footprint import FootprintFile, resolve_footprint
10 from cli.version_lock import LockError, VersionLock, lock_path, read_version_lock
11
12 REMEDIATION = "ok sync"
13
14
15 @dataclass(frozen=True)
16 class FootprintCoverageReport:
17 """Result of comparing resolve_footprint destinations to version.lock paths."""
18
19 state: str # ok | missing_from_lock | not_applicable
20 message: str
21 remediation: str | None
22 missing: tuple[str, ...] = ()
23
24 @property
25 def ok(self) -> bool:
26 return self.state in {"ok", "not_applicable"}
27
28
29 def check_footprint_coverage(
30 repo_root: Path,
31 config: OverseerConfig,
32 *,
33 lock: VersionLock | None = None,
34 rendered: list[FootprintFile] | None = None,
35 kit: Path | None = None,
36 ) -> FootprintCoverageReport:
37 """Return coverage state for resolve destinations vs lock footprint list."""
38 if lock is None:
39 path = lock_path(repo_root)
40 if not path.is_file():
41 return FootprintCoverageReport(
42 state="not_applicable",
43 message="no version.lock yet — coverage not applicable",
44 remediation=None,
45 )
46 try:
47 lock = read_version_lock(path)
48 except LockError as exc:
49 return FootprintCoverageReport(
50 state="not_applicable",
51 message=f"version.lock unreadable — coverage not applicable: {exc}",
52 remediation=None,
53 )
54
55 if not lock.footprint:
56 return FootprintCoverageReport(
57 state="not_applicable",
58 message="version.lock footprint list is empty — coverage not applicable",
59 remediation=None,
60 )
61
62 if rendered is None:
63 rendered = resolve_footprint(config, kit=kit)
64
65 lock_paths = {entry.path for entry in lock.footprint}
66 missing = tuple(
67 sorted(item.destination for item in rendered if item.destination not in lock_paths)
68 )
69
70 if missing:
71 listed = ", ".join(missing)
72 return FootprintCoverageReport(
73 state="missing_from_lock",
74 message=(
75 f"{len(missing)} resolve footprint destination(s) absent from "
76 f"version.lock: {listed}"
77 ),
78 remediation=REMEDIATION,
79 missing=missing,
80 )
81
82 return FootprintCoverageReport(
83 state="ok",
84 message="every resolve footprint destination is declared in version.lock",
85 remediation=None,
86 )
File History 1 commit
sha256:c07f2f34a0db9f43fe866f157d1935322008545ff8940c06c7c921eb219c55ab NXP-b DONE: independent BV-r2 pass + ISR (SD-17) Human minor 2 days ago