# SPDX-License-Identifier: MPL-2.0 # SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) # # canon.lock — the RELEASED identity of the Hyperpolymath canon. # Proposed location: hyperpolymath/standards/canon.lock (repository root) # # --------------------------------------------------------------------------- # WHY THIS FILE EXISTS # # rsr-template-repo's .machine_readable/rsr-profile.a2ml currently declares its # conformance with: # # spec = "rsr-criteria-v2" # declares-against = "2.0.0-draft" # # Both are free-text strings. Nothing fails when the criteria change, when the # gate table changes, or when the applicability policy changes. That is the # whole of the standards -> template binding today. # # This file turns "which standards?" into a question with a SHA-able answer. # It 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." # # canon.lock is that mechanism, promoted from a per-file pin to a single # release point for the whole canon. # # --------------------------------------------------------------------------- # PATHS AT THIS REVISION (v2.0.0) # # The paths above are the CURRENT in-tree locations, not the post-reorg paths # in 03-STANDARDS-REORG.md. That is deliberate: this file must be green on the # tree it lands on. The reorganisation is a separate change, and 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 of Gate A doing # exactly what it was built for. # # standards-map.toml records both: `from` = current, `target` = post-reorg. # --------------------------------------------------------------------------- # --------------------------------------------------------------------------- # WHO READS IT # # rsr-template-repo/rsr-template-repo_chora.deed (canon …) clause (was the # rsr-profile.a2ml [canon] block, frozen by D43; rsr-template-repo#215) # rsr-template-repo/build/just/repo-init.just writes PROVENANCE.a2ml # minted repos' .machine_readable/PROVENANCE.a2ml # hypatia -> Hypatia.Rules.RsrConformance # scaffoldia # scripts/check-canon-lockstep.sh (canon CI, Gate A) # scripts/check-standards-map.sh (canon CI, Gate D) # # --------------------------------------------------------------------------- # THE RULE # # Any change to a file named in [canon.artifacts] is a CANON CHANGE. # It MUST, in the SAME commit: # 1. bump [canon].version (semver-with-errata, per rsr-criteria [versioning]): # MAJOR = era change, may remove/redefine criteria, may move a criterion # between tiers; freezes the prior major under 0-canon/rsr/archive/ # MINOR = additive only; MUST NOT make a previously-conforming repo # non-conforming at the same tier # PATCH = editorial; no criteria-set change # 2. rewrite the affected sha256 here # 3. pass Gate A (canon-spine-lockstep) - which requires the SPINE to have # adopted the new hashes and to be GREEN against them before this lands. # # Gate A is a deliberate reversal of the current direction of travel: # # YOU MAY NOT TIGHTEN THE CRITERIA UNTIL THE REFERENCE IMPLEMENTATION # PASSES THEM. # # --------------------------------------------------------------------------- # # 2026-09-17 — PATH RELOCATION, NO VERSION BUMP. # # The whole artefact set moved out of the vendored # rhodium-standard-repositories/ copy into 0-canon/rsr/ (11 files, byte # identical — every sha256 below is UNCHANGED). This file's `path` values # therefore changed while its `sha256` values did not. # # The bump obligation above is triggered by a change to a FILE named in # [canon.artifacts]. No such file changed: the same bytes now live at a # different path. The canon's identity is its hash set, and that is identical, # so there is no new release identity for the spine to adopt. Bumping here # would force a spine re-adoption for a change that alters no law. # If you disagree, PATCH is the correct category (editorial; no criteria-set # change) - not MINOR, which is additive. # # --------------------------------------------------------------------------- # # 2026-09-17 - PATCH 2.0.1 -> 2.0.2. THE MACHINE TREE'S SPELLING. # # Ruling R-A settles the spelling as `.machine_readable/`, so the normative # artefacts stop naming the hyphenated path. This IS a change to files named in # [canon.artifacts], so the bump obligation above applies - and PATCH is the # category the rule itself gives it: rsr-criteria [versioning].rule-patch reads # "editorial (wording, detection-rule id corrections, template_ref fixes) with # no criteria-set change". There is no criteria-set change here. # # Why not MINOR, and not a MAJOR era change: # * the gates file's paths array uses '|' alternatives that ALREADY resolve # both spellings, so no repository's conformance changes at any tier. The # edit only puts the canonical form first in the list, which is why it is # cosmetic in effect as well as in intent; # * no criterion is added, removed, re-tiered or re-gated; # * `key = "machine-readable-substrate"` in the criteria is an IDENTIFIER, not # a path, and is deliberately untouched - the same trap that corrupted six # files on the spine side of this rename. # # Sequence, per [canon].order: the SPINE re-adopts these two hashes first, then # this releases. Gate A enforces the order. # # --------------------------------------------------------------------------- # --------------------------------------------------------------------------- # # 2026-11-05 - PATCH 2.1.3 -> 2.1.4. THE DESCRIPTILE CARRIERS ARE THE DEED. # # One artefact changes: TEMPLATE-APPLICABILITY-POLICY.adoc, whose universal # baseline is the human mirror of the gate table. It now names the repo deed's # (agentic) (neurosym) (playbook) clauses as the carriers of those three # descriptiles, with the legacy .a2ml files as the alternative, and names # 0-AI-MANIFEST.deed first (the gate table already accepted it, #1229). No # criterion is added, removed, re-tiered or re-gated, and the gate table and # criteria file are unchanged, so this is the editorial category. # # Why it is editorial and not a tightening: both carriers satisfy the baseline, # so no repository loses conformance. A2ML is retired (D99, D312, D313) and the # three families have a ruled mapping into the repo deed (2026-10-27); the # mirror now says what the estate writes. Hypatia's RSR scorer accepts either # carrier for criteria 3.1.5 to 3.1.7 (hyperpolymath/hypatia#1013), and the # canon archives its own three files in the same change (owner selector, # 2026-11-04). # # The criteria file still describes 3.1.5 to 3.1.7 by their .a2ml names. It is # a legacy A2ML record waiting for kyX, and a fact may not be added to one, so # its prose is left as it is; the scorer decides what satisfies the criterion. # # Sequence, per [canon].order: the spine re-adopts the applicability hash. Gate # A reports the lag until it does; it is informational unless --strict. # --------------------------------------------------------------------------- # --------------------------------------------------------------------------- # # 2026-10-25 - PATCH 2.1.2 -> 2.1.3. THE GATE TABLE CAN SEE NESTED LAYOUTS. # # One artefact changes: the gate table. No criterion is added, removed, # re-tiered or re-gated, so this is the rule-patch category: detection-rule and # table corrections. # # What changed, and why each is a correction rather than a tightening: # # * rust source row, `src/**/*.rs` -> `**/src/**/*.rs`. The old form is # root-anchored, so a cargo WORKSPACE (oikos: four crates, 20+ .rs files # under /src/) posted a permanent MISSING it could never clear # (#1228). The new form still matches the single-crate layout, because # bash's globstar lets the leading `**/` match zero components. # * rust manifest rows gain depth-anchored alternatives, for the same reason # and only to depth 2, so a vendored third-party tree is not swept in. # * reproducible-build: `manifest.scm` added (the estate's Guix convention, # #894) and `flake.nix` REMOVED (#1125) - Nix is retired, and a retired # toolchain must not satisfy a build gate. # * [baseline].paths gains the estate's alternate spellings: documents in # .adoc (ADR-004), 0-AI-MANIFEST.deed (#1229, the template retired .a2ml), # and .well-known/ published from www/ since the 2026-09 migration. # * [canon] gains .github/workflows/rust-ci.yml, which the canon SERVES as # rust-ci-reusable.yml rather than runs. # # Which repositories can flip, in both directions: # * a workspace that was permanently MISSING on rust is now satisfiable # (this is the direction the table was broken in); # * a repository carrying nested .rs sources, a nested Cargo manifest, or a # manifest.scm WITHOUT declaring the gating capability now reports # VESTIGIAL where it previously reported nothing. That is a true finding # the old rows structurally could not see, and the fix is a one-line # declaration in the repository's own profile; # * a repository whose ONLY reproducible-build evidence was flake.nix now # fails. Nix is retired, so that repository was already non-conforming in # substance; the table now says so. # [baseline] is newly ENFORCED by check-rsr-profile.sh (#1227), so a repository # missing a baseline artefact will fail where it previously passed. That is the # point of the change: prose with no reader is not a rule. # # The canon's own profile gains `rust` in the same release. It was never # declared because the root-anchored row could not see the canon's six nested # cargo projects, so the omission was never challenged. # --------------------------------------------------------------------------- # --------------------------------------------------------------------------- # # 2026-09-30 - PATCH 2.1.1 -> 2.1.2. THE MISSED CONSTITUTION BUMP (#1094). # # #1041 (2026-09-23) added one KNOWN-TENSIONS row to 0-canon/constitution/ # (a measured contradiction: an unratified principle already projected as # doctrine) without the bump THE RULE above requires in the same commit, so # Gate A has failed on main since. PATCH: editorial register entry, no # criteria-set change; #1041 itself adopts nothing. The constitution # artefact hash is rewritten below in this commit per the # [canon.artifacts] protocol. # # 2026-09-19 - PATCH 2.1.0 -> 2.1.1. ADR-0003 RATIFIED. # # SCAFFOLD-LIFECYCLE.adoc flips Status: draft -> accepted (owner ruling # 2026-09-19; the decision record is rsr-template-repo ADR-0003, flipped # to Accepted in the same release train). PATCH: editorial status change, # no criteria-set change. The lifecycle artefact hash is rewritten below # in this commit per the [canon.artifacts] protocol. # # 2026-09-19 - MINOR 2.0.4 -> 2.1.0. THE JULIA-GATED CRITERIA. # # Additive MINOR (rule-minor): four new criteria 5.2.3-5.2.6, all gated on # the julia capability, plus the four gate-table rows that make the # julia-library archetype's overlay modules checkable. Non-regression holds # by construction: a repo that does not declare `julia` has the new criteria # scored `na` - applicable set, denominator, score and tier unchanged. # # Three artefacts change: criteria (new rows + version line), gates (0.3.0, # four julia rows), applicability (human mirror sync: gate row, archetype # presets, plugin/deno/canon taxonomy rows, SSOT note). Their sha256 values # are rewritten below, in this commit, per the [canon.artifacts] protocol. # # ORDER (Gate A, spine-adopts-then-canon-releases): the spine's adoption # commit (its .machine_readable/rsr-profile.a2ml [canon] block bumped to # 2.1.0 with the two new hashes) must land BEFORE this one merges; between # the two landings the spine's reverse-direction lockstep job is red by # design - that is the ordering rule doing its job, not a defect. # # Owner ruling 2026-09-19: MINOR approved. # # 2026-09-17 - PATCH 2.0.3 -> 2.0.4. THE 3-PRACTICE DISTRICT. # # Twenty-three paths move under 3-practice/. Twenty-two of them need no law # change: the baseline `paths` list is not capability-gated, so a community-health # file may move without the table caring. MAINTAINERS.adoc is the exception - it # is one of the four governance-tier rows, and the move took it out of the only # location that row named, so the canon stopped satisfying a capability it # declares. Exactly the drift the self-conformance job exists to catch. # # The ROW GAINS THE NEW LOCATION; it does not trade one for the other. Dropping # `MAINTAINERS.adoc` would have broken every estate repo that carries it at the # root - cadastra does - by silently reducing a documented alternative to a # single canon-internal path. The precedent is the row above it: GOVERNANCE.adoc # moved into 0-canon/ in the 0-canon release and `0-canon/GOVERNANCE.adoc` was # appended the same way, for the same reason. # # PATCH, not MINOR: no criterion changes meaning, nothing new becomes required, # and every previously-conforming repo still conforms. The estate gains a # location, and loses nothing. # 2026-09-17 - PATCH 2.0.2 -> 2.0.3. THE 0-CANON RELOCATION. # # The 0-canon district exists. The law moves into it: constitution/ becomes # 0-canon/constitution/, and CANONICAL-NAMES, COMPLIANCE-DASHBOARD, # CRG-SELF-ASSESSMENT, GOVERNANCE, PORT-REGISTRY, RSR-PHILOSOPHY and # TEMPLATE-APPLICABILITY-POLICY leave the repo root for 0-canon/. Executed # from standards-map.toml, which is the spec; GATE D (check-standards-map.sh) # verifies the result bidirectionally. # # WHY THIS ONE IS PATCH AND THE EARLIER RELOCATION WAS A NO-BUMP. # # The 2026-09-17 entry above took the escape route because the moved files # were BYTE IDENTICAL: "every sha256 below is UNCHANGED. ... The canon's # identity is its hash set, and that is identical." That escape is not # available here. ALL FIVE sha256 below CHANGED. The law names paths inside # itself: template-capability-gates.toml lists "constitution/" among the # paths that define the canon, names TEMPLATE-APPLICABILITY-POLICY.adoc as # `policy`, and resolves GOVERNANCE.adoc as an alternative in the # governance-tier rule. Those references were rewritten when the files moved, # so the bytes are not identical, so the identity is not identical, so there # is a new release identity for the spine to adopt. # # The obligation therefore fires and the rule's own menu gives the category: # * no criterion is added, removed, re-tiered or re-gated; # * no previously-conforming repo is made non-conforming by the criteria # set (which is what MINOR is forbidden to do, and what would have made # this MINOR had it happened) - the criteria text is architecturally # unchanged, only the path it names; # * MAJOR is an era change and does not apply. # PATCH: editorial, no criteria-set change. Same reading of # rsr-criteria [versioning].rule-patch as the 2.0.2 entry took. # # `authority` already read 0-canon/constitution/ESTATE-CONSTITUTION.adoc # before this move, while the `constitution` slot still read "constitution/". # The relocation resolves that inconsistency rather than creating one. # # Sequence, per [canon].order: the SPINE re-adopts these five hashes first, # then this releases. Gate A enforces the order. # # --------------------------------------------------------------------------- [canon] # A released major is immutable. Bumping `version` to a new MAJOR means the # prior major's criteria file is copied to 0-canon/rsr/archive/ and pinned # there; a freeze guard fails any PR that mutates a frozen major. # 2026-11-07: draft PATCH 2.1.5 records the gate-table 0.3.1 detection/layout # corrections and their applicability-policy mirror already merged in #1419. # No criterion, tier or capability is added or removed. Both previous digests # described the pre-#1419 files, so consumers correctly rejected the mismatch. # Publication identity below # remains draft/unassigned; this repair does not create a release or tag. version = "2.1.5" spec_family = "rhodium-standard-repositories" status = "draft" # draft | stable released = "2026-09-30" authority = "0-canon/constitution/ESTATE-CONSTITUTION.adoc" # The full git ref that realises this canon. A tag alone is not sufficient # (tags move); a commit alone is not sufficient (it has no version). Both. commit = "0000000000000000000000000000000000000000" # ← fill at release tag = "canon-v2.1.2" # --------------------------------------------------------------------------- # THE ARTEFACT SET — the files that ARE the canon. # # These are also the join keys for standards-map.toml `canon_slot`, so # check-standards-map.sh can prove every canon_slot resolves to a real slot. # # HASHES BELOW ARE REAL, computed 2026-09-17 against standards@bbecaee. # --------------------------------------------------------------------------- [canon.artifacts] criteria = { path = "0-canon/rsr/rsr-criteria-v2.a2ml", # relocated from the vendored copy sha256 = "6a5aa8857bd0d0d58ef48827938ca17c251b6b854dacf59d306388d61694d82a", slot = "criteria", normative = true } gates = { path = ".machine_readable/template-capability-gates.toml", # from .machine_readable/ sha256 = "5c510bba025be718deebb12d0333bfe7db2da43c66578b728b1a334df65290b1", slot = "gates", normative = true } applicability = { path = "0-canon/TEMPLATE-APPLICABILITY-POLICY.adoc", sha256 = "677d2c346805100f1d7ed3cb64b44796a7e1bd00d44a5a4b1b6e54207fb7d9e6", slot = "applicability", normative = true } lifecycle = { path = "0-canon/rsr/SCAFFOLD-LIFECYCLE.adoc", sha256 = "2f405c9ed18e36c067b402cb303b45505f418cab06d90b9857f180950eee7aea", slot = "lifecycle", normative = true } constitution = { path = "0-canon/constitution/", # directory slots use the registry's own method: # sha256 over `git ls-files -s ` sha256 = "be48496f7f786d9aca12271afeb063c10b8ea6ce6bec2efad4f2401d0186ccef", method = "sha256(git ls-files -s )", slot = "constitution", normative = true } # --------------------------------------------------------------------------- # SPLIT-OUT CANDIDATE SLOTS. # # These name formats that 03-STANDARDS-REORG.md proposes moving to their own # repos. They are listed here NOW, at v2.0.0, with their current in-tree hashes, # for two reasons: # # * standards-map.toml's canon_slot values must all resolve (Gate D), and a # slot that vanishes at split time would silently orphan a map entry; # * when the split lands, the slot's `path` changes from an in-tree path to # an external pointer (`url + version + source_hash`) and the owning repo # takes over its cadence — which is a MINOR canon bump by definition # ("additive only; MUST NOT make a previously-conforming repo # non-conforming at the same tier"). # # Same shape as the registry's existing EXTERNAL entries for AffineScript: # a verified POINTER, never a copy. # --------------------------------------------------------------------------- [canon.split-candidates] deed-grammar = { path = "1-formats/deed/spec/DEED-GRAMMAR-SPEC.adoc", sha256 = "PENDING-FIRST-SYNC", # DRAFT v0.2.0, not yet ratified slot = "deed-grammar", normative = false, target_repo = "hyperpolymath/deed", note = "v0.2.0 DRAFT; ABNF normative at spec/abnf/deed.abnf; unblocks estate task #64 (.a2ml -> .deed)" } contractile-spec = { path = "1-formats/contractiles/README.adoc", sha256 = "PENDING-FIRST-SYNC", slot = "contractile-spec", normative = false, target_repo = "hyperpolymath/contractiles", note = "two-file vs four-file Trident is ruling 2 in STANDARDS-CRITICAL-PATH; the linter is estate task #19" } rsr-spec = { path = "0-canon/rsr/RSR-SPEC-v2.adoc", # extracted from the vendored copy sha256 = "PENDING-FIRST-SYNC", slot = "rsr-spec-home", normative = true, target_repo = null, # stays in the canon note = "the prose authority for rsr-criteria-v2.a2ml; relocated out of the vendored rhodium-standard-repositories/ copy, which is ARCHIVED rather than deleted - see AUDIT.adoc" } # --------------------------------------------------------------------------- # THE WORKFLOWS EVERY REPO CALLS. # # A repo's canon identity is these refs at `commit`. This is why the pin must # move as one unit: a consumer that re-pins one reusable workflow but not the # others is running two canons at once, and nothing reports that today. # --------------------------------------------------------------------------- [canon.workflows] governance = "hyperpolymath/standards/.github/workflows/governance-reusable.yml" hypatia = "hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml" allowlist = "hyperpolymath/standards/.github/workflows/allowlist-preflight-reusable.yml" codeql = "hyperpolymath/standards/.github/workflows/codeql-reusable.yml" scorecard = "hyperpolymath/standards/.github/workflows/scorecard-reusable.yml" secrets = "hyperpolymath/standards/.github/workflows/secret-scanner-reusable.yml" mirror = "hyperpolymath/standards/.github/workflows/mirror-reusable.yml" changelog = "hyperpolymath/standards/.github/workflows/changelog-reusable.yml" readme = "hyperpolymath/standards/.github/workflows/readme-derive-reusable.yml" provisioning = "hyperpolymath/standards/.github/workflows/provisioning-check-reusable.yml" # --------------------------------------------------------------------------- # THE ROLES. One owner per concern; a change belongs at its owning layer # ("solutions at source"). Mirrors SCAFFOLD-LIFECYCLE.adoc §Roles, but as data. # --------------------------------------------------------------------------- [canon.roles] canon = "hyperpolymath/standards" spine = "hyperpolymath/rsr-template-repo" composer = "hyperpolymath/scaffoldia" oracle = "hyperpolymath/hypatia" fleet = "hyperpolymath/gitbot-fleet" telemetry= "hyperpolymath/estate-telemetry" # ← proposed split, see 03-STANDARDS-REORG.md # --------------------------------------------------------------------------- # GATE A — the five assertions. check-canon-lockstep.sh implements all five. # --------------------------------------------------------------------------- [canon.lockstep] # 1. every sha256 in [canon.artifacts] matches the working tree require-verified-hashes = true # 2. [canon].version was bumped in the same commit as any artefact change require-version-bump = true # 3. spine@HEAD deed (canon …) == [canon].version + criteria + gates sha256 require-spine-adopted = true # 4. spine dogfood-gate is GREEN against THESE criteria require-spine-green = true # 5. canon itself scores Gold on its applicable set (Gate B, role = "canon") require-canon-gold = true # A canon change that cannot satisfy 3 and 4 is not a rejected change; it is a # change that has been made IN THE WRONG ORDER. Land the spine's adoption # first, then the canon change becomes a one-line version bump. order = "spine-adopts-then-canon-releases"