8FS-510 — incremental lint cache. Content-hash keyed so unchanged files
9are not re-linted on subsequent runs.
13- Compute deterministic content hashes for source files.
14- Store and retrieve per-file lint results from a JSON cache file.
15- Invalidate entries when file content, active rules, or OCT version change.
16- Thread-safe read/write via ``threading.Lock``.
22 L3 — cache hits/misses
23 L4 — cache I/O details
27- Cache file lives at ``.option_c/cache/lint-cache.json``.
28- Corrupt or missing cache file is silently replaced (no crash).
29- Cache is never authoritative — a ``--no-cache`` flag bypasses reads.
32from __future__
import annotations
37from dataclasses
import dataclass, field, asdict
38from pathlib
import Path
42_SCHEMA_VERSION =
"1.0"
46 """Return a SHA-256 hex digest of *text*."""
47 return hashlib.sha256(text.encode(
"utf-8")).hexdigest()
51 """Return a SHA-256 hex digest of the sorted active-rules dict."""
52 canonical = json.dumps(active_rules, sort_keys=
True)
53 return hashlib.sha256(canonical.encode(
"utf-8")).hexdigest()
58 """One cached lint result for a single file."""
61 rule_results: dict[str, bool]
62 json_detail: dict[str, Any]
68 """Thread-safe, content-addressed lint result cache.
70 Loads from *cache_path* on init; call :meth:`save` to persist.
73 def __init__(self, cache_path: Path, oct_version: str) ->
None:
81 if not self.
_path.is_file():
84 raw = json.loads(self.
_path.read_text(encoding=
"utf-8"))
85 if not isinstance(raw, dict):
87 if raw.get(
"_schema_version") != _SCHEMA_VERSION:
91 entries = raw.get(
"entries")
92 if isinstance(entries, dict):
94 except (json.JSONDecodeError, OSError):
97 def get(self, file_hash: str, active_rules_hash: str) -> LintCacheEntry |
None:
98 """Return cached entry if content + rules match, else ``None``."""
100 key = f
"{file_hash}:{active_rules_hash}"
104 if entry.get(
"content_hash") != file_hash:
107 content_hash=entry[
"content_hash"],
108 rule_results=entry[
"rule_results"],
109 json_detail=entry[
"json_detail"],
110 oct_version=entry[
"oct_version"],
111 timestamp=entry[
"timestamp"],
117 active_rules_hash: str,
118 entry: LintCacheEntry,
120 """Store a lint result in the cache."""
122 key = f
"{file_hash}:{active_rules_hash}"
126 """Write the cache to disk. Creates parent directories if needed."""
128 self.
_path.parent.mkdir(parents=
True, exist_ok=
True)
130 "_schema_version": _SCHEMA_VERSION,
134 self.
_path.write_text(
135 json.dumps(payload, indent=2), encoding=
"utf-8",
140 """Number of cached entries (for diagnostics)."""
LintCacheEntry|None get(self, str file_hash, str active_rules_hash)
None put(self, str file_hash, str active_rules_hash, LintCacheEntry entry)
None __init__(self, Path cache_path, str oct_version)
str content_hash(str text)
str rules_hash(dict[str, bool] active_rules)