Skip to content

fix(policy): make the Bun-over-Deno ruling actually effective - #592

Merged
hyperpolymath merged 2 commits into
mainfrom
chore/bun-over-deno-make-the-ruling-effective
Aug 7, 2026
Merged

fix(policy): make the Bun-over-Deno ruling actually effective#592
hyperpolymath merged 2 commits into
mainfrom
chore/bun-over-deno-make-the-ruling-effective

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

LANGUAGE-POLICY.adoc section 1 has ruled Bun > Deno > pnpm > npm since
2026-07-29, and section 1.2 retired the "use ReScript instead" rule because
ReScript is no longer used in this estate. The ruling was recorded. It was not
in force: every surface that ACTS still encoded its predecessor.

WHAT WAS ACTUALLY HAPPENING

.github/workflows/governance-reusable.yml
Failed any repository carrying bun.lockb -- "Use Deno instead" -- and any
package.json declaring runtime dependencies. Bun's model IS an
npm-compatible package.json plus a Bun lockfile, so this gate did not
merely discourage the tier-1 runtime, it made adopting it impossible.
This reusable is called estate-wide.

docs/JS-RUNTIME-POLICY.adoc
Stated the hierarchy as Deno > Bun > pnpm > npm -- the exact inverse of
LANGUAGE-POLICY section 1 -- and rule 2 required that bun.lockb MUST NOT
be tracked. This is the document governance-reusable.yml cites for
remediation, so a repository following the error message was directed
further from the policy.

.claude/CLAUDE.md
Listed Bun in the BANNED table with Deno as its replacement, and
described Deno as "replaces Node/npm/bun". This is the file agents read
first, so recorded policy and agent behaviour had diverged: agents were
being instructed to migrate away from the estate's first-choice runtime.

CHANGES

