From 0c3399968e10c9b2ffdc527e7eeb95afc2ca95a6 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 12 Nov 2026 17:00:43 +0000 Subject: [PATCH] chore: move the template pin to rsr-template-repo 83c3cf4 Applies template commits 9059284 (rsr-template-repo#344: a lagging canon pin passes with a notice) and 83c3cf4 (rsr-template-repo#345: Julia Project.toml detected up to three levels deep). With the second, src/julia/Project.toml is detected, so the julia line in mise.toml passes check-mise-language. The canon-pin-check.sh comment carries the project name, as the mint renders it, so the file matches a replay of the mint. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0136P7ZohGxQnA5cbBZyi8Wp --- build/just/provision-lib.sh | 3 ++- occupancy-types_chora.deed | 6 ++--- scripts/canon-pin-check.sh | 36 ++++++++++++++++++++++--- scripts/check-mise-language.sh | 2 +- tests/workflows/canon_pin_check_test.sh | 24 +++++++++++++++++ tests/workflows/mint_gates_test.sh | 27 +++++++++++++++++++ 6 files changed, 90 insertions(+), 8 deletions(-) diff --git a/build/just/provision-lib.sh b/build/just/provision-lib.sh index 6771618..6319326 100755 --- a/build/just/provision-lib.sh +++ b/build/just/provision-lib.sh @@ -99,6 +99,7 @@ ARCHETYPE="$(deed archetype "")" # estate ABI/FFI pattern nests deeper: Idris2 to two levels down (src/abi/*.ipkg), # Zig to three (ffi/zig/build.zig, and occupancy-types's src/interface/ffi/build.zig: # 74 repos have their only build.zig at that depth, measured 2026-09-30). +# Julia to three (src/julia/Project.toml: occupancy-types keeps its package there). # Order matters only for display. `docs` is reported when nothing else is. # Print the first path matching glob $1 within find depth $2 (default 2), # excluding .git, node_modules and root target contents; no match prints nothing. @@ -108,7 +109,7 @@ detect_langs() { local out=() [ -n "$(first Cargo.toml)" ] && out+=(rust) [ -n "$(first '*.ipkg' 3)" ] && out+=(idris2) - [ -n "$(first Project.toml 1)" ] && out+=(julia) + [ -n "$(first Project.toml 3)" ] && out+=(julia) [ -n "$(first build.zig 4)" ] && out+=(zig) [ -n "$(first mix.exs)" ] && out+=(elixir) [ -n "$(first gleam.toml)" ] && out+=(gleam) diff --git a/occupancy-types_chora.deed b/occupancy-types_chora.deed index cc9a4d7..1391ebe 100644 --- a/occupancy-types_chora.deed +++ b/occupancy-types_chora.deed @@ -1095,7 +1095,7 @@ :last-updated "2026-11-12" :parent "hyperpolymath/rsr-template-repo" ; template tip at mint - :parent-pin "2f54c7b53cabb0b49c10a2653815e1a1ba9a730c" + :parent-pin "83c3cf400ce2c376022f2ece5a100e2b78899f07" :role "minted-repo" :stack "unspecified" :direction "generated-from-core" @@ -1112,8 +1112,8 @@ :minted-at "2026-11-12" :template-repo "hyperpolymath/rsr-template-repo" :template-branch "main" - :template-commit "2f54c7b53cabb0b49c10a2653815e1a1ba9a730c" - :template-tree "1d693c668dd22ff03a05ba8a1b79f1ebf1577787" + :template-commit "83c3cf400ce2c376022f2ece5a100e2b78899f07" + :template-tree "3eb49c7d81a9aa053e31ce1fab379c77906b7d36" ; Contract: branches this repo is EXPECTED to carry beyond the default. ; Empty means default-only. A mint must never inherit template work ; branches (coderabbit/, chore/, bot-task or stale branches) — that is diff --git a/scripts/canon-pin-check.sh b/scripts/canon-pin-check.sh index 59e6927..28caa7c 100755 --- a/scripts/canon-pin-check.sh +++ b/scripts/canon-pin-check.sh @@ -22,12 +22,25 @@ # fails. Standards Gate A checks the other side: a canon PR that changes # canon.lock is not mergeable until the spine's main pins it. # +# Spine and instance. Exact equality is the SPINE's invariant: the template +# is what implements the canon. A repo minted from the template carries the +# pin as PROVENANCE, "the canon release it was cut from" (repo-init.just), +# and a provenance record cannot follow later releases. Requiring equality +# there turned every instance red on every push the moment standards +# released a newer canon (2.1.5 reddened every 2.1.2-2.1.4 instance). So an +# instance whose pin names an OLDER version passes with a notice naming the +# release it lags. The spine is recognised by GITHUB_REPOSITORY_ID, which +# survives a transfer; with no id in the environment (a local run) the +# check stays strict. +# # Usage: # canon-pin-check.sh released -# 0 the pin equals canon.lock (a spent :proposed-by gives a notice) +# 0 the pin equals canon.lock (a spent :proposed-by gives a notice), +# or, outside the spine, the pin names an older canon version # 3 the pin differs and the clause names a canon PR: the caller must # check that PR; stdout ends with the line PROPOSED_BY= -# 1 the pin differs and names no canon PR +# 1 the pin differs and names no canon PR (in an instance: the pin +# names the released version or a later one, with other hashes) # 2 malformed input (no single clause, a pin of the wrong shape, a # malformed :proposed-by, an unreadable lock) # canon-pin-check.sh pending @@ -38,7 +51,16 @@ set -euo pipefail -usage() { sed -n '25,38p' "$0" >&2; exit 2; } +usage() { sed -n '36,50p' "$0" >&2; exit 2; } + +# The spine's repository id (hyperpolymath/occupancy-types). An id, not a +# slug: a slug changes on a transfer, an id does not. +SPINE_REPOSITORY_ID=1306804488 + +# version_lt : true when semver a sorts strictly before b. +version_lt() { + [ "$1" != "$2" ] && [ "$(printf '%s\n%s\n' "$1" "$2" | sort -t. -k1,1n -k2,2n -k3,3n | head -1)" = "$1" ] +} # deed_canon : print the value of : from the deed's one # (canon …) clause, or nothing when the clause or key is absent or the clause @@ -133,6 +155,14 @@ case "$MODE" in echo "PROPOSED_BY=${PROPOSED}" exit 3 fi + pinned="$(deed_canon version "$DEED")" + released="$(canon_key version "$LOCK")" + if [ -n "${GITHUB_REPOSITORY_ID:-}" ] && [ "$GITHUB_REPOSITORY_ID" != "$SPINE_REPOSITORY_ID" ] \ + && version_lt "$pinned" "$released"; then + echo + echo "::notice::this repo was cut from canon ${pinned}; canon ${released} is released. The pin is provenance, so this is not a failure; re-pin the (canon …) clause to adopt ${released}." + exit 0 + fi exit 1 ;; pending) diff --git a/scripts/check-mise-language.sh b/scripts/check-mise-language.sh index 56a8cf7..90e3a8f 100644 --- a/scripts/check-mise-language.sh +++ b/scripts/check-mise-language.sh @@ -102,7 +102,7 @@ if [ -z "$LANGS" ] && [ "$DETECT" -eq 1 ]; then _l=() [ -n "$(_find Cargo.toml)" ] && _l+=(rust) [ -n "$(_find '*.ipkg' 3)" ] && _l+=(idris2) - [ -n "$(_find Project.toml 1)" ] && _l+=(julia) + [ -n "$(_find Project.toml 3)" ] && _l+=(julia) [ -n "$(_find build.zig 4)" ] && _l+=(zig) [ -n "$(_find mix.exs)" ] && _l+=(elixir) [ -n "$(_find gleam.toml)" ] && _l+=(gleam) diff --git a/tests/workflows/canon_pin_check_test.sh b/tests/workflows/canon_pin_check_test.sh index 4414afb..799c67a 100755 --- a/tests/workflows/canon_pin_check_test.sh +++ b/tests/workflows/canon_pin_check_test.sh @@ -10,6 +10,9 @@ set -euo pipefail root="$(cd "$(dirname "$0")/../.." && pwd)" check="$root/scripts/canon-pin-check.sh" tmp="$(mktemp -d)" +# CI sets the repository id; the strict cases below must not depend on which +# repository runs this file, so each case that wants an id passes its own. +unset GITHUB_REPOSITORY_ID trap 'rm -rf "$tmp"' EXIT A=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa @@ -73,6 +76,27 @@ expect 0 "released: a pin equal to canon.lock passes" bash "$check" released "$t deed "$tmp/behind.deed" 2.1.4 "$A" "$C" expect 1 "released: a different pin naming no canon PR fails" bash "$check" released "$tmp/behind.deed" "$tmp/released.lock" +# Spine versus instance: the pin is provenance in an instance, so an OLDER +# canon passes there with a notice; the spine (and a local run with no +# repository id) stays strict. A same-version or later pin with other hashes +# fails everywhere: that is a wrong pin, not a lag. +SPINE=1306804488 OTHER=999 +expect 1 "spine: an older pin fails" env GITHUB_REPOSITORY_ID=$SPINE bash "$check" released "$tmp/behind.deed" "$tmp/released.lock" +expect 0 "instance: an older pin passes" env GITHUB_REPOSITORY_ID=$OTHER bash "$check" released "$tmp/behind.deed" "$tmp/released.lock" +grep -q '::notice::this repo was cut from canon 2.1.4; canon 2.1.5 is released' "$tmp/out" || { echo "FAIL: lag notice missing"; exit 1; } +deed "$tmp/samever.deed" 2.1.5 "$A" "$C" +expect 1 "instance: the released version with other hashes fails" env GITHUB_REPOSITORY_ID=$OTHER bash "$check" released "$tmp/samever.deed" "$tmp/released.lock" +deed "$tmp/later.deed" 2.1.6 "$A" "$C" +expect 1 "instance: a later version with no canon PR fails" env GITHUB_REPOSITORY_ID=$OTHER bash "$check" released "$tmp/later.deed" "$tmp/released.lock" +deed "$tmp/minor.deed" 2.0.10 "$A" "$C" +expect 0 "instance: versions compare numerically (2.0.10 < 2.1.5)" env GITHUB_REPOSITORY_ID=$OTHER bash "$check" released "$tmp/minor.deed" "$tmp/released.lock" +lock "$tmp/ten.lock" 2.1.10 "$A" "$B" +lock "$tmp/nine.lock" 2.1.9 "$A" "$B" +deed "$tmp/nine.deed" 2.1.9 "$A" "$C" +deed "$tmp/ten.deed" 2.1.10 "$A" "$C" +expect 0 "instance: versions compare numerically (2.1.9 < 2.1.10)" env GITHUB_REPOSITORY_ID=$OTHER bash "$check" released "$tmp/nine.deed" "$tmp/ten.lock" +expect 1 "instance: 2.1.10 is not older than 2.1.9" env GITHUB_REPOSITORY_ID=$OTHER bash "$check" released "$tmp/ten.deed" "$tmp/nine.lock" + deed "$tmp/ahead.deed" 2.1.6 "$A" "$C" ':proposed-by "metadatastician/standards#1440"' expect 3 "released: a different pin naming a canon PR defers to that PR" bash "$check" released "$tmp/ahead.deed" "$tmp/released.lock" grep -qx 'PROPOSED_BY=metadatastician/standards#1440' "$tmp/out" || { echo "FAIL: PROPOSED_BY line missing"; exit 1; } diff --git a/tests/workflows/mint_gates_test.sh b/tests/workflows/mint_gates_test.sh index 2c91aac..f11969a 100644 --- a/tests/workflows/mint_gates_test.sh +++ b/tests/workflows/mint_gates_test.sh @@ -236,6 +236,33 @@ printf '[tools]\njust = "latest"\nshellcheck = "latest"\nyq = "latest"\ntrivy = expect_pass "--detect finds markers below the root, as provision-lib does" \ mise --root "$DD" --detect +# A Julia package whose Project.toml is not at the root (src/julia/Project.toml, +# depth 3) must be detected by BOTH detectors, with the same answer; one under an +# excluded tree (node_modules) must not be. +JD="$WORK/julia-depth"; mkdir -p "$JD/src/julia" "$JD/build/just" +cp "$REPO_DIR/build/just/provision-lib.sh" "$JD/build/just/" +printf 'name = "Demo"\n' > "$JD/src/julia/Project.toml" +printf '[tools]\njust = "latest"\nshellcheck = "latest"\nyq = "latest"\njulia = "latest"\n' > "$JD/mise.toml" +expect_pass "mise gate (engine) detects julia at src/julia/Project.toml" mise --root "$JD" +rm -rf "$JD/build" +printf '[tools]\njust = "latest"\nshellcheck = "latest"\nyq = "latest"\ntrivy = "latest"\njulia = "latest"\n' > "$JD/mise.toml" +expect_pass "mise gate (--detect mirror) detects julia at src/julia/Project.toml" mise --root "$JD" --detect +# Same julia mise.toml, but the only Project.toml sits under node_modules: julia is +# then declared for no detected language, which is the finding to look for. +JV="$WORK/julia-vendored"; mkdir -p "$JV/node_modules/dep" "$JV/build/just" +cp "$REPO_DIR/build/just/provision-lib.sh" "$JV/build/just/" +printf 'name = "Dep"\n' > "$JV/node_modules/dep/Project.toml" +printf '[tools]\njust = "latest"\nshellcheck = "latest"\nyq = "latest"\ntrivy = "latest"\njulia = "latest"\n' > "$JV/mise.toml" +out_v="$(mise --root "$JV" 2>&1)" +if grep -q 'declared for no detected language: julia' <<<"$out_v"; then + pass "mise gate (engine) ignores a vendored Project.toml" +else bad "mise gate (engine) ignores a vendored Project.toml"; fi +rm -rf "$JV/build" +out_v="$(mise --root "$JV" --detect 2>&1)" +if grep -q 'declared for no detected language: julia' <<<"$out_v"; then + pass "mise gate (--detect mirror) ignores a vendored Project.toml" +else bad "mise gate (--detect mirror) ignores a vendored Project.toml"; fi + echo "" echo "── Mint tokens ────────────────────────────────────────────"