2"""Tiered documentation freshness: hard suspicion, soft suspicion, integrity failure.
4A fingerprint answers one question: has the thing a page describes changed since a
5human or agent last compared the page against it? It never claims the page is wrong,
6and matching digests never claim the page is right. It routes review.
9from __future__
import annotations
15from pathlib
import Path
18REPO_ROOT = Path(__file__).resolve().parents[2]
19MANIFEST = REPO_ROOT /
"tests" /
"tooling" /
"freshness_manifest.json"
20PAGE_TYPES = REPO_ROOT /
"tests" /
"tooling" /
"page_types.json"
22VALID_TIERS = (
"hard",
"soft")
23VALID_ENFORCEMENT = (
"blocking",
"report")
24SURFACE_KEYS = {
"id",
"title",
"tier",
"enforcement",
"promotion_reason",
"artifact",
25 "regenerate",
"watched_paths",
"owning_pages",
"dependent_pages",
26 "attested_digest",
"attested_scope",
"note"}
28UNATTESTED =
"unattested"
33 @brief Deterministic digest over an ordered list of files.
35 @details The path is hashed alongside the bytes so that moving content between
36 watched files is itself a change.
37 @param[in] paths Repository-relative paths.
38 @return Hex digest prefixed with its algorithm.
40 accumulator = hashlib.sha256()
41 for relative
in sorted(paths):
42 accumulator.update(relative.encode(
"utf-8"))
43 accumulator.update(b
"\0")
44 accumulator.update((REPO_ROOT / relative).read_bytes())
45 accumulator.update(b
"\0")
46 return f
"sha256:{accumulator.hexdigest()}"
51 @brief The files a surface fingerprints.
52 @param[in] surface One manifest entry.
53 @return Repository-relative paths.
55 if surface[
"tier"] ==
"hard":
56 return [surface[
"artifact"]]
57 return list(surface.get(
"watched_paths", []))
62 @brief Whether a suspicion on this surface blocks.
63 @param[in] surface One manifest entry.
64 @return "blocking" or "report".
66 return surface.get(
"enforcement")
or (
"blocking" if surface[
"tier"] ==
"hard" else "report")
71 @brief Structural checks on the manifest itself.
73 @details A malformed manifest is an integrity failure, not ordinary staleness: it
74 means coverage is broken and the absence of suspicion proves nothing.
75 @param[in] surfaces Manifest entries.
76 @param[in] published Ids of published pages.
77 @return List of integrity failures.
81 for surface
in surfaces:
82 identifier = surface.get(
"id")
84 failures.append(
"a surface declares no id")
86 if identifier
in seen:
87 failures.append(f
"{identifier}: declared more than once")
89 unknown = set(surface) - SURFACE_KEYS
91 failures.append(f
"{identifier}: unknown field(s) {sorted(unknown)}")
92 if surface.get(
"tier")
not in VALID_TIERS:
93 failures.append(f
"{identifier}: tier {surface.get('tier')!r} is not hard or soft")
96 failures.append(f
"{identifier}: enforcement must be blocking or report")
97 if surface[
"tier"] ==
"hard":
98 if not surface.get(
"artifact"):
100 f
"{identifier}: a hard surface must name the normalized artifact it "
103 if not surface.get(
"regenerate"):
105 f
"{identifier}: a hard surface must name the command that regenerates it"
108 if not surface.get(
"watched_paths"):
109 failures.append(f
"{identifier}: a soft surface must watch at least one path")
110 if enforcement_of(surface) ==
"blocking" and not surface.get(
"promotion_reason"):
112 f
"{identifier}: a soft surface promoted to blocking must state why"
114 if not surface.get(
"owning_pages"):
116 f
"{identifier}: no owning page; a fingerprint with nowhere to route its "
117 f
"suspicion cannot ask anyone to review anything"
119 for field
in (
"owning_pages",
"dependent_pages"):
120 for page
in surface.get(field, []):
121 if page
not in published:
122 failures.append(f
"{identifier}: {field} names '{page}', which is not published")
124 if not (REPO_ROOT / relative).is_file():
126 f
"{identifier}: watched path '{relative}' does not exist; coverage is "
127 f
"broken, not merely stale"
132def evaluate(surfaces: list, published: set) -> dict:
134 @brief Classify every surface as current, suspect, or an integrity failure.
135 @param[in] surfaces Manifest entries.
136 @param[in] published Ids of published pages.
137 @return Report with the four classifications and the pages needing review.
140 report = {
"integrity_failures": integrity,
"hard_current": [],
"hard_suspect": [],
141 "soft_current": [],
"soft_suspect": [],
"unattested": []}
144 for surface
in surfaces:
146 attested = surface.get(
"attested_digest")
149 "title": surface.get(
"title", surface[
"id"]),
150 "tier": surface[
"tier"],
152 "current_digest": current,
153 "attested_digest": attested,
154 "pages":
list(surface.get(
"owning_pages", [])) +
155 list(surface.get(
"dependent_pages", [])),
156 "regenerate": surface.get(
"regenerate"),
158 if attested
in (
None, UNATTESTED):
159 report[
"unattested"].append(entry)
160 elif attested == current:
161 report[f
"{surface['tier']}_current"].append(entry)
163 report[f
"{surface['tier']}_suspect"].append(entry)
169 @brief The suspicions that must fail the build.
170 @param[in] report Output of evaluate().
171 @return Entries whose enforcement is blocking.
173 suspects = report[
"hard_suspect"] + report[
"soft_suspect"] + report[
"unattested"]
174 return [entry
for entry
in suspects
if entry[
"enforcement"] ==
"blocking"]
177def attest(surfaces: list, wanted: list) -> list:
179 @brief Record the current digests as reviewed.
180 @param[in] surfaces Manifest entries, mutated in place.
181 @param[in] wanted Surface ids, or ["all"].
182 @return Ids that were re-attested.
185 for surface
in surfaces:
186 if wanted != [
"all"]
and surface[
"id"]
not in wanted:
189 if surface.get(
"attested_digest") != current:
190 surface[
"attested_digest"] = current
191 changed.append(surface[
"id"])
197 @brief Report freshness, or re-attest reviewed surfaces.
198 @return Process status code.
200 parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
201 parser.add_argument(
"--attest", nargs=
"+", metavar=
"ID",
202 help=
"Record current digests as reviewed. Pass 'all' for every surface.")
203 parser.add_argument(
"--json", action=
"store_true", help=
"Emit the report as JSON.")
204 parser.add_argument(
"--page", metavar=
"PAGE_ID",
205 help=
"Report only the surfaces that route review to this page.")
206 arguments = parser.parse_args()
208 document = json.loads(MANIFEST.read_text(encoding=
"utf-8"))
209 surfaces = document[
"surfaces"]
210 published = set(json.loads(PAGE_TYPES.read_text(encoding=
"utf-8"))[
"assignments"])
215 print(
"Cannot attest against a manifest with integrity failures:", file=sys.stderr)
216 for failure
in failures:
217 print(f
" {failure}", file=sys.stderr)
219 changed =
attest(surfaces, arguments.attest)
220 MANIFEST.write_text(json.dumps(document, indent=2) +
"\n", encoding=
"utf-8")
222 print(
"Re-attested: " +
", ".join(changed))
223 print(
"This records that the owning pages were compared against these sources.\n"
224 "It is not a claim that the comparison found nothing - only that it happened.")
226 print(
"Nothing to re-attest; every requested surface already matches.")
229 report =
evaluate(surfaces, published)
231 for key
in (
"hard_current",
"hard_suspect",
"soft_current",
"soft_suspect",
"unattested"):
232 report[key] = [e
for e
in report[key]
if arguments.page
in e[
"pages"]]
234 print(json.dumps(report, indent=2))
237 if report[
"integrity_failures"]:
238 print(
"Freshness integrity failures (coverage is broken):", file=sys.stderr)
239 for failure
in report[
"integrity_failures"]:
240 print(f
" {failure}", file=sys.stderr)
243 for entry
in report[
"hard_suspect"]:
244 print(f
"HARD SUSPECT {entry['id']} - {entry['title']}", file=sys.stderr)
245 print(f
" the normalized artifact changed since it was last reviewed", file=sys.stderr)
246 print(f
" review: {', '.join(entry['pages'])}", file=sys.stderr)
247 print(f
" then: python3 tests/tooling/audit_freshness.py --attest {entry['id']}",
249 for entry
in report[
"soft_suspect"]:
250 stream = sys.stderr
if entry[
"enforcement"] ==
"blocking" else sys.stdout
251 label =
"SOFT SUSPECT (blocking)" if entry[
"enforcement"] ==
"blocking" else "soft suspect"
252 print(f
"{label} {entry['id']} - {entry['title']}", file=stream)
253 print(f
" watched sources changed; no semantic extractor exists for this surface",
255 print(f
" review: {', '.join(entry['pages'])}", file=stream)
256 for entry
in report[
"unattested"]:
257 blocking_entry = entry[
"enforcement"] ==
"blocking"
258 stream = sys.stderr
if blocking_entry
else sys.stdout
259 label =
"UNATTESTED (blocking)" if blocking_entry
else "unattested"
260 print(f
"{label} {entry['id']} - {entry['title']}: never reviewed against its "
261 f
"sources", file=stream)
262 print(f
" review: {', '.join(entry['pages'])}", file=stream)
264 counts = (f
"{len(report['hard_current'])} hard-current, {len(report['hard_suspect'])} "
265 f
"hard-suspect, {len(report['soft_current'])} soft-current, "
266 f
"{len(report['soft_suspect'])} soft-suspect, "
267 f
"{len(report['unattested'])} never attested")
270 print(f
"\nFreshness: {counts}. Blocking on {len(blocking)} surface(s).", file=sys.stderr)
271 print(
"A suspicion is a request to review, not a claim the page is wrong. Compare the\n"
272 "page with its sources, correct what drifted, then re-attest.", file=sys.stderr)
274 print(f
"Freshness: {counts}.")
275 if report[
"soft_suspect"]
or report[
"unattested"]:
276 print(
"Advisory entries above do not block. A never-attested surface means no page has\n"
277 "yet been compared against it - an honest gap, not a failure.")
281if __name__ ==
"__main__":
282 raise SystemExit(
main())
int main()
Report freshness, or re-attest reviewed surfaces.
str digest_of(list paths)
Deterministic digest over an ordered list of files.
list surface_paths(dict surface)
The files a surface fingerprints.
list blocking_entries(dict report)
The suspicions that must fail the build.
list validate_manifest(list surfaces, set published)
Structural checks on the manifest itself.
dict evaluate(list surfaces, set published)
Classify every surface as current, suspect, or an integrity failure.
str enforcement_of(dict surface)
Whether a suspicion on this surface blocks.
list attest(list surfaces, list wanted)
Record the current digests as reviewed.
Head of a generic C-style linked list.