From 4e9142b3f7bf12e9b1ff2c27f7304ebb465afd20 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Wed, 29 Jul 2026 06:55:11 +0100 Subject: [PATCH] fix(ci): Bun-first runtime policy; retire two gates that enforced the opposite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Authority: standards LANGUAGE-POLICY.adoc ยง1 (added 2026-07-29) -- Bun > Deno > pnpm > npm. REPLACED npm-bun-blocker.yml -> runtime-policy.yml The old gate failed any build carrying bun.lockb with 'npm/bun artifacts detected. Use Deno instead.' It blocked what is now the FIRST-choice runtime and mandated the second. Present in 55 repos; zero repos had adopted Bun, because adopting it would have turned them red. The replacement fails on something real -- MIXED TOOLCHAINS, two package managers' lockfiles in one repo, i.e. two dependency graphs that can disagree -- and reports the tier in use otherwise. npm is tier 4 but PERMITTED, so it warns rather than blocks. Red-teamed both directions before shipping: none/bun/deno/npm alone -> exit 0; bun+npm and deno+pnpm -> exit 1. DELETED ts-blocker.yml It enforced 'use ReScript instead'. ReScript is itself retired estate-wide, so it policed a dead alternative. Worse, it could not fail: it diffed git diff HEAD~1 on a depth-1 checkout, where HEAD~1 does not exist, sent the error to /dev/null and used || true -- so the match set was ALWAYS empty. Measured 20/20 runs green; it has never once fired. Neither workflow is a required status check in any ruleset, so removing them creates no phantom context. Co-Authored-By: Claude Opus 5 --- .github/workflows/npm-bun-blocker.yml | 30 ----------- .github/workflows/runtime-policy.yml | 71 +++++++++++++++++++++++++++ .github/workflows/ts-blocker.yml | 35 ------------- 3 files changed, 71 insertions(+), 65 deletions(-) delete mode 100644 .github/workflows/npm-bun-blocker.yml create mode 100644 .github/workflows/runtime-policy.yml delete mode 100644 .github/workflows/ts-blocker.yml diff --git a/.github/workflows/npm-bun-blocker.yml b/.github/workflows/npm-bun-blocker.yml deleted file mode 100644 index 2c56ae7..0000000 --- a/.github/workflows/npm-bun-blocker.yml +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -name: "๐Ÿ”ด Gate: No-npm/bun policy" -on: - push: - branches: [main, master] - pull_request: - -# Estate guardrail: scope push to default branches so a PR fires once (not -# push+PR), and cancel superseded runs. Safe โ€” read-only PR-triggered check. -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read -jobs: - check: - runs-on: ubuntu-latest - timeout-minutes: 15 - permissions: - contents: read - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Block npm/bun - run: | - if [ -f "package-lock.json" ] || [ -f "bun.lockb" ] || [ -f ".npmrc" ]; then - echo "โŒ npm/bun artifacts detected. Use Deno instead." - exit 1 - fi - echo "โœ… No npm/bun violations" diff --git a/.github/workflows/runtime-policy.yml b/.github/workflows/runtime-policy.yml new file mode 100644 index 0000000..a376c70 --- /dev/null +++ b/.github/workflows/runtime-policy.yml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: MPL-2.0 +# Runtime and package-manager policy check. +# +# Authority: hyperpolymath/standards LANGUAGE-POLICY.adoc ยง1. +# Ordering: Bun (1st) > Deno (2nd) > pnpm (3rd) > npm (last resort). +# +# REPLACES npm-bun-blocker.yml, which failed any build carrying `bun.lockb` with +# the message "npm/bun artifacts detected. Use Deno instead." That gate blocked +# what is now the FIRST-choice runtime and mandated the second. It was present in +# 55 repositories. +# +# What this fails on, deliberately: +# MIXED TOOLCHAINS -- two different package managers' lockfiles in one repo. +# That is real, actionable drift: two dependency graphs that can disagree. +# What it does NOT fail on: +# Using bun, deno, pnpm or npm. npm is LAST but PERMITTED; the check reports +# the tier in use so drift is visible without blocking legitimate work. +name: Runtime Policy +on: + push: + branches: [main, master] + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + runtime-policy: + name: Runtime Policy + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Report runtime tier and reject mixed toolchains + run: | + set -euo pipefail + + bun=0; deno=0; pnpm=0; npm=0 + [ -f bun.lockb ] || [ -f bun.lock ] && bun=1 || true + [ -f deno.lock ] || [ -f deno.json ] || [ -f deno.jsonc ] && deno=1 || true + [ -f pnpm-lock.yaml ] && pnpm=1 || true + [ -f package-lock.json ] && npm=1 || true + + total=$((bun + deno + pnpm + npm)) + + if [ "$total" -eq 0 ]; then + echo "::notice::No JS/TS package manager in use โ€” nothing to check." + exit 0 + fi + + # Report the tier actually in use (LANGUAGE-POLICY.adoc ยง1). + [ "$bun" -eq 1 ] && echo "Bun โ€” tier 1 (preferred)" + [ "$deno" -eq 1 ] && echo "Deno โ€” tier 2 (accepted; existing projects are grandfathered)" + [ "$pnpm" -eq 1 ] && echo "pnpm โ€” tier 3" + [ "$npm" -eq 1 ] && echo "::warning::npm lockfile present. npm is tier 4, the last resort โ€” permitted, never preferred. See LANGUAGE-POLICY.adoc ยง1." + + if [ "$total" -gt 1 ]; then + echo "::error::Mixed toolchains: $total package managers have lockfiles in this repository." + echo "Two dependency graphs that can disagree is real drift. Pick one โ€” preferring the" + echo "highest tier present โ€” and delete the others' lockfiles." + exit 1 + fi + + echo "โœ… Single package manager in use." diff --git a/.github/workflows/ts-blocker.yml b/.github/workflows/ts-blocker.yml deleted file mode 100644 index 6e9b743..0000000 --- a/.github/workflows/ts-blocker.yml +++ /dev/null @@ -1,35 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -name: "๐Ÿ”ด Gate: No-TypeScript policy" -on: - push: - branches: [main, master] - pull_request: - -# Estate guardrail: scope push to default branches so a PR fires once (not -# push+PR), and cancel superseded runs. Safe โ€” read-only PR-triggered check. -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read -jobs: - check: - runs-on: ubuntu-latest - timeout-minutes: 15 - permissions: - contents: read - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Block new TypeScript/JavaScript - run: | - NEW_TS=$(git diff --name-only --diff-filter=A HEAD~1 2>/dev/null | grep -E '\.(ts|tsx)$' | grep -v '\.gen\.' || true) - NEW_JS=$(git diff --name-only --diff-filter=A HEAD~1 2>/dev/null | grep -E '\.(js|jsx)$' | grep -v '\.res\.js$' | grep -v '\.gen\.' | grep -v 'node_modules' || true) - - if [ -n "$NEW_TS" ] || [ -n "$NEW_JS" ]; then - echo "โŒ New TS/JS files detected. Use ReScript instead." - [ -n "$NEW_TS" ] && echo "$NEW_TS" - [ -n "$NEW_JS" ] && echo "$NEW_JS" - exit 1 - fi - echo "โœ… ReScript policy enforced"