// SPDX-License-Identifier: CC-BY-SA-4.0
= JS Runtime & Package Management Policy
:revdate: 2026-05-31
:status: ACTIVE
:issue-67: hyperpolymath/standards#67
:issue-68: hyperpolymath/standards#68
:issue-253: hyperpolymath/standards#253

This document is the canonical estate-wide policy for JavaScript/TypeScript
runtimes and package management.  It is referenced by
`governance-reusable.yml` (enforcement) and the canonical template
`.gitignore` files (rsr-template-repo, v3-templater).

See also: `scripts/purge-node-modules.sh` (remediation utility) and
`docs/migrations/npm-to-deno-template/MIGRATION.md` (per-repo recipe).

[NOTE]
====
*npm → Deno migration: substantially complete 2026-05-31.* Umbrella
{issue-253} and the seven STEP issues (#261/#262/#265/#268/#270/#273/#275)
all closed. ~22 physical-migration PRs landed in STEP 3 plus
named-bucket audits closed `SUBSTANTIALLY DONE` for STEPS 4/5/6/7.
Residual longtail repos (`flat-mate`, `accessibility-everywhere`,
`kaldor-iiot`, `excel-economic-numbers-tool`, `ubicity`, a handful of
heavier Vite/Vitest/Jest/Express stacks) are tracked as per-repo
follow-ups rather than blocking the umbrella close. The hypatia rule
`cicd_rules/nodejs_detected` remains armed with nine carve-out classes
(see `.claude/CLAUDE.md §npm Exemptions`).
====

== Runtime Hierarchy

Tool selection MUST follow this hierarchy:

[cols="1,2,3", options="header"]
|===
| Priority | Tool | Rationale

| 1
| *Bun*
| Default for all new work.  Executes TypeScript directly with no build step;
built-in test runner and bundler.

| 2
| *Deno*
| Second position.  Existing Deno projects are *grandfathered* and need not
migrate; prefer it over pnpm/npm where Bun genuinely cannot be used.

| 3
| *pnpm*
| Third.  Only where an upstream toolchain requires a `node_modules` layout.

| 4
| *npm*
| Last resort.  Permitted, never preferred.  Reaching for npm should be a
deliberate, noted decision — not a default.
|===

[IMPORTANT]
====
*Corrected 2026-08-07.* This table previously ran `Deno > Bun > pnpm > npm`,
the exact inverse of `LANGUAGE-POLICY.adoc` §1, which has been the ruling since
2026-07-29. The ordering above now matches it. `LANGUAGE-POLICY.adoc` is
authoritative; if the two ever disagree again, that one wins.
====

== Hard Rules (enforced by governance-reusable.yml)

1. `package-lock.json` MUST NOT be tracked in any repo. ({issue-67})
   - Add to `.gitignore` via canonical template propagation.
   - If already tracked: `git rm --cached package-lock.json` and add to
     `.gitignore`.  Use `purge-node-modules.sh --force` to clean local
     copies.
2. `yarn.lock` and `.npmrc` MUST NOT be tracked.
   - `bun.lock` / `bun.lockb` ARE permitted and expected: Bun is tier 1, and
     its model is an npm-compatible `package.json` plus a Bun lockfile. The
     previous form of this rule, together with the matching check in
     `governance-reusable.yml`, made adopting the tier-1 runtime impossible.
3. `node_modules/` MUST NOT be tracked (already in canonical `.gitignore`).
4. `.editorconfig` and `.claude/` MUST NOT be tracked. ({issue-68})
   - These are local-only (agent scaffolding + editor config).
   - Add to `.gitignore` via canonical template propagation.

== Canonical .gitignore Entries (template source of truth)

The following entries MUST be present in every repo's `.gitignore` (carried
from rsr-template-repo and v3-templater via template propagation):

----
# npm-avoidant (standards#67): estate JS-runtime policy is Bun>Deno>pnpm>npm.
# npm lockfiles must never be committed estate-wide.
package-lock.json
**/package-lock.json

# Agent & editor scaffolding (standards#68) — local-only, never committed
# estate-wide. Owner decision 2026-05-16: gitignore both rather than commit
# per-repo agent/editor config.
.editorconfig
.claude/
----

== Remediation

=== Remove a tracked package-lock.json

[source,bash]
----
git rm --cached package-lock.json
echo 'package-lock.json' >> .gitignore
echo '**/package-lock.json' >> .gitignore
git commit -m "chore(gitignore): remove tracked package-lock.json (standards#67)"
----

=== Bulk remediation (estate-wide audit)

Use the propagation script at `scripts/propagate-gitignore-67-68.sh` in this
repo to identify and prepare per-repo branches.  The script is READ-ONLY by
default (dry-run); pass `--fix` to stage changes for review.

See also: `npm-avoidant/scripts/purge-node-modules.sh` for removing local
`node_modules/` and lockfile debris.

== Consumer-repo Audit (2026-05-19 snapshot)

Obtained via `git ls-files` sweep across `/home/hyperpolymath/dev/repos/`:

[cols="2,1", options="header"]
|===
| Metric | Count

| Repos with tracked `package-lock.json`
| 10

| Repos with tracked `.editorconfig`
| 118

| Repos with tracked `.claude/` files
| 56
|===

Detailed list of repos with tracked `package-lock.json` (primary blocker for
standards#67):

* `ci` (nested: `firefox-lsp/vscode-extension/package-lock.json`)
* `claude-integrations` (nested: `firefox-lsp/vscode-extension/package-lock.json`)
* `developer-ecosystem` (nested: `rescript-ecosystem/rescript-tea/package-lock.json`)
* `git-scripts` (nested: `ui/package-lock.json`)
* `hyperpolymath-archive` (nested: `flatracoon-netstack/interface/package-lock.json`)
* `panll` (root)
* `repos-monorepo` (nested: multiple `boj-cartridges/` sub-packages)
* `rescript-tea` (root)
* `typed-wasm` (root)
* `v3-templater` (root — RESOLVED by PR #68/69, now in `.gitignore`)

NOTE: `.editorconfig` (118 repos) and `.claude/` (56 repos) are already-committed
historical files.  The gitignore fix prevents new commits; removing existing
tracked copies is a separate per-repo chore, owner-gated.  See
`scripts/propagate-gitignore-67-68.sh` for the propagation recipe.
