2026-05-10 — Phase 8: server-side governance audit (MR-4, MR-7, MR-10)
Date: 2026-05-10 Status: closed Supersedes: none Superseded-by: none — current
Context
Section titled “Context”petrova.governance.audit was a v0 stub returning status: "not-run" with the note that real MR audits “run via the petrova CLI; server-side audit is v1.” The dashboard’s /console/governance consequently rendered an empty page until today’s interim fix that grouped registry+state by phase. The MR rules themselves are mechanically encoded in core/templates/.github/workflows/docs-invariants.yml — every consumer repo runs them in CI, but the control plane had no aggregate view.
Decision
Section titled “Decision”Implement a server-side AuditSource in host/src/sources/audit.ts that walks each registered repo’s docs/decisions/ and docs/findings/ via Octokit (App auth, same path as GithubEvaSource and ActsSource) and runs three MR checks per repo. Results are returned by petrova.governance.audit as one row per (slug, mr). Dashboard renders a repo × MR grid with a per-finding detail table.
Initial check set:
| MR | What is checked |
|---|---|
| MR-4 | Every docs/decisions/*.md matches YYYY-MM-DD-<slug>.md; every docs/findings/*.md matches YYYYMMDD[-HHMM]-<slug>.md. |
| MR-7 | Every decision doc with **Status:** superseded carries a non-empty **Superseded-by:** link. |
| MR-10 | Every phase-N-(close|complete) decision has a corresponding verification-round artefact in docs/findings/. |
Status values: pass, warn (non-blocking, e.g. MR-10’s heuristic miss), fail (blocking, e.g. MR-4 filename violation), skip (couldn’t run — repo unreachable, no docs dir).
Alternatives considered
Section titled “Alternatives considered”- Lift the consumer-side workflow into the control plane verbatim (Python validators in
docs-invariants.yml). Rejected: that workflow runs over a checked-out repo with all branches, which doesn’t fit the host’s per-request remote-fetch model. Re-implementing the same rules in TypeScript over Octokit reads is cheaper and matches the rest of the host (GithubEvaSource,ActsSource). - Fold audit results into
petrova.context. Rejected: audit is expensive (multiple Octokit calls per repo, body fetches for MR-7); the/contextbundle is on the request-latency-critical path. Audit gets its own tool with a 5-minute in-memory cache.
Consequences
Section titled “Consequences”For code:
- New
host/src/sources/audit.tswith the three checks and a per-call cache. governance.auditrewritten — now returns{scanned_at, rows[]}instead of the v0 single-row stub.host/tests/tools/governance.test.tsupdated to match the new shape./console/governancerebuilt as a repo × MR matrix + findings table.
For docs:
- This doc.
For in-flight phases:
- Phase 6 (state freshness) is complementary, not blocking.
For invariants:
- No MR-N changes. The audit enforces MR-4/7/10 from the control plane; the rules themselves live in
META-RULES.md.
References
Section titled “References”core/templates/.github/workflows/docs-invariants.yml— the consumer-side reference implementation.host/src/sources/audit.ts— the server-side translation.- Earlier governance dashboard fix:
1e2c53b.
Sign-off
Section titled “Sign-off”- Subagent: Claude Code (
maindirect-push under standing approval) - Human: alex@devarno.com — 2026-05-10