docs(audit): record F6 — the spine's lock pins floating refs, so the template teaches non-pinning - #811
Merged
Conversation
WHAT THIS IS
`canon.lock` makes "which standards?" a question with a hashable answer.
Today the standards -> template binding is two free-text strings in
rsr-template-repo/machine-readable/rsr-profile.a2ml:
spec = "rsr-criteria-v2"
declares-against = "2.0.0-draft"
Nothing fails when the criteria change, when the gate table changes, or when
the applicability policy changes. A grep for any version identifier across both
repos returns exactly ONE hit: that string.
This file is the mechanism rsr-criteria-v2.a2ml already asks for under
[versioning]:
freeze-mechanism = "A released major is frozen by pinning this file's
byte-hash ... NOT by a prose 'FROZEN' banner."
It promotes that per-file pin to a single release point for the whole canon:
version + sha256 of every law artefact + the reusable-workflow refs + the roles.
WHAT IT RECORDS
[canon.artifacts] criteria, gates, applicability, lifecycle, constitution —
each with a REAL sha256 computed against this tree
[canon.split-candidates] deed-grammar, contractile-spec, rsr-spec — listed NOW
so a future split does not orphan a standards-map slot
[canon.workflows] the reusable gates every repo calls
[canon.roles] canon / spine / composer / oracle / fleet
[canon.lockstep] Gate A's five assertions and their ORDER
THE RULE
Any change to a file named in [canon.artifacts] is a CANON CHANGE and MUST,
in the same commit, bump [canon].version and rewrite the hash. That is
assertion 2 of Gate A, implemented in scripts/check-canon-lockstep.sh.
Assertion 4 is a deliberate reversal and is the point of the exercise:
YOU MAY NOT TIGHTEN THE CRITERIA UNTIL THE REFERENCE IMPLEMENTATION
PASSES THEM.
PATHS AT THIS REVISION
The paths recorded are the CURRENT in-tree locations, not the post-reorg paths
in docs/binding/03-STANDARDS-REORG.md. This file must be green on the tree it
lands on. Because every artefact move is a path change to a hash-pinned file,
THE MOVE COMMIT MUST BUMP THIS FILE IN THE SAME COMMIT — which is assertion 2
doing exactly what it was built for.
WHAT IS STILL RED, AND WHY THAT IS CORRECT
Assertion 5 fails: this repo has NO rsr-profile.a2ml, so
scripts/check-rsr-profile.sh exits 2 on the repo that ships it. The law is not
subject to the law. That is fixed by the next commit in this series; it is left
failing here rather than stubbed green.
Witnesses:
scripts/check-canon-lockstep.sh --canon . --base HEAD
-> assertion 1: 5/5 artefacts verified against the tree
-> assertion 2: PASS
-> assertion 5: FAIL (correct — no profile exists yet)
THE MECHANICAL BLOCKER THIS REMOVES
`find . -name "rsr-profile*"` in this repository returned NOTHING. The repo
that ships scripts/check-rsr-profile.sh exited 2 on itself: `no profile at
./.machine_readable/rsr-profile.a2ml`. THE LAW WAS NOT SUBJECT TO THE LAW.
The reason was structural, not backlog. rsr-criteria-v2.a2ml's applicable-set
rule is:
A criterion is APPLICABLE iff its `gate` is `universal` OR the repo's
rsr-profile declares the gating capability.
template-capability-gates.toml carried a [carrier] section for role = "spine"
— the template legitimately carries rust-ci.yml while having no Rust — but had
NO equivalent for the canon. So the moment this repo got a profile it was
scored against gates that are impossible for it to satisfy: it carries the
criteria SSOT (which no capability gates — the criteria are the document that
DEFINES the gates, not an instance of one), docs/proofs/ (283 files, which are
proof artefacts OF THE ESTATE, not proofs of this repo's own code), and
.github/workflows/*-reusable.yml (the gates every other repo CALLS and this
repo SERVES). Compliance was unreachable BY CONSTRUCTION.
WHAT THIS COMMIT DOES
1. template-capability-gates.toml
- "canon" added to [capabilities].known — without it, role = "canon" is
rejected as an unknown CAPABILITY rather than accepted as a role
- new [canon] section listing the 11 paths the canon may carry exempt from
VESTIGIAL, in the same voice and for the same reason as [carrier]
- new `canon` preset = ["bash", "docs-site", "governance-tier"]
2. scripts/check-rsr-profile.sh
- the ROLE switch gains a `canon` arm selecting [canon] paths, and an
explicit warning for an unknown role instead of silently exempting nothing
3. .machine_readable/rsr-profile.a2ml (NEW)
- role = "canon"; declares bash (107 gate scripts), docs-site (casket-pages),
governance-tier, reproducible-build (guix.scm is a real Guix manifest)
- a [canon] block carrying the criteria and gates hashes, so this file is
the spine-side half of the lockstep
- an [notes] block giving a REASON for every omitted capability, the same
discipline rsr-template-repo applies in its own profile
4. docs/AUDIT.adoc, docs/AFFIRMATION.adoc (NEW)
- declaring `governance-tier` exposed two genuine gaps the moment the
checker could read the profile: the canon carried GOVERNANCE.adoc and
MAINTAINERS.adoc at root but had NO AUDIT.adoc and NO AFFIRMATION.adoc.
- AFFIRMATION.adoc is authored in **profile B** (MUST / INTEND / WISH),
because this repo is a *policy surface* and its own
docs/AFFIRMATION-STANDARD.adoc §"Profile B — required sections" says so.
- It is marked DRAFT — UNSIGNED. Every claim is measured and reproducible;
the attestation block is deliberately empty, because a signature is the
owner's to give and not an agent's. IT MUST NOT BE CITED UNTIL SIGNED.
- AUDIT.adoc adds the gate inventory and the carried findings table, so the
audit posture is visible without discovering the standards first.
WITNESSES
scripts/check-rsr-profile.sh .
BEFORE: ERROR: no profile at ./.machine_readable/rsr-profile.a2ml
AFTER: role: canon - [canon] paths exempt from VESTIGIAL
rsr-profile check: OK — scaffold matches declared capabilities.
The FIRST run after adding the profile returned three real findings, all
resolved honestly rather than suppressed:
VESTIGIAL (no 'reproducible-build'): guix.scm|build/guix.scm|flake.nix
-> guix.scm IS a real Guix manifest; reproducible-build is now DECLARED.
Declaring it is the truthful resolution; adding the path to [canon]
would have been a suppression.
MISSING (governance-tier): docs/AUDIT.adoc|AUDIT.adoc
MISSING (governance-tier): docs/AFFIRMATION.adoc|AFFIRMATION.adoc
-> both authored. Not stubbed, not carrier-exempted.
scripts/check-canon-lockstep.sh --canon . --base HEAD
-> assertion 1: 5/5 verified, including the RECOMPUTED gates hash
-> assertion 2: PASS (gates changed AND canon.lock bumped in this commit,
which is assertion 2's whole purpose, demonstrated on itself)
-> assertion 5: now PASSES the existence + role check
python3 -c "import tomllib; tomllib.load(...)" -> TOML OK
bash -n scripts/check-rsr-profile.sh -> syntax OK
WHAT IS STILL RED
Nothing in this commit. The canon now satisfies the checker it ships. It does
NOT yet hold Gold under rsr-criteria-v2 — that requires the one normative
oracle (hypatia:rsr-conformance), which is still marked "to be implemented".
docs/AFFIRMATION.adoc records that under *Held* rather than claiming a tier.
THE COMPLAINT, QUANTIFIED
This repository has 118 top-level entries (57 files, 61 directories) and 2,639
tracked files. `.machine_readable/REGISTRY.a2ml` indexes 33 of them — the
SPECS — and does it well. The other 85 have no declared domain, kind,
lifecycle, relationship or gate. THAT ABSENCE IS THE "UNREADABLE" COMPLAINT.
It is not fixable by prose, because the eight kinds of thing interleaved at one
level are not distinguishable by convention that exists in anyone's head: law,
format specs, protocol specs, grading frameworks, lifecycle gates, enforcement
machinery, generated telemetry, migration debris. Nothing tells a reader why
`audits/` and `docs/audits/` both exist, or `licenses/` and `LICENSES/`, or
`tasks/` versus `tools/`.
WHAT THIS ADDS
standards-map.toml — one [[entry]] per top-level entry, ALL 118 plus the two
files this series introduces. Per entry: target district, kind, lifecycle
(versioned | generated | frozen | deprecated), canonical (may it be linked
normatively by other repos?), canon_slot (join key into canon.lock),
canonical_doc, and the CI gate that fails if it is wrong.
Four things fall out of it that are impossible today:
* `canonical = true` filtered by district IS the answer to "can every spec be
pointed at?" — the SSOT rule, checkable rather than asserted.
* `lifecycle = "generated"` IS the do-not-hand-edit list, which is currently
prose repeated in three files that disagree.
* `canon_slot` IS the join between the map and canon.lock — which files, when
changed, force a canon release.
* `gate` IS the answer to "what fails if I break this?"
BIDIRECTIONAL, DELIBERATELY
scripts/check-standards-map.sh copies the hardest-won lesson in the estate, and
it is the template's own words in machine-readable/root-allow.txt:
"The check is BIDIRECTIONAL:
* anything at root that is not listed here is drift, and fails;
* anything listed here WITHOUT the '?' marker must exist, and its
absence fails.
The second half is the important one. … A one-directional allowlist only
ever ratchets open."
That comment was written after a root cleanup left stale PERMISSIONS behind, so
the allowlist had "quietly become a licence for the very drift it was written to
prevent". A map of this repository has exactly the same failure mode, so it gets
exactly the same test.
WHY IT LANDS BEFORE ANYTHING MOVES
On the unmoved tree this is trivially green, and it proves the checker works.
After the reorganisation it is the artefact that proves the move was COMPLETE.
A checker first exercised BY the move is a checker nobody trusts.
It has already earned its keep: on its first run against the working tree it
correctly flagged the two files this same change series had just added at root
(canon.lock, standards-map.toml) as unmapped, rather than silently passing.
WITNESSES
scripts/check-standards-map.sh --repo .
[1] all 120 mapped paths exist
[2] all 120 top-level entries are mapped
[3] every canonical entry names a canonical_doc
[4] all canon_slot values resolve:
applicability constitution contractile-spec criteria
deed-grammar gates lifecycle rsr-spec-home
[5] entry_count = 120
-> GATE D PASSED
Negative control (the check is fallible, not merely green):
an unmapped entry fails [2]; a listed path that is gone fails [1].
NOTES ON CONTENT
* `target` records the post-reorg path; `from` is the current one. The map is
therefore also the migration manifest for 03-STANDARDS-REORG.md, and the
executes-in-reviewable-steps claim is verifiable rather than promised.
* The three `canon.split-candidates` slots (deed-grammar, contractile-spec,
rsr-spec-home) resolve against canon.lock, so a future split cannot
silently orphan a map entry.
Adds .github/workflows/canon-spine-lockstep.yml with three jobs:
lockstep Gate A — the canon and the spine are on the same law.
Checks out BOTH repos (canon at fetch-depth 0, because
assertion 2 diffs against a base ref and assertion 1 hashes
directories with `git ls-files -s`), then runs
scripts/check-canon-lockstep.sh. GH_TOKEN is passed because
assertion 4 reads the spine's last dogfood-gate conclusion;
without it the script degrades to SKIP rather than passing
silently.
`workflow_dispatch` exposes a `strict` input to promote
assertions 3/4/5 from SKIP to FAIL.
map Gate D — standards-map.toml integrity, in both directions.
self-conformance The canon against its own gate table. Before this change
the repository that SHIPS scripts/check-rsr-profile.sh
exited 2 on itself.
The workflow file is triggered only on paths that can actually change the
answer, so an unrelated docs PR does not pay for a two-repo checkout.
ON actions.lock — hand-edited DELIBERATELY
actions.lock enumerates every workflow file and is enforced at workflow LOAD
time. A lock that does not know about a new workflow kills every consumer with
`jobs=0` and NO annotation — the exact silent failure documented in the
message of 092deda. So a new workflow file REQUIRES a lock entry in the same
commit.
`gh actions-lock` is not available here, and the estate has already established
that its bare *update* mode is not trustworthy on this tree: 092deda records it
prepending a duplicate banner above the SPDX header on all 42 workflows,
rewriting `uses: ./.github/actions/signed-push` to the invalid `uses: $/…`
spelling, and leaving genuinely-stale rows in place while exiting 0 reporting
"All 42 workflows valid".
The VERIFIER is trustworthy even though the updater is not, and the entry is
trivially checkable, so the row was added by hand and then witnessed:
yaml.safe_load('.github/workflows/actions.lock')
-> 42 workflows in lock (was 41)
-> every lock entry corresponds to a real file: []
-> uses: in the new workflow == lock entry for the new workflow: MATCH
The single pin used is actions/checkout@3d3c42e
(# v7.0.1) — the same pin as the other 61 uses in this repo. No new action is
introduced, and no secret is referenced (only github.token), so the pin SET
did not change. That is what makes a hand-edit safe here specifically.
Negative control: the entry is placed alphabetically (boj-build <
canon-spine-lockstep < casket-pages). A stray entry would fail the
"lock entries with no file" check; a missing one fails at load time.
WHAT THIS DOES NOT DO
Assertions 3/4/5 SKIP unless --strict or the workflow_dispatch input is set.
They cannot pass yet: the spine does not carry a [canon] block, and the one
normative oracle (hypatia:rsr-conformance) is still marked "to be implemented"
in the criteria SSOT. Wiring them as hard failures now would block every PR to
this repository. They become hard failures in execution-plan §Phase 5.4,
after the spine adopts this canon.
…template teaches non-pinning
Found while wiring Gate A's counterpart into the spine, and worth recording
because it INVERTS the usual reading of the estate board.
The estate's canon requires SHA-pinned `uses:`. The canon's own actions.lock
enforces it (it records `actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1`).
But rsr-template-repo's actions.lock records FLOATING refs:
'actions/checkout@v7.0.1'
'hyperpolymath/deed-ecosystem@main'
'hyperpolymath/k9-ecosystem@main'
Measured 2026-09-17: 54 `@vN`/`@main` references across that repo's
.github/workflows/.
THAT IS UPSTREAM OF THE BOARD, NOT DOWNSTREAM OF IT
docs/ESTATE-BOARD.adoc reports `sha_pinning_required: false` on 8 repos and
`allowed_actions: all` on 148, and reads those as loose settings. They are
compatible with the template's own example. A repo minted from this spine
inherits workflows that pin tags and point at `@main`, and a lock that accepts
them, so the template teaches the practice the canon forbids — and no repo has
to make a decision to end up there.
WHY IT IS NOT FIXED IN THIS PR
It is a 54-reference change across .github/workflows/ plus a regenerated lock,
and the lock is load-time enforced: a mistaken edit kills every consumer with
`jobs=0` and no annotation. It needs the canon's own `check-allowed-actions`
gate as the witness and its own PR. Filed as F6 with severity High, because the
blast radius is every repo minted from this spine rather than any single repo.
Contributor
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (9)
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| @@ -0,0 +1,126 @@ | |||
| // SPDX-License-Identifier: MPL-2.0 | |||
Found by the estate's own CI on PR #811 — my gate as written made its own PR unmergeable, and inverted the ordering rule it was built to enforce. THE BUG The commit that wired this gate claimed "Assertions 3/4/5 SKIP unless --strict or the workflow_dispatch input is set". That was FALSE as implemented. Every workflow invocation passes --spine, and assertion 3 failed HARD whenever a spine directory was present — regardless of --strict. Result: Canon / spine lockstep -> failure (exit 1) Why that is self-defeating, not merely inconvenient: assertion 3 asks "has the spine adopted THIS canon?". The spine's adoption necessarily pins hashes that exist only in this unmerged PR. So a hard assertion 3 means NO canon release can ever merge before the spine has adopted it, and the spine cannot adopt it before it is released. The gate deadlocked itself. canon.lock [canon.lockstep].order says "spine-adopts-then-canon-releases". The script must not contradict the file it reads. THE FIX — three classes of outcome, not two fail() a HARD invariant (assertions 1 and 2). Always fails. softfail() a REAL deviation (spine on a different canon, canon has no profile, profile role wrong). FAILS under --strict; reported as INFO otherwise. skip() the check could not RUN (no `gh`, no `hypatia`, no --spine). ALWAYS informational, and NEVER promoted by --strict. That last point is a second bug fixed here. Previously skip() promoted to failure under --strict, so --strict could never pass in any environment lacking `gh` and `hypatia` — and `hypatia`'s oracle is marked "to be implemented" in the criteria SSOT, so --strict was permanently red. An absent TOOL is not a lockstep DEVIATION; conflating them trains people to ignore the gate. A green build must not read as "fully verified", so skipped assertions are now collected and printed as an explicit NOT VERIFIED block: passed 8 failed 0 skipped 2 NOT VERIFIED (these assertions did not run — a green result is NOT a full verification): - gh not available; cannot read the spine's last dogfood-gate conclusion - hypatia (the one normative oracle) not available; oracle is marked 'to be implemented' WHERE DRIFT IS ACTUALLY CAUGHT This makes canon CI informational for adoption status, which is correct — it is the SPINE's job to track the canon, and the spine's own `Canon lockstep` job (dogfood-gate.yml, in PR hyperpolymath/rsr-template-repo#101) fails hard on a mismatch. Canon CI: "have you adopted?" Spine CI: "are you still on it?" WITNESSES non-strict, real spine branch -> passed 8 failed 0 skipped 2 GATE A PASSED --strict, real spine branch -> passed 8 failed 0 skipped 2 GATE A PASSED (+ NOT VERIFIED block) NEGATIVE, spine pin corrupted: non-strict -> INFO "spine is on a DIFFERENT canon" GATE A PASSED (correct: canon CI does not gate on adoption status) --strict -> FAIL "spine is on a DIFFERENT canon" GATE A FAILED, exit 1 NEGATIVE, --strict with no spine -> FAIL, exit 1 bash -n -> syntax OK
…ed on spelling
The real cause of the CI failure on this PR, found by reading the job log rather
than by inference. Assertions 1 and 2 were accusing a valid PR of two things it
had not done:
FAIL constitution constitution/
-> the law changed without re-releasing canon.lock (bump version + rewrite hash)
FAIL canon artefacts changed ( gates ) but canon.lock is untouched
-> Bump [canon].version and rewrite the hash in the SAME commit.
Neither was true. The law had not changed, and canon.lock WAS bumped. Both
assertions were reading paths that git never matched.
THE BUG
Git pathspecs resolve against the REPOSITORY ROOT and know nothing about the
--canon prefix. Two places handed git "$CANON/..." instead of a repo-relative
path:
hash_path() cd "$CANON" && git ls-files -s "$CANON/constitution/"
-> matched no path, printed nothing, and sha256sum faithfully
hashed the EMPTY stream: e3b0c442…, the digest of nothing.
Compared against the real 7fbd1015… -> "the law changed".
assertion 2 git -C "$CANON" diff --quiet "$BASE_REF"...HEAD -- "$CANON/canon.lock"
-> matched no path, so `git diff --quiet` exited 0 ("nothing
changed") and the gate reported the lock as untouched.
WHY CI AND MY LOCAL RUN DISAGREED
I had only ever invoked it as `--canon .`. With that spelling "./constitution/"
and "./canon.lock" happen to resolve, so the pathspec matched by accident and
the gate reported "passed 8 failed 0 GATE A PASSED". CI invokes it as
`--canon canon`; the same code then reported "passed 6 failed 2 GATE A
FAILED". The verdict was a function of how the argument was SPELLED, not of the
state of the repository. A gate that can be made green by relabelling its input
is not a gate.
Reproduced before fixing, byte-for-byte:
--canon standards -> passed 6 failed 2 skipped 2 GATE A FAILED (= CI)
--canon . -> passed 8 failed 0 skipped 2 GATE A PASSED
THE FIX
* hash_path() strips the --canon prefix before handing the path to git, and
REFUSES to hash an empty listing. The old code's failure mode was invisible
because hashing nothing yields a plausible 64-hex digest; a wrong pathspec
can no longer be mistaken for a hash.
* assertion 1 reports "could not hash" instead of comparing against it.
* $LOCK_REL, the repo-relative twin of $LOCK, is used for the `git diff`
pathspec in assertion 2.
WITNESSES AFTER THE FIX — all three spellings now agree
--canon standards -> passed 8 failed 0 skipped 2 GATE A PASSED
--canon . -> passed 8 failed 0 skipped 2 GATE A PASSED
--canon /home/user/work/standards -> passed 8 failed 0 skipped 2 GATE A PASSED
NEGATIVE CONTROL, untracked directory declared in canon.lock:
ERROR: git ls-files matched no files for 'zzz/' in .
FAIL constitution: could not hash 'zzz/' — see the error above
(the old code would have hashed the empty stream and produced a digest)
Also carried from the previous commit on this branch: assertions 3/4/5 are
informational unless --strict, and skip() is no longer promoted by --strict
(an absent tool is not a deviation). With this fix, non-strict on this PR
yields 1-2-4-5 pass, 3 informational pending the spine's adoption in
hyperpolymath/rsr-template-repo#101, and a loud NOT VERIFIED block.
bash -n -> syntax OK.
|
This was referenced Sep 17, 2026
hyperpolymath
added a commit
that referenced
this pull request
Sep 17, 2026
…t left by #811 (#813) Follow-up to #811, which merged at `ca1c24e`. This is the one commit from that branch that was **not** part of the merge: `236fcb8`. ## Why this exists Triage of the remaining CI failures on #811 turned up two defects in the merged work itself. Both are still live on `main`. ### 1. `Hypatia` was not a workflow I triaged it last because it looked like an unrelated pre-existing failure. No workflow or job in this repository is named `Hypatia`. The check-run comes from the **GitHub Advanced Security** app — it is the code-scanning gate, and it failed because the SARIF upload from #811 contained *1 new alert in code changed by this pull request*: ``` .machine_readable/rsr-profile.a2ml warning: doc references `src/interface/` but no such directory exists in the tree (likely surviving a directory rename) ``` A true positive against a file #811 added. The directory really is absent (0 tracked paths under `src/interface/`, and the canon has none). The sentence was declaring *that absence* — "no C-ABI seam and no formally specified ABI; src/interface/ absent" — but written so it read as a live path reference. Reworded to state the same fact without the bare path token. The alert was **not** dismissed and no scanning configuration was touched. The text is simply unambiguous now about what it means. ### 2. Three references to a document that does not exist Checking every path that #811's files point at turned up references to `docs/binding/04-EXECUTION-PLAN.md`. Neither that file nor `docs/binding/` exists anywhere in this repository, and never has: | file | reference | |---|---| | `.machine_readable/rsr-profile.a2ml:37` | "see docs/binding/04-EXECUTION-PLAN.md, ruling R-B" | | `docs/AFFIRMATION.adoc:54` | `link:../binding/04-EXECUTION-PLAN.md[...]` | | `scripts/check-canon-lockstep.sh:98` | "See docs/binding/04-EXECUTION-PLAN.md." | These point at a plan that existed only in conversation. A document citing a non-existent authority is worse than one citing none: it reads as though the argument has been made somewhere. Repointed at what exists and actually says it: - the dialect note -> `docs/AUDIT.adoc` finding **F3**, which records that the law is authored in a superseded dialect while DEED is itself an unratified DRAFT - the ordering rule -> `canon.lock [canon.lockstep]`, where `spine-adopts-then-canon-releases` is actually stated - AFFIRMATION's "We intend" list -> `docs/AUDIT.adoc`, which enumerates the release conditions those actions serve Every cross-reference in every `.adoc` and `.a2ml` this commit touches now resolves. ## No lock bump required `.machine_readable/rsr-profile.a2ml` is not named in `canon.lock [canon.artifacts]` (criteria, gates, applicability, lifecycle, constitution), so a comment edit in it is not a canon change and Gate A assertion 2 stays satisfied. ## Gates ``` Gate A, --canon canon -> passed 8 failed 0 skipped 2 GATE A PASSED Gate A, --canon . -> passed 8 failed 0 skipped 2 GATE A PASSED check-rsr-profile.sh . -> OK - scaffold matches declared capabilities check-standards-map.sh -> GATE D PASSED, entry_count = 120 ```
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.




docs(audit): record F6 — the spine's lock pins floating refs, so the template teaches non-pinning
because it INVERTS the usual reading of the estate board.
The estate's canon requires SHA-pinned
uses:. The canon's own actions.lockenforces it (it records
actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1).But rsr-template-repo's actions.lock records FLOATING refs:
Measured 2026-09-17: 54
@vN/@mainreferences across that repo's.github/workflows/.
THAT IS UPSTREAM OF THE BOARD, NOT DOWNSTREAM OF IT
docs/ESTATE-BOARD.adoc reports
sha_pinning_required: falseon 8 repos andallowed_actions: allon 148, and reads those as loose settings. They arecompatible with the template's own example. A repo minted from this spine
inherits workflows that pin tags and point at
@main, and a lock that acceptsthem, so the template teaches the practice the canon forbids — and no repo has
to make a decision to end up there.
WHY IT IS NOT FIXED IN THIS PR
It is a 54-reference change across .github/workflows/ plus a regenerated lock,
and the lock is load-time enforced: a mistaken edit kills every consumer with
jobs=0and no annotation. It needs the canon's owncheck-allowed-actionsgate as the witness and its own PR. Filed as F6 with severity High, because the
blast radius is every repo minted from this spine rather than any single repo.
ci(gates): wire Gate A and Gate D into CI + lock the new workflow
lockstep Gate A — the canon and the spine are on the same law.
Checks out BOTH repos (canon at fetch-depth 0, because
assertion 2 diffs against a base ref and assertion 1 hashes
directories with
git ls-files -s), then runsscripts/check-canon-lockstep.sh. GH_TOKEN is passed because
assertion 4 reads the spine's last dogfood-gate conclusion;
without it the script degrades to SKIP rather than passing
silently.
workflow_dispatchexposes astrictinput to promoteassertions 3/4/5 from SKIP to FAIL.
map Gate D — standards-map.toml integrity, in both directions.
self-conformance The canon against its own gate table. Before this change
the repository that SHIPS scripts/check-rsr-profile.sh
exited 2 on itself.
The workflow file is triggered only on paths that can actually change the
answer, so an unrelated docs PR does not pay for a two-repo checkout.
ON actions.lock — hand-edited DELIBERATELY
actions.lock enumerates every workflow file and is enforced at workflow LOAD
time. A lock that does not know about a new workflow kills every consumer with
jobs=0and NO annotation — the exact silent failure documented in themessage of 092deda. So a new workflow file REQUIRES a lock entry in the same
commit.
gh actions-lockis not available here, and the estate has already establishedthat its bare update mode is not trustworthy on this tree: 092deda records it
prepending a duplicate banner above the SPDX header on all 42 workflows,
rewriting
uses: ./.github/actions/signed-pushto the invaliduses: $/…spelling, and leaving genuinely-stale rows in place while exiting 0 reporting
"All 42 workflows valid".
The VERIFIER is trustworthy even though the updater is not, and the entry is
trivially checkable, so the row was added by hand and then witnessed:
yaml.safe_load('.github/workflows/actions.lock')
-> 42 workflows in lock (was 41)
-> every lock entry corresponds to a real file: []
-> uses: in the new workflow == lock entry for the new workflow: MATCH
The single pin used is actions/checkout@3d3c42e
(# v7.0.1) — the same pin as the other 61 uses in this repo. No new action is
introduced, and no secret is referenced (only github.token), so the pin SET
did not change. That is what makes a hand-edit safe here specifically.
Negative control: the entry is placed alphabetically (boj-build <
canon-spine-lockstep < casket-pages). A stray entry would fail the
"lock entries with no file" check; a missing one fails at load time.
WHAT THIS DOES NOT DO
Assertions 3/4/5 SKIP unless --strict or the workflow_dispatch input is set.
They cannot pass yet: the spine does not carry a [canon] block, and the one
normative oracle (hypatia:rsr-conformance) is still marked "to be implemented"
in the criteria SSOT. Wiring them as hard failures now would block every PR to
this repository. They become hard failures in execution-plan §Phase 5.4,
after the spine adopts this canon.
feat(map): add standards-map.toml — the repository, readable by machine
This repository has 118 top-level entries (57 files, 61 directories) and 2,639
tracked files.
.machine_readable/REGISTRY.a2mlindexes 33 of them — theSPECS — and does it well. The other 85 have no declared domain, kind,
lifecycle, relationship or gate. THAT ABSENCE IS THE "UNREADABLE" COMPLAINT.
It is not fixable by prose, because the eight kinds of thing interleaved at one
level are not distinguishable by convention that exists in anyone's head: law,
format specs, protocol specs, grading frameworks, lifecycle gates, enforcement
machinery, generated telemetry, migration debris. Nothing tells a reader why
audits/anddocs/audits/both exist, orlicenses/andLICENSES/, ortasks/versustools/.WHAT THIS ADDS
standards-map.toml — one [[entry]] per top-level entry, ALL 118 plus the two
files this series introduces. Per entry: target district, kind, lifecycle
(versioned | generated | frozen | deprecated), canonical (may it be linked
normatively by other repos?), canon_slot (join key into canon.lock),
canonical_doc, and the CI gate that fails if it is wrong.
Four things fall out of it that are impossible today:
canonical = truefiltered by district IS the answer to "can every spec bepointed at?" — the SSOT rule, checkable rather than asserted.
lifecycle = "generated"IS the do-not-hand-edit list, which is currentlyprose repeated in three files that disagree.
canon_slotIS the join between the map and canon.lock — which files, whenchanged, force a canon release.
gateIS the answer to "what fails if I break this?"BIDIRECTIONAL, DELIBERATELY
scripts/check-standards-map.sh copies the hardest-won lesson in the estate, and
it is the template's own words in machine-readable/root-allow.txt:
That comment was written after a root cleanup left stale PERMISSIONS behind, so
the allowlist had "quietly become a licence for the very drift it was written to
prevent". A map of this repository has exactly the same failure mode, so it gets
exactly the same test.
WHY IT LANDS BEFORE ANYTHING MOVES
On the unmoved tree this is trivially green, and it proves the checker works.
After the reorganisation it is the artefact that proves the move was COMPLETE.
A checker first exercised BY the move is a checker nobody trusts.
It has already earned its keep: on its first run against the working tree it
correctly flagged the two files this same change series had just added at root
(canon.lock, standards-map.toml) as unmapped, rather than silently passing.
WITNESSES
scripts/check-standards-map.sh --repo .
[1] all 120 mapped paths exist
[2] all 120 top-level entries are mapped
[3] every canonical entry names a canonical_doc
[4] all canon_slot values resolve:
applicability constitution contractile-spec criteria
deed-grammar gates lifecycle rsr-spec-home
[5] entry_count = 120
-> GATE D PASSED
Negative control (the check is fallible, not merely green):
an unmapped entry fails [2]; a listed path that is gone fails [1].
NOTES ON CONTENT
targetrecords the post-reorg path;fromis the current one. The map istherefore also the migration manifest for 03-STANDARDS-REORG.md, and the
executes-in-reviewable-steps claim is verifiable rather than promised.
canon.split-candidatesslots (deed-grammar, contractile-spec,rsr-spec-home) resolve against canon.lock, so a future split cannot
silently orphan a map entry.
feat(gates): add role=canon — unblocks the law being subject to the law
find . -name "rsr-profile*"in this repository returned NOTHING. The repothat ships scripts/check-rsr-profile.sh exited 2 on itself:
no profile at ./.machine_readable/rsr-profile.a2ml. THE LAW WAS NOT SUBJECT TO THE LAW.The reason was structural, not backlog. rsr-criteria-v2.a2ml's applicable-set
rule is:
template-capability-gates.toml carried a [carrier] section for role = "spine"
— the template legitimately carries rust-ci.yml while having no Rust — but had
NO equivalent for the canon. So the moment this repo got a profile it was
scored against gates that are impossible for it to satisfy: it carries the
criteria SSOT (which no capability gates — the criteria are the document that
DEFINES the gates, not an instance of one), docs/proofs/ (283 files, which are
proof artefacts OF THE ESTATE, not proofs of this repo's own code), and
.github/workflows/*-reusable.yml (the gates every other repo CALLS and this
repo SERVES). Compliance was unreachable BY CONSTRUCTION.
WHAT THIS COMMIT DOES
template-capability-gates.toml
rejected as an unknown CAPABILITY rather than accepted as a role
VESTIGIAL, in the same voice and for the same reason as [carrier]
canonpreset = ["bash", "docs-site", "governance-tier"]scripts/check-rsr-profile.sh
canonarm selecting [canon] paths, and anexplicit warning for an unknown role instead of silently exempting nothing
.machine_readable/rsr-profile.a2ml (NEW)
governance-tier, reproducible-build (guix.scm is a real Guix manifest)
the spine-side half of the lockstep
discipline rsr-template-repo applies in its own profile
docs/AUDIT.adoc, docs/AFFIRMATION.adoc (NEW)
governance-tierexposed two genuine gaps the moment thechecker could read the profile: the canon carried GOVERNANCE.adoc and
MAINTAINERS.adoc at root but had NO AUDIT.adoc and NO AFFIRMATION.adoc.
because this repo is a policy surface and its own
docs/AFFIRMATION-STANDARD.adoc §"Profile B — required sections" says so.
the attestation block is deliberately empty, because a signature is the
owner's to give and not an agent's. IT MUST NOT BE CITED UNTIL SIGNED.
audit posture is visible without discovering the standards first.
WITNESSES
scripts/check-rsr-profile.sh .
BEFORE: ERROR: no profile at ./.machine_readable/rsr-profile.a2ml
AFTER: role: canon - [canon] paths exempt from VESTIGIAL
rsr-profile check: OK — scaffold matches declared capabilities.
The FIRST run after adding the profile returned three real findings, all
resolved honestly rather than suppressed:
VESTIGIAL (no 'reproducible-build'): guix.scm|build/guix.scm|flake.nix
-> guix.scm IS a real Guix manifest; reproducible-build is now DECLARED.
Declaring it is the truthful resolution; adding the path to [canon]
would have been a suppression.
MISSING (governance-tier): docs/AUDIT.adoc|AUDIT.adoc
MISSING (governance-tier): docs/AFFIRMATION.adoc|AFFIRMATION.adoc
-> both authored. Not stubbed, not carrier-exempted.
scripts/check-canon-lockstep.sh --canon . --base HEAD
-> assertion 1: 5/5 verified, including the RECOMPUTED gates hash
-> assertion 2: PASS (gates changed AND canon.lock bumped in this commit,
which is assertion 2's whole purpose, demonstrated on itself)
-> assertion 5: now PASSES the existence + role check
python3 -c "import tomllib; tomllib.load(...)" -> TOML OK
bash -n scripts/check-rsr-profile.sh -> syntax OK
WHAT IS STILL RED
Nothing in this commit. The canon now satisfies the checker it ships. It does
NOT yet hold Gold under rsr-criteria-v2 — that requires the one normative
oracle (hypatia:rsr-conformance), which is still marked "to be implemented".
docs/AFFIRMATION.adoc records that under Held rather than claiming a tier.
feat(canon): add canon.lock — the released identity of the canon
canon.lockmakes "which standards?" a question with a hashable answer.Today the standards -> template binding is two free-text strings in
rsr-template-repo/machine-readable/rsr-profile.a2ml:
Nothing fails when the criteria change, when the gate table changes, or when
the applicability policy changes. A grep for any version identifier across both
repos returns exactly ONE hit: that string.
This file is the mechanism rsr-criteria-v2.a2ml already asks for under
[versioning]:
It promotes that per-file pin to a single release point for the whole canon:
version + sha256 of every law artefact + the reusable-workflow refs + the roles.
WHAT IT RECORDS
[canon.artifacts] criteria, gates, applicability, lifecycle, constitution —
each with a REAL sha256 computed against this tree
[canon.split-candidates] deed-grammar, contractile-spec, rsr-spec — listed NOW
so a future split does not orphan a standards-map slot
[canon.workflows] the reusable gates every repo calls
[canon.roles] canon / spine / composer / oracle / fleet
[canon.lockstep] Gate A's five assertions and their ORDER
THE RULE
Any change to a file named in [canon.artifacts] is a CANON CHANGE and MUST,
in the same commit, bump [canon].version and rewrite the hash. That is
assertion 2 of Gate A, implemented in scripts/check-canon-lockstep.sh.
Assertion 4 is a deliberate reversal and is the point of the exercise:
PATHS AT THIS REVISION
The paths recorded are the CURRENT in-tree locations, not the post-reorg paths
in docs/binding/03-STANDARDS-REORG.md. This file must be green on the tree it
lands on. Because every artefact move is a path change to a hash-pinned file,
THE MOVE COMMIT MUST BUMP THIS FILE IN THE SAME COMMIT — which is assertion 2
doing exactly what it was built for.
WHAT IS STILL RED, AND WHY THAT IS CORRECT
Assertion 5 fails: this repo has NO rsr-profile.a2ml, so
scripts/check-rsr-profile.sh exits 2 on the repo that ships it. The law is not
subject to the law. That is fixed by the next commit in this series; it is left
failing here rather than stubbed green.
Witnesses:
scripts/check-canon-lockstep.sh --canon . --base HEAD
-> assertion 1: 5/5 artefacts verified against the tree
-> assertion 2: PASS
-> assertion 5: FAIL (correct — no profile exists yet)