1 Commits
Author SHA1 Message Date
clawbot c1b469d2d0 Commit yarn 4 in the tree and install with --immutable (closes #149)
check / check (push) Canceled after 0s
Follows sneak's ruling at
#149 (comment). This
repository now carries yarn 4.18.1 under .yarn/releases, and whichever
yarn the distribution packages hands the work to it. yarn.lock is
converted once, and script/bootstrap installs with --immutable instead
of stopping on yarn's major version. Node on Alpine is nodejs-current.
Every Dockerfile stage that needs yarn also copies .yarnrc.yml and
.yarn/releases/. The policies and both checklists say so, with the
steps for converting and for a new repository.

Model: opus-5-5
2026-11-04 18:00:43 +00:00
8 changed files with 32 additions and 52 deletions
+4 -7
View File
@@ -35,12 +35,6 @@
# `services/api/.claude/` and must add its own anchored entry.
.claude
# yarn's install state and cache stay out, and its release file stays
# in. Docker obeys the last matching line, so these two lines sit above
# the secret patterns, which must win under .yarn/releases/.
.yarn/*
!.yarn/releases
# Environment files. `*.env` covers bare `.env` and the `prod.env`
# convention. Re-include a committed template with a negation if the
# build needs one: `!docs/example.env`.
@@ -61,8 +55,11 @@
**/[iI][dD]_[eE][dD]25519
**/[iI][dD]_[eE][dD]25519_[sS][kK]
# Dependencies: restored inside the image, never copied in.
# Dependencies: restored inside the image, never copied in. yarn's install
# state and cache stay out, and its release file stays in.
**/node_modules
.yarn/*
!.yarn/releases
# OS metadata.
**/.DS_Store
+3 -3
View File
@@ -22,9 +22,9 @@ node_modules/
.yarn/*
!.yarn/releases
# Secrets. Unanchored, so each matches at every depth. Matching is
# case-sensitive on Linux, so names use character ranges rather than a
# lowercase form that misses `Server.Key`.
# Secrets. Unanchored like every entry above, so each matches at every
# depth. Matching is case-sensitive on Linux, so names use character
# ranges rather than a lowercase form that misses `Server.Key`.
# Environment files. `*.env` covers bare `.env` and the `prod.env`
# convention. Only the templates `example.env` and `sample.env` are
+2 -2
View File
@@ -6,8 +6,8 @@
# system packages. script/ passes a new value on every build. That value
# reruns every RUN step below the ARG line, and the install step keeps
# its layer. node and yarn come from the distribution, which is trusted,
# so they need no version and no hash. That yarn only starts the release
# file in .yarn/releases/, so every stage copies that file and
# so they need no version and no hash. That yarn hands the work to the
# release file in .yarn/releases/, so every stage copies that file and
# .yarnrc.yml before it runs script/bootstrap.
#
# alpine 3.24.3, 2026-11-04
-7
View File
@@ -36,13 +36,6 @@ fmt-check, and commit.
and yarn.
- Both checklists give the steps, and the ignore files keep yarn's install
state out of git and the build context.
- 2026-11-04: An agent whose work is confined to one package may run `go test`
directly on that package's suite, or on individual tests in it, while
iterating (https://git.eeqj.de/sneak/prompts/issues/51), on sneak's ruling at
https://git.eeqj.de/sneak/prompts/issues/51#issuecomment-167600. The full
`make check` is still required before every push, and the `Makefile` gains
nothing. The rule on Docker now names the lint and test gates, because a run
while iterating is not one.
- 2026-11-04: node, yarn, npm, python, pip and venv now come from the
distribution with no version and no hash
(https://git.eeqj.de/sneak/prompts/issues/146), on sneak's ruling at
+7 -11
View File
@@ -165,9 +165,8 @@ with your task.
in the same commit, set the lint phase digest to the one named in the
`.golangci.yml` paragraph of `REPO_POLICIES.md`)
- [ ] JS: `package.json`, `yarn.lock`, `.yarnrc.yml`, `.yarn/releases/`,
`.gitattributes`, `.prettierrc`, `.prettierignore` (fetch the last two
from `https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierrc`
and
`.prettierrc`, `.prettierignore` (fetch the last two from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierrc` and
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierignore`).
- [ ] JS: `yarn.lock` is in yarn 4's format. If its second line says
`yarn lockfile v1`, convert it with the steps below. Do them in one
@@ -191,11 +190,9 @@ with your task.
6. Name any package that must run its install script in `package.json`,
as `"dependenciesMeta": { "<name>": { "built": true } }`, and run
`yarn install` again.
7. Add `.yarn/*` and `!.yarn/releases` above the secret patterns in
`.gitignore` and `.dockerignore`. Docker obeys the last matching
line, and the secret patterns must win. Add `.yarn/` to
`.prettierignore`, and `.yarn/releases/** binary` to
`.gitattributes`.
7. Add `.yarn/*` and `!.yarn/releases` to `.gitignore` and
`.dockerignore`, `.yarn/` to `.prettierignore`, and
`.yarn/releases/** binary` to `.gitattributes`.
8. Run `yarn install --immutable`. It must pass and change nothing.
- [ ] Python: `pyproject.toml`
- [ ] Docs/writing: `.prettierrc`, `.prettierignore` (same URLs as above)
@@ -234,9 +231,8 @@ with your task.
- [ ] `script/bootstrap` installs node, yarn and the node packages only when
`yarn.lock` is in the tree. node and yarn come from the distribution when
they are missing. It installs the node packages with
`yarn install --immutable`. The distribution's yarn only starts the
release file that `yarnPath` in `.yarnrc.yml` names, and that file does
the work.
`yarn install --immutable`. Whichever yarn the distribution packages hands
the work to the release file that `yarnPath` in `.yarnrc.yml` names.
- [ ] A `Dockerfile` stage that needs yarn may start from any distribution that
packages node and yarn. It copies `package.json`, `yarn.lock`,
`.yarnrc.yml` and `.yarn/releases/` before it runs `script/bootstrap`. A
+3 -3
View File
@@ -210,9 +210,9 @@ are thin shims calling them. Model scripts:
- It installs node, yarn and the node packages only when `yarn.lock` is in
the tree. node and yarn come from that package manager when they are
missing.
- It installs the node packages with `yarn install --immutable`. The
distribution's yarn only starts the release file that `yarnPath` in
`.yarnrc.yml` names, and that file does the work.
- It installs the node packages with `yarn install --immutable`. Whichever
yarn the distribution packages hands the work to the release file that
`yarnPath` in `.yarnrc.yml` names.
- A non-server repo's development environment stage runs it, with only
system packages installed inline above it.
- A gate phase or the build stage installs what its base image lacks, either
+8 -14
View File
@@ -132,8 +132,8 @@ style conventions are in separate documents:
is out of step with `yarn.lock`.
Each repository commits the yarn that runs. `yarnPath` in `.yarnrc.yml`
points at the release file in `.yarn/releases/`. The distribution's yarn
only starts that file, and that file does the work. Alpine packages yarn 1,
points at the release file in `.yarn/releases/`. Whichever yarn the
distribution packages hands the work to that file. Alpine packages yarn 1,
and Debian 13 packages yarn 4.1. The same committed yarn does the work on
both. Corepack never runs, and no yarn is downloaded.
@@ -143,8 +143,8 @@ style conventions are in separate documents:
- `enableTelemetry: false`, because yarn otherwise sends usage reports from
any host that is not CI
By default, yarn runs no dependency's install script. A dependency that must
run one is named in `package.json`, as
Under these settings yarn runs no package's install script. A package that
must run one is named in `package.json`, as
`"dependenciesMeta": { "<name>": { "built": true } }`.
`.gitattributes` carries `.yarn/releases/** binary`, so diffs and merges
@@ -203,12 +203,6 @@ style conventions are in separate documents:
instead of invoking the underlying tools directly. The Makefile is the single
source of truth for how these operations are run.
An agent whose work is confined to one package may run `go test` directly on
that package's suite, or on individual tests in it, while iterating. This is
the only exception to this rule. `go build`, `go vet` and the linter still
go through make targets. A run while iterating is not a gate, so the full
`make check` is still required before every push.
- The Makefile is authoritative documentation for how the repo is used. Beyond
the required targets above, it should have targets for every common operation:
- running a local development server (`make run`, `make dev`)
@@ -241,9 +235,9 @@ style conventions are in separate documents:
- `package.json`, `yarn.lock`, `.yarnrc.yml` and `.yarn/releases/`
- `go.mod` and `go.sum`
- **The lint and test gates run in Docker, as phases of the `Dockerfile`.**
There is no separate lint file. `script/lint` and `script/test` each build one
phase and nothing else:
- **Linting and testing run in Docker, as phases of the `Dockerfile`.** There is
no separate lint file. `script/lint` and `script/test` each build one phase
and nothing else:
```sh
docker build --build-arg CACHEBUST="$(date +%s)-$$" \
@@ -1135,7 +1129,7 @@ style conventions are in separate documents:
- `.gitea/workflows/check.yml`
- Go: `go.mod`, `go.sum`, `.golangci.yml`
- JS: `package.json`, `yarn.lock`, `.yarnrc.yml`, `.yarn/releases/`,
`.gitattributes`, `.prettierrc`, `.prettierignore`
`.prettierrc`, `.prettierignore`
- Python: `pyproject.toml`
- Guidance for coding agents lives in one `AGENTS.md` at the repository root. It
+5 -5
View File
@@ -4,10 +4,10 @@
# installed tools are skipped. Base tools, node and yarn come from nix,
# apt, brew, or apk (detected in that order), with no version and no
# hash, because the distribution is trusted. It installs node, yarn and
# the node packages only when yarn.lock is in the tree. The
# distribution's yarn only starts the release file that yarnPath in
# .yarnrc.yml names, and that file does the work. So git pins the yarn
# that runs. Assumes nothing is present.
# the node packages only when yarn.lock is in the tree. Whichever yarn
# the distribution packages hands the work to the release file that
# yarnPath in .yarnrc.yml names, so git pins the yarn that runs. Assumes
# nothing is present.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
@@ -80,7 +80,7 @@ main() {
# A Dockerfile stage that needs no yarn copies no yarn.lock.
if [ -f yarn.lock ]; then
if missing node; then pkg_install nodejs_latest nodejs node nodejs-current; fi
if missing node; then pkg_install nodejs nodejs node nodejs-current; fi
if missing yarn && missing yarnpkg; then
pkg_install yarn yarnpkg yarn yarn
fi