check.py
python
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