Skip to content

Commit 040bb60

Browse files
committed
ci(governance): R5 — hard gate against doc canonical-reference drift
Encodes the defer-to-canonical convention introduced by #169 / #170 as a hard CI gate so the cleanup doesn't quietly come undone. What the rule does ------------------ Two sub-checks against a fixed list of 14 load-bearing top-level docs (README.{md,adoc}, EXPLAINME.adoc, CLAUDE.md, CONTRIBUTING.{md,adoc}, QUICKSTART-*.adoc, RSR_COMPLIANCE.adoc, SECURITY.md, MAINTAINERS.adoc, AUTHORS.md, CODE_OF_CONDUCT.md): R5a — bare prover counts outside docs/PROVER_COUNT.md Matches patterns the #169/#170 cleanup had to chase across five mutually-disagreeing surfaces: "<N> prover backends?", "<N>-prover core", "<N> core (backends?|provers?)", "<N> (backends?|provers?) (operational|advertised|implemented|available|across|total)", "<N> ProverKind variants?", "<N> backend implementation files?", "<N>/<M> (backends?|provers?)" Each alternation is anchored with (^|[[:space:]]) so neighbours like `Tier-1`, `v1.2.3`, `port 8081` don't false-positive. R5b — pinned version strings outside CHANGELOG.md / Cargo.toml Matches `Version: 1.2.3`, `**Version**: 1.2.3`, `_Version_= v1.2.3` and the like — the shape that drifted across three files before #169/#170. Avoids generic `version =` in code blocks because the leading `Version` keyword is anchored at line start. Self-tested ----------- - Negative test (synthetic drift) fires both R5a and R5b. - Positive test (current main + this PR's README.adoc:256 fix) is clean: 0 hits on both R5a and R5b. Side fix -------- README.adoc:256 still carried `provers/ # 102 prover backend implementation files (ProverBackend trait)` in the architecture tree diagram — missed by #169 because the regex sweep there was prose-only. Replaced with a pointer to docs/PROVER_COUNT.md, matching the convention. What's out of scope (deliberate) -------------------------------- - Sub-tree historical docs (docs/handover/, docs/decisions/, docs/releases/, docs/reports/, audits/). Owner-managed snapshots, not canonical narrative. - Sub-crate READMEs (crates/*/README.md). Belong to their own cadence; will route through the same convention if/when promoted. - Cargo.toml / Cargo.lock semver pins. Those ARE the canonical version source — the rule explicitly exempts them. - LICENSE / SPDX sweep. Owner-managed (per feedback_echidna_license_docs_mpl_intentional). Promotion path -------------- Lives in a separate workflow file (not appended to governance.yml) so a fail closes one rule, not the wrapper-call to standards. Natural follow-up: once the pattern proves out here, promote into hyperpolymath/standards's governance-reusable.yml as an estate-wide R5; the four other repos in the canonical-reference cohort (proven, ephapax, affinescript, typed-wasm) would benefit immediately. Refs #169, refs #170.
1 parent 92d06ce commit 040bb60

2 files changed

Lines changed: 152 additions & 1 deletion

