Feat/provisioning canon - #1132
Merged
Merged
Conversation
Add 3-practice/provisioning/: the Provisioning Standard (prose + praxis deed), the engine (provision.just, provision-lib.sh, provision-modes.sh, provision-check.sh), and the minted templates (launcher, Justfile module, mise, guix source/cargo packages + manifest + channels, SETUP, AI install guide, three llm-warmups, README ai-install fragment, per-repo provisioning_praxis.deed). Launcher standard 0.5.0: every repository carries launcher.sh, profiled by archetype; --setup/--doctor/--heal/--ai-setup call the engine directly so a repo's own root recipe cannot shadow the canon; repo checks live in *-local recipes and a failing doctor-local is FAIL PV-E50. guix.scm: the licence field was a malformed ad-hoc license object pointing at palimpsest-license; it is now (guix licenses) mpl2.0, the licence the file's own SPDX header already declares. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
- Idris2 and Zig are detected to depth 3 (src/abi/*.ipkg, ffi/zig/build.zig): 234 build.zig sit at that depth across the estate and 37 repos have their only Idris2/Zig marker there. Zig verbs now run in the build.zig directory. - launcher.sh is `generated`: realign re-renders it only when it carries the @launcher-deed block; hand-written launchers are kept and source provision-modes.sh. - A minted set whose deed :repo is another repository's is inherited (e.g. from rsr-template-repo) and is re-minted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
Hypatia code_safety flagged the mise install hint in provision-modes.sh as download-then-run (CWE-494, high, new). Every mise install hint now leads with the OS package manager (brew, Fedora COPR, winget, mise's install docs) and gives the installer only as download, read, run -- never piped into a shell. One MISE_INSTALL_HINT in provision-lib.sh feeds both doctor and setup messages; SETUP and the AI guide match, and the AI guide says to show the installer to the user before running it. The two `eval "$(mise env -s bash)"` sites now prepend `mise bin-paths` to PATH instead, which is what they needed and needs no eval. REGISTRY.a2ml was stale (RSR source_hash): regenerated with scripts/build-registry.sh, which also fixes build-registry-test.sh (9 passed, 0 failed locally). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
A file sits at the root only when a tool needs it there. The Guix trio and the warm-ups follow the repository's existing layout: root, or build/ (guix) and docs/onboarding/ or docs/ (warm-ups), as rsr-template-repo already does. provision-lib.sh owns the answer (guix-dir, set-files); doctor, dev-shell, toolchain-refresh and provision-check.sh all ask it, so the engine and its checker cannot disagree about where a file lives. Both guix.scm and build/guix.scm present is the new PV-W35. Zig detection now reaches depth 4 (src/interface/ffi/build.zig): 74 repos keep their only build.zig there (measured 2026-09-30). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
Found while provisioning rsr-template-repo against the canon: - zig `test` runs only when build.zig declares a "test" step (bench and run already checked); bun's fallback `bun test` runs only when test files exist, because `bun test` exits 0 on none. - ai-setup reads the "Just say it" sentence from the README's [[ai-install]] section, so the README and the recipe cannot disagree. - __GUIX_PREFIX__ slot: SETUP and the dev warm-up name build/manifest.scm etc. when the Guix trio lives under build/; guix.scm templates compute %source-dir from their own location. - hp_app_name falls back to the origin remote's name (worktrees). - provision.just: doc comments on the per-language verbs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
fmt-check was aliased to lint, which differs per language (clippy, credo). It is now its own contract verb: cargo fmt --check, zig fmt --check, mix format --check-formatted, gleam format --check, dune build @fmt, or a bun "fmt-check" script; N/A elsewhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
- provision-lib.sh: build/just/doctor-local.sh runs sourced in a subshell; an `exit` or tripped `set -e` is FAIL PV-E51 and the checks it finished still count (tally path baked into the EXIT trap, since set -e unwinds locals). Tested: normal hook 19/1/0, `exit 3` 19/0/1, `set -e; false` 19/0/1. - provision-lib.sh: recipe tools (trivy) pinned in mise only where a recipe uses them. - provision-modes.sh: hp_provision_or_return replaces `&& exit $?`, which returned success for a failing mode. - provision.just: quote the ai-warmup audience argument. - check-launcher-standard-currency.sh: CURRENT_VERSION 0.5.0. - PV-E51 registered in the deed, the standard and the SETUP troubleshooting table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
Running provision-check.sh on a deliberately weak fixture showed three holes: a 0-byte mise.lock passed (presence was -f), a fake guix.scm passed (the stub test was a blacklist), and doctor and the check used different stub regexes and different banned lists. provision-lib.sh now owns three predicates that both call: mise-banned, mise-lock-gaps (every mise.toml tool needs a concrete version in mise.lock, and the file must carry sha256 checksums) and guix-stub (positive: every package field present, manifest lists specifications, channels pin a 40-hex commit). It also gains fact verbs the generator fills templates from (guix-gaps, tool-table, system-deps), so no second per-language table exists. Mutants killed: empty lock, lock without a tool, versionless block, empty version, aqua:denoland/deno, fake guix.scm, template residue, unpinned channels. Controls pass: a real `mise lock` output, a real guix.scm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
Realign replaces a mise.toml that names a banned tool (PV-W23), carrying its other [tools] entries, so the deno-to-bun ruling can execute. The template launcher serves library/tool/theory/docs; app launchers come from launch-scaffolder mint and source the same provisioning modes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
guix.scm.cargo.tmpl promised that `just toolchain-refresh` regenerates the crate inputs from Cargo.lock; nothing did. New verb `crates-scm` runs `guix import crate --lockfile` (GUIX may name a container wrapper) and writes build/guix/crates.scm whole or not at all: the output is accepted only when it defines one origin per registry crate in Cargo.lock, since a containerised guix loses its exit status. Otherwise PV-E41 and the old file stays. Verified on launch-scaffolder's Cargo.lock: 151/151 origins, loaded by `guix repl` ((length %crate-inputs) = 151, origin? #t), byte-identical on regeneration. Two mutants killed: truncated importer output (10/151) and a failing importer (0/151) both exit 1 with PV-E41, file sha256 unchanged. channels.scm is reclassified engine -> minted: it is re-pinned per repo, so it is checked for a 40-hex pin, never byte-compared with the canon. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
main took 0.4.0 -> 0.5.0 for the (js-runtime) clause (D224, #1100) while this branch took the same number for the archetype profile and the provisioning mode family. 0.5.0 is published with D224's meaning, so the provisioning obligations are 0.6.0 (2026-10-01): deed :standard-version, the currency gate's CURRENT_VERSION (its test asserts they agree), the .adoc section, the launcher template's compliance claim and provision-modes.sh. A consumer still citing 0.5.0 gets the gate's non-blocking stale-version warning (standards#991), not a failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
mise_lock_gaps accepted the lock when one `checksum = "sha256:` line existed anywhere, so a tool whose artefacts carried none still passed doctor and provision-check.sh (CodeRabbit, #1096). Every [tools.X."platforms.P"] table now needs its own sha256, and the gap names each tool/platform that lacks one. A tool with no platform tables is not a gap: measured with `mise lock`, core:rust (rustup) and cargo: (built from source) are written with no platform tables and no checksums, while all 97 platform tables across three real locks (just, bun, zig, gleam, shellcheck, erlang, elixir, julia, opam, lychee) carry one. Requiring a checksum per tool would have failed every Rust repository. Controls: the three real locks pass; one zig/linux-x64 checksum removed fails naming exactly that table (the previous code passed it); all removed fails; an empty sha256 value fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
asciidoctor processes `[[ai-install]]` as an anchor even inside backticks, so the standard silently defined an `ai-install` id twice (rsr-template-repo#213 failed check-adoc-renders on the same text). `\[[ai-install]]` renders the same literal code and defines nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
- toolchain-refresh re-pins only the `guix` channel's commit in channels.scm (guix_channel_commit / repin_guix_channel) instead of replacing the file with `guix describe` output; when the current commit cannot be read, or the file has no guix channel, it WARNs and leaves the file byte-identical. - doctor prints its "Next:" hint on stderr, so the PASS/WARN/FAIL tally is the last stdout line on every outcome. - The deed marks channels.scm minted (re-pinned per repository, never byte-compared) and lists build/guix/crates.scm as generated; the maintainer warm-up no longer claims realign overwrites launcher.sh or channels.scm. Raised by CodeRabbit on hyperpolymath/launch-scaffolder#67, which vendors this canon. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
mise merges .tool-versions, mise.toml and .mise.toml, so a banned tool pinned in either secondary file passed PV-W23, which read mise.toml only. The check now reads all three, flags npm:/pipx:/pip:/go: backends whatever the package name, and bans denojs (the 07-18 sweep's name for deno). The deed's :banned-tools is now the same list as BANNED_TOOLS. Controls: python in .tool-versions and npm:prettier in .mise.toml -> mise-banned rc=1 naming both; the same configs without them -> rc=0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
prettier = "latest" resolves to npm:prettier, the only backend the mise registry has for it, so it installed through node while passing PV-W23. mise_banned now asks `mise registry` (offline) and flags a bare name whose every backend is npm:/pipx:/pip:/go:. A name with any other backend (shfmt: aqua) or one mise does not know is not flagged, and without mise on PATH nothing is called banned on a guess. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>
**Stacked on #1096.** The base branch is `feat/provisioning-canon`. After #1096 squash-merges, run: ``` git rebase --onto main feat/provisioning-canon feat/provisioning-check-reusable ``` and retarget this PR to `main`. Until then the diff is just this gate. ## What `.github/workflows/provisioning-check-reusable.yml`, the CI gate from `3-practice/provisioning`. It checks the caller against the canon at the workflow's own commit (`job.workflow_sha`), in two steps that report separately: | Step | Fails when | |---|---| | Engine files match the canon | any `build/just/{provision.just,provision-lib.sh,provision-modes.sh,provision-check.sh}` is missing or differs byte-for-byte | | Provisioning set conforms | the **canon** `provision-check.sh`, run without `--dev`, reports FAIL. It loads `provision-lib.sh` from its own directory, so a drifted caller copy cannot vouch for itself | - `channels.scm` is deliberately not compared byte-for-byte: `toolchain-refresh` re-pins it per repository. `provision-check.sh` checks its pin instead. - `just` 1.56.0 comes from the release tarball, pinned by sha256 (the same pin as launch-scaffolder#67). No new `uses:` is added. - `actions.lock` gains the section by hand (checkout only). `canon.lock` lists the reusable as `provisioning` under `[canon.workflows]`. ## Evidence (local dry run of both steps; the CI proof follows on a throwaway caller) | Case | cmp step | provision-check | |---|---|---| | rsr-template-repo #213 head (control) | pass | pass | | mutant: `fmt-check` recipe removed | pass | **FAIL** | | mutant: `python` added to `mise.toml` | pass | **FAIL** (banned tool + unpinned) | | mutant: `provision-lib.sh` changed | **FAIL** | pass | | mutant reverted | pass | pass | The third mutant is why there are two steps: an engine edit that leaves conformance intact is caught only by the byte comparison. ## Known, not new - actionlint does not know the `job.workflow_sha` context. It reports the same thing 4 times on `allowlist-preflight-reusable.yml`. - `gh actions-lock` gives this file the same `sha-as-ref` advisory that every SHA-pinned workflow here carries (94 on the base, 95 with this one). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01UkSmyapDUmuGyyZSJmvbKy Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
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 |
Contributor
|
Autopilot could not be updated. Open Coding to check access and billing. |
Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>
hyperpolymath
enabled auto-merge (squash)
October 2, 2026 01:24
hyperpolymath
disabled auto-merge
October 2, 2026 01:48
hyperpolymath
enabled auto-merge (squash)
October 2, 2026 01:48
hyperpolymath
disabled auto-merge
October 2, 2026 02:00
hyperpolymath
enabled auto-merge (squash)
October 2, 2026 02:02
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.
Summary
Closes #
Type of change
How has this been verified?
Checklist
git commit -S).SPDX-License-Identifier(code/configMPL-2.0,prose
CC-BY-SA-4.0); I did not relicense existing files.Notes for reviewers