From 7fcbc4a64640a82a578f59c385c3f2900abbb556 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 11:22:03 +0000 Subject: [PATCH 1/2] feat(provisioning): a package.json provisions bun, deno and pnpm; deno and pnpm leave the banned list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 2026-10-14 ruling (LANGUAGE-POLICY §1.3–§1.4, standards#1249) put Bun > Deno > pnpm > npm and one package.json, every runtime. The provisioning canon still enforced the old policy against it: - BANNED_TOOLS (and the deed's :banned-tools) listed deno and pnpm, so doctor's PV-W23 warned on a mise.toml that pins them; - PV-W30 called deno.lock a Deno leftover and PV-W34 called pnpm-lock.yaml one, though every package must now carry both; - a package.json provisioned only bun, while js-runtimes regen needs all three tools. Now a package.json provisions bun, deno and pnpm (mise, remedy text, Guix gap via mise); deno and pnpm leave both banned lists (node stays, so npm is never provisioned); deno.lock and pnpm-lock.yaml are no longer leftovers; a deno.json is a notice. provision-lib 0.5.0 -> 0.6.0. Measured on a fixture (package.json, mise.toml with bun/deno/pnpm, the three locks), old engine vs new: mise-tools just shellcheck bun -> just shellcheck bun deno pnpm mise-banned exit 1 'deno pnpm' -> exit 0 doctor WARN PV-W30 deno.lock, WARN PV-W34 pnpm-lock.yaml -> PASS no ... leftovers Controls: node in mise.toml is still banned (exit 1); package-lock.json still warns PV-W34; deno.json is an info line. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Y6FD63UrriRBHqmhhEca9t --- .../provisioning/PROVISIONING-STANDARD.adoc | 14 ++++++--- .../provisioning-standard_praxis.deed | 6 ++-- .../templates/build/just/provision-lib.sh | 31 ++++++++++++------- .../templates/docs/SETUP.adoc.tmpl | 4 +-- .../templates/llm-warmup-dev.adoc.tmpl | 3 +- .../provisioning/templates/mise.toml.tmpl | 3 +- 6 files changed, 38 insertions(+), 23 deletions(-) diff --git a/3-practice/provisioning/PROVISIONING-STANDARD.adoc b/3-practice/provisioning/PROVISIONING-STANDARD.adoc index 8975116e9..a29a2cb55 100644 --- a/3-practice/provisioning/PROVISIONING-STANDARD.adoc +++ b/3-practice/provisioning/PROVISIONING-STANDARD.adoc @@ -116,8 +116,8 @@ and exit 0; they are replaced by `doctor: provision::doctor`, not renamed. * *mise*: tools at `latest`, `[settings] lockfile = true`; `mise.lock` pins each to a version and per-platform checksum. The base set is `just` and `shellcheck`; each detected language adds its own (rust, zig, julia, erlang+elixir, - erlang+gleam, opam, bun, lychee for docs). Banned tools (python, deno, node, - npm, yarn, go, java, make) never appear. + erlang+gleam, opam, bun+deno+pnpm for a `package.json`, lychee for docs). + Banned tools (python, node, npm, yarn, go, java, make) never appear. * *Guix*: `channels.scm` pins the commit; `manifest.scm` lists the development shell. Rust repositories get a `cargo-build-system` `guix.scm`; every other ecosystem gets a *source* package (`copy-build-system`) that says so, because a @@ -128,9 +128,13 @@ and exit 0; they are replaced by `doctor: provision::doctor`, not renamed. * *just ≥ 1.42*: a root recipe depending on a module recipe (`doctor: provision::doctor`) fails on 1.31, 1.36, 1.40 and 1.41 and works from 1.42 (measured 2026-09-30). Doctor enforces the floor as PV-E02. -* *bun* is the only JavaScript runtime. Leftover deno, Nix, Makefile, Python or - npm files are WARNs (PV-W30–W34), not FAILs. Each deno repository gets one - migration issue. +* *JavaScript* (owner ruling 2026-10-14, LANGUAGE-POLICY §1.3–§1.4): Bun > + Deno > pnpm > npm, from one `package.json`. A repository with a `package.json` + is provisioned with bun, deno and pnpm, because `js-runtimes regen` writes all + three locks (`bun.lock`, `deno.lock`, `pnpm-lock.yaml`) and governance checks + each leg. node stays banned, so npm, the last resort, is never provisioned. A + `deno.json` is a notice (PV-W30), not a leftover. Leftover Nix, Makefile, + Python, npm/yarn or TypeScript files are WARNs (PV-W31–W34), not FAILs. == 5. Template slots diff --git a/3-practice/provisioning/provisioning-standard_praxis.deed b/3-practice/provisioning/provisioning-standard_praxis.deed index 14a2e5a85..5cc07ef82 100644 --- a/3-practice/provisioning/provisioning-standard_praxis.deed +++ b/3-practice/provisioning/provisioning-standard_praxis.deed @@ -96,7 +96,9 @@ ;; tests that they agree). A tool installed through an npm:, pipx:, pip: or go: ;; backend is banned whatever its name; mise.toml, .mise.toml and ;; .tool-versions are all read (doctor PV-W23). - :banned-tools ("python" "deno" "denojs" "node" "nodejs" "npm" "yarn" "pnpm" + ;; deno and pnpm left the list on 2026-10-14 (Bun > Deno > pnpm > npm, + ;; LANGUAGE-POLICY §1.3–§1.4); node stays, so npm is never provisioned. + :banned-tools ("python" "node" "nodejs" "npm" "yarn" "typescript" "rescript" "make" "black" "ruff" "pip" "poetry" "nix" "go" "golang" "java" "kotlin") :banned-backends ("npm" "pipx" "pip" "go") @@ -105,7 +107,7 @@ :just-floor "1.42.0" :guix-channels "channels.scm" :guix-package (rust "cargo-build-system" other "copy-build-system source package") - :guix-gaps ("idris2" "gleam" "bun" "lychee")) + :guix-gaps ("idris2" "gleam" "bun" "deno" "pnpm" "lychee")) ;; ------------------------------------------------------------- slot tiers (slots diff --git a/3-practice/provisioning/templates/build/just/provision-lib.sh b/3-practice/provisioning/templates/build/just/provision-lib.sh index 08ed56b3a..1ceaaa8d0 100755 --- a/3-practice/provisioning/templates/build/just/provision-lib.sh +++ b/3-practice/provisioning/templates/build/just/provision-lib.sh @@ -24,7 +24,7 @@ # Exit: 0 ok | 1 a FAIL was found / a step failed | 2 usage error set -uo pipefail -PROVISION_LIB_VERSION="0.5.0" +PROVISION_LIB_VERSION="0.6.0" ROOT="${PROVISION_ROOT:-$(pwd)}" cd "$ROOT" || exit 2 DEED=".machine_readable/descriptiles/provisioning_praxis.deed" @@ -135,13 +135,16 @@ lang_tools() { gleam) echo "gleam erl" ;; ocaml) echo "opam dune ocaml" ;; haskell) echo "ghc cabal" ;; - bun) echo "bun" ;; + # One package.json, every runtime (LANGUAGE-POLICY §1.4): bun runs it, and + # `js-runtimes regen` needs deno and pnpm to write their locks too. + bun) echo "bun deno pnpm" ;; docs) echo "" ;; esac } # Guix package specs per language (verified 2026-09-30 against guix ae77aeb in # docker.io/metacall/guix). Guix has NO idris2, gleam, bun or lychee, and its -# julia is 1.8.5: those come from mise, which Guix itself ships ("mise"). +# julia is 1.8.5: those come from mise, which Guix itself ships ("mise"). deno +# and pnpm come from mise as well (their Guix packages were not measured). GUIX_BASE="git bash coreutils nss-certs just mise shellcheck" # Print the Guix package specifications for language $1, if available. lang_guix() { @@ -160,7 +163,7 @@ lang_guix() { # Tools a Guix shell cannot supply, so mise (inside that shell) does. lang_guix_gap() { case "$1" in - idris2) echo "idris2 (via pack)" ;; julia) echo julia ;; gleam) echo gleam ;; bun) echo bun ;; docs) echo lychee ;; + idris2) echo "idris2 (via pack)" ;; julia) echo julia ;; gleam) echo gleam ;; bun) echo "bun deno pnpm" ;; docs) echo lychee ;; esac } # Print deduplicated base and detected-language Guix package specifications. @@ -180,7 +183,7 @@ RECIPE_TOOLS="trivy" lang_mise() { case "$1" in rust) echo "rust" ;; zig) echo "zig" ;; julia) echo "julia" ;; elixir) echo "erlang elixir" ;; gleam) echo "erlang gleam" ;; ocaml) echo "opam" ;; - bun) echo "bun" ;; docs) echo "lychee" ;; esac; } + bun) echo "bun deno pnpm" ;; docs) echo "lychee" ;; esac; } # Print known recipe tools referenced outside comments in the repository Justfiles. recipe_tools() { local t f body="" @@ -212,7 +215,7 @@ lang_remedy() { gleam) echo "mise use gleam@latest erlang@latest" ;; ocaml) echo "mise use opam@latest && opam init -y && opam switch create . --deps-only -y" ;; haskell) echo "ghcup: https://www.haskell.org/ghcup/ (or: just dev-shell, which uses Guix)" ;; - bun) echo "mise use bun@latest" ;; + bun) echo "mise use bun@latest deno@latest pnpm@latest" ;; esac } @@ -339,7 +342,9 @@ has_recipe() { have just && just --summary 2>/dev/null | tr " " "\n" | grep -qx version_ge() { [ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -1)" = "$2" ]; } # Tools that must never appear in a repo's toolchain (estate language policy). -BANNED_TOOLS='python|deno|denojs|node|nodejs|npm|yarn|pnpm|typescript|rescript|make|black|ruff|pip|poetry|nix|go|golang|java|kotlin' +# deno and pnpm are permitted runtimes since the 2026-10-14 ruling (Bun > Deno > +# pnpm > npm); node stays banned, so npm, the last resort, is never provisioned. +BANNED_TOOLS='python|node|nodejs|npm|yarn|typescript|rescript|make|black|ruff|pip|poetry|nix|go|golang|java|kotlin' BANNED_BACKENDS='npm|pipx|pip|go' # --------------------------------------------------------------------------- @@ -546,7 +551,7 @@ cmd_doctor() { [ -f .tool-versions ] && warn "PV-W22 .tool-versions present — a second toolchain source; fold it into mise.toml" if [ -f mise.toml ] || [ -f .mise.toml ] || [ -f .tool-versions ]; then local bad; bad=$(mise_banned) - [ -z "$bad" ] && pass "the mise configs pin no banned tool" || warn "PV-W23 a mise config pins banned tool(s): $bad (language policy: bun, no python/deno/node/npm/make)" + [ -z "$bad" ] && pass "the mise configs pin no banned tool" || warn "PV-W23 a mise config pins banned tool(s): $bad (language policy: Bun > Deno > pnpm; no python/node/npm/make)" fi # hypatia guix_not_stub reads guix.scm AND build/guix.scm; an unfilled __PLACEHOLDER__ is a stub too. local g r gstub="" @@ -571,14 +576,16 @@ cmd_doctor() { hdr "Estate policy drift" local f any=0 - for f in deno.json deno.jsonc deno.lock import_map.json; do - [ -f "$f" ] && { warn "PV-W30 $f — Deno leftover; the estate runtime is bun (migration issue tracks it)"; any=1; } + # deno.lock is a generated lock every package carries (`js-runtimes regen`); + # authoring in Deno is permitted (tier 2), so its manifests are a notice only. + for f in deno.json deno.jsonc import_map.json; do + [ -f "$f" ] && info "PV-W30 $f — authored for Deno (permitted, tier 2); package.json stays the one manifest every runtime reads" done for f in flake.nix flake.lock shell.nix default.nix; do [ -f "$f" ] && { warn "PV-W31 $f — Nix is not an estate toolchain; Guix is"; any=1; }; done for f in Makefile GNUmakefile makefile; do [ -f "$f" ] && { warn "PV-W32 $f — Justfile is the only task runner"; any=1; }; done for f in requirements.txt pyproject.toml setup.py Pipfile; do [ -f "$f" ] && { warn "PV-W33 $f — Python is not an estate language"; any=1; }; done - for f in package-lock.json yarn.lock pnpm-lock.yaml tsconfig.json; do [ -f "$f" ] && { warn "PV-W34 $f — npm/yarn/pnpm/TypeScript leftover; bun only"; any=1; }; done - [ $any -eq 0 ] && pass "no deno / nix / make / python / npm leftovers" + for f in package-lock.json yarn.lock tsconfig.json; do [ -f "$f" ] && { warn "PV-W34 $f — npm/yarn/TypeScript leftover; package.json + bun.lock, deno.lock and pnpm-lock.yaml (js-runtimes regen)"; any=1; }; done + [ $any -eq 0 ] && pass "no nix / make / python / npm leftovers" local hook=build/just/doctor-local.sh if [ -f "$hook" ]; then diff --git a/3-practice/provisioning/templates/docs/SETUP.adoc.tmpl b/3-practice/provisioning/templates/docs/SETUP.adoc.tmpl index 71de1d1e7..a71c2e5af 100644 --- a/3-practice/provisioning/templates/docs/SETUP.adoc.tmpl +++ b/3-practice/provisioning/templates/docs/SETUP.adoc.tmpl @@ -189,11 +189,11 @@ every fix marked *auto* for you. |PV-W27 |launcher.sh is missing |as PV-W24 |no |PV-W28 |a setup document is missing |as PV-W24 |no |PV-W29 |a setup document still holds unfilled template slots |replace each `__SPEC_…__` slot with the facts for this repository |no -|PV-W30 |Deno files remain |the estate runtime is bun; a migration issue tracks it |no +|PV-W30 |a Deno manifest (`deno.json`, `deno.jsonc`, `import_map.json`) exists |a notice, not a fault: Deno is permitted (tier 2) and reads `package.json`, which stays the one manifest |no |PV-W31 |Nix files remain |use Guix |no |PV-W32 |a Makefile remains |use the Justfile |no |PV-W33 |Python files remain |not an estate language |no -|PV-W34 |npm/yarn/pnpm/TypeScript files remain |use bun |no +|PV-W34 |npm/yarn/TypeScript files remain (`package-lock.json`, `yarn.lock`, `tsconfig.json`) |keep `package.json`, and let `js-runtimes regen` write `bun.lock`, `deno.lock` and `pnpm-lock.yaml` |no |PV-W35 |both guix.scm and build/guix.scm exist |keep one; the engine uses build/ |no |=== diff --git a/3-practice/provisioning/templates/llm-warmup-dev.adoc.tmpl b/3-practice/provisioning/templates/llm-warmup-dev.adoc.tmpl index c85e1187c..0a3030273 100644 --- a/3-practice/provisioning/templates/llm-warmup-dev.adoc.tmpl +++ b/3-practice/provisioning/templates/llm-warmup-dev.adoc.tmpl @@ -40,7 +40,8 @@ __SPEC_LAYOUT__ == Rules that bite -* Languages: bun is the JS runtime; Python, Deno, TypeScript, ReScript, Nix, +* Languages: JavaScript runs Bun > Deno > pnpm > npm from one `package.json`; + after `bun add`, run `js-runtimes regen`. Python, TypeScript, ReScript, Nix, Go and Makefiles are banned. New code follows the repository's existing languages. * Every new file carries an SPDX header: `__LICENSE__` for code and config, `__DOC_LICENSE__` for prose docs. Never change an existing file's licence. diff --git a/3-practice/provisioning/templates/mise.toml.tmpl b/3-practice/provisioning/templates/mise.toml.tmpl index be4bc98a7..d11e2f9a1 100644 --- a/3-practice/provisioning/templates/mise.toml.tmpl +++ b/3-practice/provisioning/templates/mise.toml.tmpl @@ -5,7 +5,8 @@ # records the concrete version and per-platform checksum of each, so every # checkout installs the same bits. Refresh both with: just toolchain-refresh # Canon: hyperpolymath/standards 3-practice/provisioning (TRUST-DEFAULTS §toolchain). -# Estate policy: no python, deno, node/npm/yarn, make or nix here; the JS runtime is bun. +# Estate policy: no python, node/npm/yarn, make or nix here. JavaScript runs Bun > Deno > +# pnpm from one package.json, so a repository with one pins all three (LANGUAGE-POLICY §1.4). [settings] lockfile = true From f22d5a6f9c83aab6b39b03e131c2f9252b8e1582 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 11:30:59 +0000 Subject: [PATCH 2/2] test(governance): freeze the two JS runtimes job names added by #1249 standards#1249 added the `js-runtimes-provision` and `js-runtimes` jobs to governance-reusable.yml but not to the context-freeze list in tests/test_governance_reusable_shape.sh, so Self Test has been red on main since 25d23cf3 ("job names drifted from the frozen list"). The test's own header allows additions ("Adding a job is allowed (it adds a context)"); the equality check just needs the two names. A rename still fails (checked by renaming "Debt ratchet" locally). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Y6FD63UrriRBHqmhhEca9t --- tests/test_governance_reusable_shape.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_governance_reusable_shape.sh b/tests/test_governance_reusable_shape.sh index 8283661c2..9e89ac969 100755 --- a/tests/test_governance_reusable_shape.sh +++ b/tests/test_governance_reusable_shape.sh @@ -50,7 +50,9 @@ Trusted-base reduction policy Licence consistency Exemption ratchet Debt ratchet -UUID v7 conformance" +UUID v7 conformance +JS runtimes — environment provisions Bun first +JS runtimes — \${{ matrix.runtime }}" ACTUAL=$(grep -P '^ name: ' "$F" | sed 's/^ name: //') if [ "$(printf '%s' "$FROZEN" | sort)" = "$(printf '%s' "$ACTUAL" | sort)" ]; then ok "job names match the frozen context list ($(printf '%s\n' "$ACTUAL" | wc -l) jobs)" else bad "job names drifted from the frozen list — renames create phantom contexts estate-wide"; diff <(printf '%s\n' "$FROZEN" | sort) <(printf '%s\n' "$ACTUAL" | sort); fi