2"""Create a commit-scoped documentation certification after strict validation."""
4from __future__
import annotations
11from pathlib
import Path
14REPO_ROOT = Path(__file__).resolve().parents[2]
17def run(command: list[str], label: str) ->
None:
19 @brief Run one certification gate and stream its output.
20 @param[in] command Executable and arguments for the gate.
21 @param[in] label Human-readable gate label.
25 print(f
"==> {label}", flush=
True)
26 completed = subprocess.run(command, cwd=REPO_ROOT, check=
False)
27 if completed.returncode != 0:
28 raise RuntimeError(f
"{label} failed with exit code {completed.returncode}.")
33 @brief Return trimmed output from one Git command.
34 @param[in] args Arguments passed after `git`.
35 @return Decoded, trimmed Git stdout.
38 return subprocess.check_output([
"git", *args], cwd=REPO_ROOT, text=
True).strip()
43 @brief Require that HEAD names a clean, commit-scoped source revision.
44 @return Full SHA of the clean HEAD revision.
49 "Documentation certification requires a clean working tree so its result can be "
50 "claimed for one commit. Commit or stash changes, then rerun."
57 @brief Fail certification when Doxygen reports a warning.
61 warning_log = REPO_ROOT /
"logs" /
"doxygen.warnings"
62 warnings = warning_log.read_text(encoding=
"utf-8").strip()
if warning_log.exists()
else ""
65 "Doxygen emitted warnings; documentation cannot be certified.\n" + warnings
71 @brief Describe how much of the invariant surface has an automated checker.
73 Most invariant contracts are tracked rather than enforced, so the certificate must
74 not imply that every guarantee is verified.
75 @return Human-readable contract scope statement.
77 path = REPO_ROOT /
"tests" /
"tooling" /
"contract_registry.json"
79 contracts = json.loads(path.read_text(encoding=
"utf-8"))[
"contracts"]
80 except (OSError, ValueError, KeyError):
81 return "scope unknown (contract registry unreadable)"
82 enforced = [c[
"id"]
for c
in contracts
if c[
"status"] ==
"enforced"]
83 other = [c
for c
in contracts
if c[
"status"] !=
"enforced"]
85 f
"{len(enforced)} enforced and verified; "
86 f
"{len(other)} TRACKED OR PLANNED without an automated checker"
92 @brief Describe how much of the capability surface has enforced documentation coverage.
94 The certificate must not imply that every capability is documented while families
95 are still being backfilled, so it names what is enforced and what is advisory.
96 @return Human-readable coverage scope statement.
98 registry_path = REPO_ROOT /
"tests" /
"tooling" /
"capability_families.json"
100 families = json.loads(registry_path.read_text(encoding=
"utf-8"))[
"families"]
101 except (OSError, ValueError, KeyError):
102 return "scope unknown (capability registry unreadable)"
103 enforced = sorted(f[
"id"]
for f
in families
if f.get(
"coverage_enforced"))
104 advisory = sorted(f[
"id"]
for f
in families
if not f.get(
"coverage_enforced"))
106 return f
"enforced for all {len(enforced)} registered families"
108 f
"enforced for {', '.join(enforced) or 'no families'}; "
109 f
"ADVISORY ONLY for {', '.join(advisory)} (backfill pending)"
115 @brief Describe the four freshness states separately.
117 @details Mechanical verification, a current fingerprint, a soft suspicion, and a
118 human review are four different claims. The certificate must not blur
119 them: a matching digest records that a review happened, never that the
120 prose it covers is correct.
121 @return Human-readable freshness statement.
123 path = REPO_ROOT /
"tests" /
"tooling" /
"freshness_manifest.json"
125 surfaces = json.loads(path.read_text(encoding=
"utf-8"))[
"surfaces"]
126 except (OSError, ValueError, KeyError):
127 return "scope unknown (freshness manifest unreadable)"
130 def digest(paths: list) -> str:
132 @brief Digest matching audit_freshness.digest_of.
133 @param[in] paths Repository-relative paths.
134 @return Hex digest with algorithm prefix.
136 accumulator = hashlib.sha256()
137 for relative
in sorted(paths):
138 accumulator.update(relative.encode(
"utf-8"))
139 accumulator.update(b
"\0")
140 accumulator.update((REPO_ROOT / relative).read_bytes())
141 accumulator.update(b
"\0")
142 return f
"sha256:{accumulator.hexdigest()}"
144 hard_current = soft_current = suspect = never = 0
145 for surface
in surfaces:
146 paths = ([surface[
"artifact"]]
if surface[
"tier"] ==
"hard"
147 else list(surface.get(
"watched_paths", [])))
148 attested = surface.get(
"attested_digest")
149 if attested
in (
None,
"unattested"):
151 elif attested == digest(paths):
152 if surface[
"tier"] ==
"hard":
159 f
"{hard_current} hard-current (normalized artifact unchanged since review); "
160 f
"{soft_current} soft-current (watched sources unchanged since review); "
161 f
"{suspect} SUSPECT; {never} NEVER ATTESTED against their sources"
167 @brief Write a reproducible certification record for one validated revision.
168 @param[in] sha Full Git revision validated by the certification gates.
169 @param[in] runtime_checked Whether the PETSc/MPI runtime suite was included.
170 @return Path of the generated certificate.
173 certificate = REPO_ROOT /
"logs" / f
"documentation-certificate-{sha}.md"
177 timestamp = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat()
179 "`make check-full` (including `unit-io` startup-banner and `unit-logging` contracts)"
184 "This full certification is authoritative only for the commit named above. "
185 "Any source, configuration, example, or documentation change requires a new certification.\n"
187 else "This structural certification is authoritative only for the commit named above and "
188 "does not certify PETSc/MPI runtime behavior. A pre-push policy may reuse a recent full "
189 "certificate only when it establishes that runtime-relevant files are unchanged.\n"
191 certificate.write_text(
192 "# PICurv Documentation Certification\n\n"
193 f
"- Certified commit: `{sha}`\n"
194 f
"- Certified at (UTC): `{timestamp}`\n"
195 "- Portable shared agent instructions and skill parity: passed\n"
196 "- Markdown links (all tracked and non-ignored Markdown): passed\n"
197 "- Field catalog identity and layout: passed\n"
198 "- Public-header and implementation comment audit: passed\n"
199 "- User-facing C/Python reporting audit: passed\n"
200 "- Starter template, example, and configuration audit: passed\n"
201 "- Generic documentation-expansion debris audit: passed\n"
202 f
"- Capability parity (full source chain): passed\n"
203 f
"- Invariant contract registry: {contract_scope}\n"
204 f
"- Tier-2 documentation coverage: {coverage_scope}\n"
205 "- Page-type coverage (every published page typed): passed\n"
206 "- Subsystem lifecycle obligations: passed\n"
207 f
"- Documentation freshness: {freshness}\n"
208 "- PETSc option-ingress manifest audit: passed\n"
209 "- Example/template and configuration regression suite: passed\n"
210 "- Doxygen HTML build: passed with zero warnings\n"
211 "- Published-site URLs and navigation (validated against generated HTML): passed\n"
212 f
"- PETSc/MPI runtime validation: {runtime_gate}\n\n"
214 +
"\nThese lines record four different kinds of claim and must not be conflated. A "
215 "gate that\npassed is **mechanically verified**. A hard-current surface means the "
216 "normalized artifact a\npage describes has not changed since someone last compared "
217 "them - not that the page is\ncorrect. A soft suspicion is advisory. A never-attested "
218 "surface means no such comparison has\nhappened at all. **Scientific and visual review "
219 "by the repository owner is a separate,\nhuman act that no gate here performs.**\n",
227 @brief Parse documentation-certification command-line arguments.
228 @param[in] argv Arguments excluding the executable name.
229 @return Parsed command-line namespace.
232 parser = argparse.ArgumentParser(
233 description=
"Validate and certify PICurv documentation for the current clean Git commit."
239 "also run the PETSc/MPI `make check-full` runtime validation gate "
240 "(including startup-banner and logging contracts)"
246 help=
"run the gates without writing logs/documentation-certificate-<sha>.md",
248 return parser.parse_args(argv)
251def main(argv: list[str] |
None =
None) -> int:
253 @brief Run the commit-scoped documentation certification workflow.
254 @param[in] argv Optional command-line arguments excluding the executable name.
255 @return Process status code.
258 args =
parse_args(sys.argv[1:]
if argv
is None else argv)
261 run([sys.executable,
"tests/tooling/audit_agent_setup.py"],
"portable agent setup")
262 run([sys.executable,
"tests/tooling/check_markdown_links.py"],
"Markdown links")
263 run([sys.executable,
"tests/tooling/audit_function_docs.py"],
"function documentation")
264 run([sys.executable,
"tests/tooling/audit_user_facing_reporting.py"],
"user-facing reporting")
265 run([sys.executable,
"tests/tooling/audit_starter_content.py"],
"starter templates and configuration")
266 run([sys.executable,
"tests/tooling/audit_generic_expansion.py"],
"generic documentation-expansion debris")
267 run([sys.executable,
"tests/tooling/audit_path_literals.py"],
"unmanaged run-path literals")
268 run([sys.executable,
"tests/tooling/audit_capability_coverage.py"],
"capability parity and documentation coverage")
269 run([sys.executable,
"tests/tooling/audit_family_census.py"],
"capability family census")
270 run([sys.executable,
"tests/tooling/generate_cli_reference.py",
"--check"],
"generated CLI reference")
271 run([sys.executable,
"tests/tooling/audit_ingress.py"],
"option-ingress manifest")
278 "tests/test_repo_consistency.py",
279 "tests/test_config_regressions.py",
280 "tests/test_capability_tooling.py",
282 "example/template, configuration, and capability-tooling validation",
284 run([
"make",
"--no-print-directory",
"build-docs"],
"Doxygen HTML build")
288 run([sys.executable,
"tests/tooling/audit_docs_site.py"],
"published-site URLs and navigation")
289 run([sys.executable,
"tests/tooling/audit_page_types.py"],
"page-type coverage")
290 run([sys.executable,
"tests/tooling/audit_field_catalog.py"],
291 "field catalog identity and layout")
292 run([sys.executable,
"tests/tooling/audit_inline_choices.py"],
293 "named public choice sets")
294 run([sys.executable,
"tests/tooling/audit_subsystem_lifecycle.py"],
295 "subsystem lifecycle obligations")
296 run([sys.executable,
"tests/tooling/audit_freshness.py"],
297 "documentation freshness (hard blocking, soft advisory)")
298 run([sys.executable,
"tests/tooling/audit_contracts.py"],
"invariant contract registry")
301 [
"make",
"--no-print-directory",
"check-full"],
302 "PETSc/MPI runtime validation (including startup-banner and logging contracts)",
304 if not args.no_certificate:
306 certification_kind =
"full" if args.runtime
else "structural"
307 print(f
"{certification_kind.capitalize()} documentation certification through commit {sha}: {certificate}")
309 except (RuntimeError, subprocess.CalledProcessError)
as error:
310 print(f
"Documentation certification failed: {error}", file=sys.stderr)
314if __name__ ==
"__main__":
315 raise SystemExit(
main())
None run(list[str] command, str label)
Run one certification gate and stream its output.
None ensure_empty_doxygen_warning_log()
Fail certification when Doxygen reports a warning.
str require_clean_revision()
Require that HEAD names a clean, commit-scoped source revision.
argparse.Namespace parse_args(list[str] argv)
Parse documentation-certification command-line arguments.
str invariant_contract_scope()
Describe how much of the invariant surface has an automated checker.
str freshness_scope()
Describe the four freshness states separately.
int main(list[str]|None argv=None)
Run the commit-scoped documentation certification workflow.
Path write_certificate(str sha, bool runtime_checked)
Write a reproducible certification record for one validated revision.
str capability_coverage_scope()
Describe how much of the capability surface has enforced documentation coverage.
str git_output(*str args)
Return trimmed output from one Git command.
Head of a generic C-style linked list.