File tree

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
#
3+
# governance-doc-drift.yml — local-to-echidna governance rule R5.
4+
#
5+
# Companion to the shared `governance.yml` wrapper (which calls
6+
# hyperpolymath/standards/governance-reusable.yml). Adds a hard gate
7+
# specifically for the kind of doc drift that the
8+
# #169 / #170 narrative-coherence PRs cleaned up: bare prover counts
9+
# outside docs/PROVER_COUNT.md and pinned version strings outside
10+
# CHANGELOG.md / Cargo.toml.
11+
#
12+
# Scope is deliberately tight: load-bearing top-level docs only.
13+
# Sub-tree docs (docs/handover/, docs/decisions/, docs/releases/,
14+
# docs/reports/, audits, etc.) are owner-managed historical
15+
# snapshots and not scanned. Sub-crate READMEs likewise belong to
16+
# their own crate-local cadence.
17+
#
18+
# Promoting this rule into hyperpolymath/standards's
19+
# governance-reusable.yml is the natural follow-up once the pattern
20+
# proves out here — keeping it local first means a fail closes one
21+
# repo, not the estate.
22+
23+
name: Governance / Doc drift (R5)
24+
25+
on:
26+
push:
27+
branches: [main, master]
28+
paths:
29+
- '*.md'
30+
- '*.adoc'
31+
- '.github/workflows/governance-doc-drift.yml'
32+
pull_request:
33+
paths:
34+
- '*.md'
35+
- '*.adoc'
36+
- '.github/workflows/governance-doc-drift.yml'
37+
workflow_dispatch:
38+
39+
concurrency:
40+
group: ${{ github.workflow }}-${{ github.ref }}
41+
cancel-in-progress: true
42+
43+
permissions:
44+
contents: read
45+
46+
jobs:
47+
r5-doc-drift:
48+
name: R5 — canonical-reference drift detection
49+
runs-on: ubuntu-latest
50+
steps:
51+
- name: Checkout
52+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
53+
54+
- name: R5a — bare prover counts outside docs/PROVER_COUNT.md
55+
run: |
56+
set -euo pipefail
57+
# Load-bearing narrative-carrying surfaces. Add new entries
58+
# here when promoting a doc into the canonical-reference set.
59+
DOCS=(
60+
README.md README.adoc
61+
EXPLAINME.adoc
62+
CLAUDE.md
63+
CONTRIBUTING.md CONTRIBUTING.adoc
64+
QUICKSTART-USER.adoc QUICKSTART-DEV.adoc QUICKSTART-MAINTAINER.adoc
65+
RSR_COMPLIANCE.adoc
66+
SECURITY.md
67+
MAINTAINERS.adoc
68+
AUTHORS.md
69+
CODE_OF_CONDUCT.md
70+
)
71+
# Patterns that consistently re-introduced drift across the
72+
# #169 / #170 cleanup. Each alternation is anchored so a digit
73+
# following `Tier-`, `v`, `port `, `:` etc. does NOT match —
74+
# only standalone counts trip the rule.
75+
PATTERNS=(
76+
'(^|[[:space:]])[0-9]+[[:space:]]+prover[[:space:]]+backends?\b'
77+
'(^|[[:space:]])[0-9]+[[:space:]]*-[[:space:]]*prover[[:space:]]+core\b'
78+
'(^|[[:space:]])[0-9]+[[:space:]]+core[[:space:]]+(backends?|provers?)\b'
79+
'(^|[[:space:]])[0-9]+[[:space:]]+(backends?|provers?)[[:space:]]+(operational|advertised|implemented|available|across|total)\b'
80+
'(^|[[:space:]])[0-9]+[[:space:]]+ProverKind[[:space:]]+variants?\b'
81+
'(^|[[:space:]])[0-9]+[[:space:]]+backend[[:space:]]+implementation[[:space:]]+files?\b'
82+
'(^|[[:space:]])[0-9]+/[0-9]+[[:space:]]+(backends?|provers?)\b'
83+
)
84+
HITS=0
85+
for doc in "${DOCS[@]}"; do
86+
[ -f "$doc" ] || continue
87+
for pat in "${PATTERNS[@]}"; do
88+
while IFS= read -r line; do
89+
[ -n "$line" ] || continue
90+
echo "::error file=$doc::R5a bare prover count: $line — route to docs/PROVER_COUNT.md (the canonical tier table)"
91+
HITS=$((HITS+1))
92+
done < <(grep -nE "$pat" "$doc" 2>/dev/null || true)
93+
done
94+
done
95+
if [ "$HITS" -gt 0 ]; then
96+
echo ""
97+
echo "::error::R5a tripped on $HITS line(s)."
98+
echo "Fix: replace the bare count with a pointer to docs/PROVER_COUNT.md"
99+
echo "(\"the canonical tier table\"). See PR #169 / #170 for the pattern."
100+
exit 1
101+
fi
102+
echo "✓ R5a clean — no bare prover counts in load-bearing docs."
103+
104+
- name: R5b — pinned version strings outside CHANGELOG.md / Cargo.toml
105+
run: |
106+
set -euo pipefail
107+
DOCS=(
108+
README.md README.adoc
109+
EXPLAINME.adoc
110+
CLAUDE.md
111+
CONTRIBUTING.md CONTRIBUTING.adoc
112+
QUICKSTART-USER.adoc QUICKSTART-DEV.adoc QUICKSTART-MAINTAINER.adoc
113+
RSR_COMPLIANCE.adoc
114+
SECURITY.md
115+
MAINTAINERS.adoc
116+
AUTHORS.md
117+
CODE_OF_CONDUCT.md
118+
)
119+
# Matches `Version: 1.2.3` / `**Version**: 1.2.3` / `_Version_: v1.2.3`
120+
# etc. Not `Version` without a number, not `version = "x"` in code
121+
# blocks that just talk about a dep field name.
122+
PATTERN='^[[:space:]]*[*_]{0,2}Version[*_]{0,2}[[:space:]]*[:=][[:space:]]*v?[0-9]+\.[0-9]+\.[0-9]+'
123+
HITS=0
124+
for doc in "${DOCS[@]}"; do
125+
[ -f "$doc" ] || continue
126+
while IFS= read -r line; do
127+
[ -n "$line" ] || continue
128+
echo "::error file=$doc::R5b pinned version: $line — route to CHANGELOG.md (release history) or Cargo.toml (semver pin)"
129+
HITS=$((HITS+1))
130+
done < <(grep -nE "$PATTERN" "$doc" 2>/dev/null || true)
131+
done
132+
if [ "$HITS" -gt 0 ]; then
133+
echo ""
134+
echo "::error::R5b tripped on $HITS line(s)."
135+
echo "Fix: drop the embedded version; defer to CHANGELOG.md + the"
136+
echo "git log. Cargo.toml's [package].version is the semver pin."
137+
exit 1
138+
fi
139+
echo "✓ R5b clean — no pinned version strings in load-bearing docs."
140+
141+
- name: Summary
142+
run: |
143+
echo "R5 (canonical-reference drift) summary"
144+
echo "----------------------------------------"
145+
echo "R5a: bare prover counts → docs/PROVER_COUNT.md"
146+
echo "R5b: pinned version strings → CHANGELOG.md / Cargo.toml"
147+
echo ""
148+
echo "Scope: 14 load-bearing top-level docs. Sub-tree historical"
149+
echo "docs (docs/handover/, docs/decisions/, docs/releases/, …)"
150+
echo "are deliberately out of scope — they are owner-managed"
151+
echo "snapshots, not the canonical narrative surface."

README.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ podman run -it echidna:latest
253253
[source]
254254
----
255255
src/rust/
256-
provers/ # 102 prover backend implementation files (ProverBackend trait)
256+
provers/ # Per-backend ProverBackend impls (see docs/PROVER_COUNT.md for the live tier table)
257257
verification/ # Trust-hardening subsystem
258258
portfolio.rs # SMT portfolio solving / cross-checking
259259
certificates.rs # Proof certificate checking (Alethe, DRAT/LRAT, TSTP)

0 commit comments

Comments
 (0)