governance-reusable.yml bun.lock / bun.lockb accepted and reported as tier
1. package.json runtime dependencies accepted when
a Bun lockfile is present, still flagged otherwise.
yarn.lock and .npmrc still fail; remediation text
now names Bun rather than Deno. The
package-lock.json rule (standards#67) is UNCHANGED
in behaviour -- only its message was corrected.

JS-RUNTIME-POLICY.adoc Hierarchy table inverted to match LANGUAGE-POLICY,
with a note recording that LANGUAGE-POLICY is
authoritative if the two ever disagree again.
Rule 2 split: yarn.lock and .npmrc still banned,
Bun lockfiles explicitly permitted and expected.

.claude/CLAUDE.md Bun promoted to a tier-1 row; the banned rows for
Node.js/npm/Bun/pnpm-yarn replaced with accurate
ones. npm is described as tier 4 -- permitted,
never preferred -- which is what the policy says,
rather than "banned".

Also corrected: docs/migrations/npm-to-deno-template/MIGRATION.md,
meta-a2ml/.claude/CLAUDE.md, rhodium-standard-repositories/PROJECT-STATUS.md
and rhodium-standard-repositories/.github/workflows/language-policy.yml.

DELIBERATELY NOT CHANGED

  • The package-lock.json prohibition. LANGUAGE-POLICY calls npm "permitted,
    never preferred" while standards#67 hard-fails a tracked lockfile. That
    tension predates this change and deserves its own decision.

  • .claude/CLAUDE.md bans TypeScript in favour of AffineScript, while
    LANGUAGE-POLICY section 1.2 says TypeScript is "permitted under Bun".
    Both cannot be right. The AffineScript direction is a substantive
    architectural position, not an oversight, so it is flagged in the file for
    an owner ruling rather than changed here.

  • consent-aware-http/.claude/CLAUDE.md carries the same stale table but that
    directory is being extracted to metadatastician/consent-aware-web in a
    separate PR, so it is left alone here to avoid a conflict.

STILL BROKEN ELSEWHERE (cannot be fixed from this repository)

  • The metadatastician organisation Actions allowlist has patterns_allowed
    containing 'denoland/' but not 'oven-sh/'. Any workflow referencing
    oven-sh/setup-bun is rejected with startup_failure, zero jobs, and no
    explanatory log. Adding the pattern per-repo returns 409 Conflict --
    it is set at organisation level. Needs an org admin.

  • hyperpolymath/rsr-template-repo ships .cursorrules, .clinerules,
    .windsurfrules and .github/copilot-instructions.md all instructing
    "TypeScript -> use ReScript" and "Node.js / npm / bun -> use Deno". These
    propagate to every mint. Corrected wording exists in
    metadatastician/consent-aware-web and can be lifted verbatim; a separate PR
    is needed against the template.

Found while extracting consent-aware-http into its own repository, where the
manifest validator had to be written dependency-free specifically to avoid
tripping the gate this commit fixes.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

🤖 Generated with Claude Code

LANGUAGE-POLICY.adoc section 1 has ruled Bun > Deno > pnpm > npm since
2026-07-29, and section 1.2 retired the "use ReScript instead" rule because
ReScript is no longer used in this estate. The ruling was recorded. It was not
in force: every surface that ACTS still encoded its predecessor.
WHAT WAS ACTUALLY HAPPENING
.github/workflows/governance-reusable.yml
Failed any repository carrying bun.lockb -- "Use Deno instead" -- and any
package.json declaring runtime dependencies. Bun's model IS an
npm-compatible package.json plus a Bun lockfile, so this gate did not
merely discourage the tier-1 runtime, it made adopting it impossible.
This reusable is called estate-wide.
docs/JS-RUNTIME-POLICY.adoc
Stated the hierarchy as Deno > Bun > pnpm > npm -- the exact inverse of
LANGUAGE-POLICY section 1 -- and rule 2 required that bun.lockb MUST NOT
be tracked. This is the document governance-reusable.yml cites for
remediation, so a repository following the error message was directed
further from the policy.
.claude/CLAUDE.md
Listed Bun in the BANNED table with Deno as its replacement, and
described Deno as "replaces Node/npm/bun". This is the file agents read
first, so recorded policy and agent behaviour had diverged: agents were
being instructed to migrate away from the estate's first-choice runtime.
CHANGES
governance-reusable.yml bun.lock / bun.lockb accepted and reported as tier
1. package.json runtime dependencies accepted when
a Bun lockfile is present, still flagged otherwise.
yarn.lock and .npmrc still fail; remediation text
now names Bun rather than Deno. The
package-lock.json rule (standards#67) is UNCHANGED
in behaviour -- only its message was corrected.
JS-RUNTIME-POLICY.adoc Hierarchy table inverted to match LANGUAGE-POLICY,
with a note recording that LANGUAGE-POLICY is
authoritative if the two ever disagree again.
Rule 2 split: yarn.lock and .npmrc still banned,
Bun lockfiles explicitly permitted and expected.
.claude/CLAUDE.md Bun promoted to a tier-1 row; the banned rows for
Node.js/npm/Bun/pnpm-yarn replaced with accurate
ones. npm is described as tier 4 -- permitted,
never preferred -- which is what the policy says,
rather than "banned".
Also corrected: docs/migrations/npm-to-deno-template/MIGRATION.md,
meta-a2ml/.claude/CLAUDE.md, rhodium-standard-repositories/PROJECT-STATUS.md
and rhodium-standard-repositories/.github/workflows/language-policy.yml.
DELIBERATELY NOT CHANGED
- The package-lock.json prohibition. LANGUAGE-POLICY calls npm "permitted,
never preferred" while standards#67 hard-fails a tracked lockfile. That
tension predates this change and deserves its own decision.
- .claude/CLAUDE.md bans TypeScript in favour of AffineScript, while
LANGUAGE-POLICY section 1.2 says TypeScript is "permitted under Bun".
Both cannot be right. The AffineScript direction is a substantive
architectural position, not an oversight, so it is flagged in the file for
an owner ruling rather than changed here.
- consent-aware-http/.claude/CLAUDE.md carries the same stale table but that
directory is being extracted to metadatastician/consent-aware-web in a
separate PR, so it is left alone here to avoid a conflict.
STILL BROKEN ELSEWHERE (cannot be fixed from this repository)
- The metadatastician organisation Actions allowlist has patterns_allowed
containing 'denoland/*' but not 'oven-sh/*'. Any workflow referencing
oven-sh/setup-bun is rejected with startup_failure, zero jobs, and no
explanatory log. Adding the pattern per-repo returns 409 Conflict --
it is set at organisation level. Needs an org admin.
- hyperpolymath/rsr-template-repo ships .cursorrules, .clinerules,
.windsurfrules and .github/copilot-instructions.md all instructing
"TypeScript -> use ReScript" and "Node.js / npm / bun -> use Deno". These
propagate to every mint. Corrected wording exists in
metadatastician/consent-aware-web and can be lifted verbatim; a separate PR
is needed against the template.
Found while extracting consent-aware-http into its own repository, where the
manifest validator had to be written dependency-free specifically to avoid
tripping the gate this commit fixes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gitar-bot

This comment has been minimized.

@gitar-bot
gitar-botBot enabled auto-merge (squash) August 7, 2026 11:46
gitar-bot[bot]
gitar-botBot previously approved these changes Aug 7, 2026

@gitar-botgitar-botBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gitar has auto-approved this PR and enabled auto-merge (configure)

@hyperpolymath

Copy link
Copy Markdown
OwnerAuthor

Correction to the last paragraph of the description, which said the oven-sh/* allowlist gap was the reason Bun setup actions were rejected.

That was incomplete. Fixing the allowlist did not unblock it. A probe branch on metadatastician/consent-aware-web established two independent blockers, either sufficient on its own:

  1. Allowlistoven-sh is not a GitHub-verified creator (is_verified: false), so verified_allowed: true did not cover it and github_owned_allowed does not apply. Under allowed_actions: selected with no oven-sh/* pattern, the action was not permitted. Fixed 2026-08-07oven-sh/* added to the org allowlist, 19 → 20 patterns, nothing removed, denoland/* retained. Verified to propagate to consent-aware-web and sr71-blackglider.
  2. Lockfile — removing only the oven-sh/setup-bun@v2.2.0 entry from actions.lock's dependencies: section, with the allowlist fixed and everything else identical, reproduced startup_failure.

Proven working afterwards; probe branch deleted.

Two further things worth knowing for anyone adopting Bun:

  • The lockfile model wants a TAG ref in the workflow (@v2.2.0), with actions.lock recording the tag → commit pin. A raw SHA has no tag to resolve, so gh actions-lock cannot create a dependency entry and the workflow is rejected. That is the estate's SHA pinning.
  • Never hand-edit actions.lock. Changing a dependency key without its ref: body makes the file unreadable by gh actions-lock entirely — it refuses to run rather than repairing itself.

Method note, since it nearly went the other way: the API accepting the allowlist PUT, and reading the pattern back at both org and repo level, was not evidence that a workflow would start. Only running one was. This PR's substance is unaffected — governance-reusable.yml genuinely did fail any repo carrying a Bun lockfile — but the attribution in that closing paragraph needed correcting.

'Registry + topology in sync' failed with 'REGISTRY.a2ml is stale'.
This PR edits two files under rhodium-standard-repositories/, which is a spec
home in the registry, and the registry records a source_hash per spec. Editing
anything under a spec's home directory invalidates that hash, and nothing warns
at edit time — the only signal is this gate, after push.
Regenerated; build-registry.sh --check now reports in sync.
Rule of thumb worth writing down: 'just registry' (== bash
scripts/build-registry.sh) must be the LAST thing run before committing any
change that touches a spec home. Running it first, then making further edits,
produces exactly this failure.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

hyperpolymath added a commit to hyperpolymath/rsr-template-repo that referenced this pull request Aug 7, 2026
All four agent-instruction files, plus the arrival-pack source that
generates `CLAUDE.md`, instructed agents:
```
TypeScript -> use ReScript
Node.js / npm / bun -> use Deno
```
Both have been wrong since 2026-07-29.
`standards/LANGUAGE-POLICY.adoc` §1 rules **Bun > Deno > pnpm > npm**.
§1.2 retires the ReScript rule outright, because ReScript is no longer
used in this estate — so that rule pointed agents at a dead alternative.
**These files propagate to every mint.** Every repository created from
this template is born instructing its agents to migrate away from the
estate's first-choice runtime and toward a language nobody uses. A
ruling that lives only in the document a human reads, while the files
that steer behaviour encode its predecessor, is recorded rather than in
force.
## Changed
| File | Why it matters |
|---|---|
| `.machine_readable/ai/.cursorrules` | read by Cursor |
| `.machine_readable/ai/.clinerules` | read by Cline |
| `.machine_readable/ai/.windsurfrules` | read by Windsurf |
| `.github/copilot-instructions.md` | read by Copilot |
| `.machine_readable/arrival-pack/arrival-pack.ncl` | generates
`CLAUDE.md` — without this the correction is undone on the next `just
claude-md` |
npm is now described as **tier 4 — permitted, never preferred**, which
is what the policy says, rather than banned. Go and Python remain
banned; those rows are untouched.
## Verification
`metadatastician/consent-aware-web` was minted from this template at
`c250e661`, carries this exact wording, and its regenerated `CLAUDE.md`
renders correctly.
## Related
- hyperpolymath/standards#592 — same inversion in
`governance-reusable.yml`, `docs/JS-RUNTIME-POLICY.adoc` and
`.claude/CLAUDE.md`. That one matters most: the gate **failed any repo
carrying `bun.lockb`**, and Bun's model is `package.json` + `bun.lock`,
so adopting tier 1 was impossible rather than merely awkward.
- Not fixable from either repo: the `metadatastician` org Actions
allowlist permits `denoland/*` but not `oven-sh/*`, so any workflow
using `oven-sh/setup-bun` is rejected with `startup_failure` and zero
jobs. Needs an org admin.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@hyperpolymath
hyperpolymath merged commit b2eb1ef into mainAug 7, 2026
35 of 36 checks passed
@hyperpolymath
hyperpolymath deleted the chore/bun-over-deno-make-the-ruling-effective branch August 7, 2026 13:29
hyperpolymath added a commit that referenced this pull request Aug 7, 2026
REGISTRY.a2ml conflicted on rebase. It is a GENERATED file, so the resolution
is to regenerate from the source spec table in scripts/build-registry.sh rather
than hand-merge two machine outputs. build-registry.sh --check reports in sync.
Verified after rebase: consent-aware-http/ is still removed, the external
pointer for metadatastician/consent-aware-web is present, and #592's Bun
changes to .claude/CLAUDE.md are preserved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Aug 7, 2026
#593)
Removes **189 files** across two divergent copies and retires a phantom
submodule. Full reasoning in
`docs/decisions/ADR-005-extract-consent-aware-web.adoc`.
New home: **https://github.com/metadatastician/consent-aware-web**
(public, live, CI green).
## Why this was more than a directory move
| | |
|---|---|
| **Rated 1 of 5 MUSTs** | by this repo's own scorecard (2026-07-03),
while shipping an `RSR-COMPLIANCE.md` claiming *"RSR Gold, 100%
compliant"* — asserting PASS for `LICENSE.txt`, `MAINTAINERS.md`,
`CHANGELOG.md`, `flake.nix` and a whole `.well-known/` tree, none of
which existed |
| **Phantom submodule** | `.gitmodules` declared
`satellites/consent-aware-http` as a submodule of
`hyperpolymath/consent-aware-http` — a repo that has **never existed**.
Materialised as 81 ordinary tracked files, 26 behind the live copy |
| **The flagship draft existed twice** | with different normative text.
The root copy is byte-identical to the satellite copy (the retired
ancestor); the `drafts/` copy is the later edit and the only valid
RFC-XML v3. They disagreed on `category`: `std` vs `info` |
## Registry — regenerated, not hand-edited
`AGENTS.md` says `REGISTRY.a2ml` and `TOPOLOGY.md` are generated and
must never be edited directly. The source of truth is the spec table
inside `scripts/build-registry.sh`; the entry moved from the **in-tree**
table to the **EXTERNAL** table, so this repo now holds a verified
pointer rather than a copy — the pattern already used for the
AffineScript specs. Same for `COMPLIANCE-DASHBOARD.md` via
`build-scorecards.sh`.
`bash scripts/build-registry.sh --check` → *OK: registry + topology are
in sync with the file tree.*
⚠️ `spec_kind` is recorded as `service-coupled`. The enum is
`language-coupled | service-coupled`, and a protocol specification is
honestly neither — `service-coupled` is the closer reading rather than a
good one. **Worth a third value.**
## Salvaged before deletion
The satellite carried a **418-line `GOVERNANCE.adoc`** — Tri-Perimeter
Contribution Framework, decision authority by perimeter, funding model,
IETF coordination — that exists nowhere else. The live copy's
`docs/governance.md` is an unrelated essay on consent in institutions;
the two share a name and nothing else. Copied into the new repository
first.
This is the failure mode `AGENTS.md` warns about: cold is not
disposable. A de-duplication by filename would have destroyed it.
## Licence references — kept, not removed
Those in `LICENCE-POLICY.adoc`, `PALIMPSEST.adoc`, `README.adoc`,
`.claude/CLAUDE.md` and `.machine_readable/licensing-policy.toml` are
statements about a **repository**, not a directory. They remain correct
and now name the new location. The PMPL prospective carve-out and the
IETF-mandated MIT carve-out both still apply. **No licence file was
edited.**
## Also repointed
Seven dangling relative links under `docs/UX-standards/` and
`docs/wikis/developers/`, `.well-known/humans.txt`, and one
`.hypatia-baseline.json` finding whose file no longer exists here.
## Left open
The two drafts name **different author organisations** (`NUJ Ethics
Council` vs `National Union of Journalists`). Normatively visible in a
published Internet-Draft. Recorded as a blocker in the new repo's
`STATE.a2ml`; not decided here.
## Related
- #592 — Bun-over-Deno ruling made effective
- hyperpolymath/rsr-template-repo#25 — agent rule files said Bun was
banned
- hyperpolymath/rsr-template-repo#26 — `repo-init.just:305` self-name
guard
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Aug 7, 2026
#599)
## The deadlock
`.github/CODEOWNERS` made `@hyperpolymath` the **sole** owner of every
path, and the Base ruleset sets `require_code_owner_review: true`.
GitHub will not let a pull request's author satisfy the code-owner
requirement themselves. With one owner who is also always the author,
**the rule could not be satisfied by anybody** — every
maintainer-authored PR sat permanently `BLOCKED`.
The evidence is in the merge history: #592, #593 and #597 all merged
with `DISMISSED` / `CHANGES_REQUESTED` and **zero approving reviews**,
via the admin bypass (`RepositoryRole id=2, mode=always`).
A rule that can never pass doesn't gate anything. It just makes bypass
the normal path — and once bypass is routine it stops carrying
information, which is how three *other* broken rules in this same
ruleset went unnoticed for months.
## The fix
`@JoshuaJewell` (repo admin, the only other org member) is named
co-owner, so either maintainer can approve the other's work and the
requirement becomes satisfiable for the first time.
**On every line, not just `*`.** In CODEOWNERS the last matching rule
wins, and a specific path line *replaces* the default rather than adding
to it. Co-owning only the `*` default would leave `SECURITY.md`,
`.github/workflows/`, `.machine_readable/`, `LICENSE`, `*.sh` and the
rest single-owned — and any PR touching them still deadlocked.
## Bootstrapping
The ruleset reads CODEOWNERS from the **base** branch, so this change
only takes effect once it is on `main`. This PR itself therefore still
needs one admin-bypass merge. After that, code-owner review works
normally.
## Context
This is the fourth of four independent deadlocks found in this ruleset
on 2026-08-07:
1. `code_quality` / `code_coverage` / `copilot_code_review` — features
producing no PR result — **removed**
2. `required_deployments: github-pages` — Pages deploys on `push` only,
so no PR head can ever have one — **removed** (18 other repos still
affected; see `scripts/clear-pages-deployment-deadlock.sh`)
3. `code_scanning` @ error threshold — 13 alerts from May–July, all
already excused in `.hypatia-baseline.json` — **resolved**, 12 dismissed
4. `require_code_owner_review` — **this PR**
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gitar-approvedAdded by Gitar

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@hyperpolymath