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 980 additions and 937 deletions

No files matched your search

+4 -7
View File
@@ -35,12 +35,6 @@
# `services/api/.claude/` and must add its own anchored entry. # `services/api/.claude/` and must add its own anchored entry.
.claude .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` # Environment files. `*.env` covers bare `.env` and the `prod.env`
# convention. Re-include a committed template with a negation if the # convention. Re-include a committed template with a negation if the
# build needs one: `!docs/example.env`. # build needs one: `!docs/example.env`.
@@ -61,8 +55,11 @@
**/[iI][dD]_[eE][dD]25519 **/[iI][dD]_[eE][dD]25519
**/[iI][dD]_[eE][dD]25519_[sS][kK] **/[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 **/node_modules
.yarn/*
!.yarn/releases
# OS metadata. # OS metadata.
**/.DS_Store **/.DS_Store
+3 -3
View File
@@ -22,9 +22,9 @@ node_modules/
.yarn/* .yarn/*
!.yarn/releases !.yarn/releases
# Secrets. Unanchored, so each matches at every depth. Matching is # Secrets. Unanchored like every entry above, so each matches at every
# case-sensitive on Linux, so names use character ranges rather than a # depth. Matching is case-sensitive on Linux, so names use character
# lowercase form that misses `Server.Key`. # ranges rather than a lowercase form that misses `Server.Key`.
# Environment files. `*.env` covers bare `.env` and the `prod.env` # Environment files. `*.env` covers bare `.env` and the `prod.env`
# convention. Only the templates `example.env` and `sample.env` are # 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 # 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 # 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, # 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 # so they need no version and no hash. That yarn hands the work to the
# file in .yarn/releases/, so every stage copies that file and # release file in .yarn/releases/, so every stage copies that file and
# .yarnrc.yml before it runs script/bootstrap. # .yarnrc.yml before it runs script/bootstrap.
# #
# alpine 3.24.3, 2026-11-04 # alpine 3.24.3, 2026-11-04
-19
View File
@@ -21,18 +21,6 @@ fmt-check, and commit.
# Completed Steps # Completed Steps
- 2026-11-06: Compacted `prompts/REPO_POLICIES.md` from 1,145 lines and 8,427
words to 1,066 lines and 7,812 words
(https://git.eeqj.de/sneak/prompts/issues/158). Every rule keeps its meaning.
The rules now sit under headings, the pinning rule first. Each rule that was
stated in several places is stated once. The Makefile copies of the Go and
Python test commands are gone, because the `Dockerfile` examples carry the
same commands. Both checklists name the `.golangci.yml` section.
- 2026-11-06: The pinning rule in `prompts/REPO_POLICIES.md` now opens with its
scope: what we download and install ourselves in scripts and Dockerfiles
(https://git.eeqj.de/sneak/prompts/issues/154). Distribution packages are
outside the rule, and updating and upgrading them is allowed. Both checklists
say the same.
- 2026-11-04: Each repository now commits yarn 4 and installs with - 2026-11-04: Each repository now commits yarn 4 and installs with
`yarn install --immutable` (https://git.eeqj.de/sneak/prompts/issues/149), on `yarn install --immutable` (https://git.eeqj.de/sneak/prompts/issues/149), on
sneak's ruling at sneak's ruling at
@@ -48,13 +36,6 @@ fmt-check, and commit.
and yarn. and yarn.
- Both checklists give the steps, and the ignore files keep yarn's install - Both checklists give the steps, and the ignore files keep yarn's install
state out of git and the build context. 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 - 2026-11-04: node, yarn, npm, python, pip and venv now come from the
distribution with no version and no hash distribution with no version and no hash
(https://git.eeqj.de/sneak/prompts/issues/146), on sneak's ruling at (https://git.eeqj.de/sneak/prompts/issues/146), on sneak's ruling at
+25 -29
View File
@@ -1,6 +1,6 @@
--- ---
title: Existing Repo Checklist title: Existing Repo Checklist
last_modified: 2026-11-06 last_modified: 2026-11-04
--- ---
Use this checklist when beginning work in a repo that may not yet conform to our Use this checklist when beginning work in a repo that may not yet conform to our
@@ -163,18 +163,17 @@ with your task.
- [ ] Go: `go.mod`, `go.sum`, `.golangci.yml` (fetch from - [ ] Go: `go.mod`, `go.sum`, `.golangci.yml` (fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml` and, `https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml` and,
in the same commit, set the lint phase digest to the one named in the in the same commit, set the lint phase digest to the one named in the
`.golangci.yml` section of `REPO_POLICIES.md`) `.golangci.yml` paragraph of `REPO_POLICIES.md`)
- [ ] JS: `package.json`, `yarn.lock`, `.yarnrc.yml`, `.yarn/releases/`, - [ ] JS: `package.json`, `yarn.lock`, `.yarnrc.yml`, `.yarn/releases/`,
`.gitattributes`, `.prettierrc`, `.prettierignore` (fetch the last two `.prettierrc`, `.prettierignore` (fetch the last two from
from `https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierrc` `https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierrc` and
and
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierignore`). `https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierignore`).
- [ ] Any repository with `yarn.lock`: that file is in yarn 4's format. If - [ ] JS: `yarn.lock` is in yarn 4's format. If its second line says
its second line says `yarn lockfile v1`, convert it with the steps `yarn lockfile v1`, convert it with the steps below. Do them in one
below. Do them in one commit with the re-vendored scripts, because commit with the re-vendored scripts, because yarn 1 ignores
yarn 1 ignores `--immutable` and exits 0 on a tree with no `--immutable` and exits 0 on a tree with no `.yarnrc.yml`. Run them
`.yarnrc.yml`. Run them with any distribution's node and yarn, and use with any distribution's node and yarn, and use `yarnpkg` on Debian and
`yarnpkg` on Debian and Ubuntu. Ubuntu.
1. Read the version from `yarnPath` in 1. Read the version from `yarnPath` in
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.yarnrc.yml`. `https://git.eeqj.de/sneak/prompts/raw/branch/main/.yarnrc.yml`.
2. Run `yarn set version <version>`. It writes 2. Run `yarn set version <version>`. It writes
@@ -188,14 +187,12 @@ with your task.
`npmMinimalAgeGate` from `.yarnrc.yml`. The conversion wrote them. `npmMinimalAgeGate` from `.yarnrc.yml`. The conversion wrote them.
They allow install scripts, any git repository and packages of any They allow install scripts, any git repository and packages of any
age, which yarn otherwise refuses. age, which yarn otherwise refuses.
6. In `package.json`, name any package that must run its install script, 6. Name any package that must run its install script in `package.json`,
as `"dependenciesMeta": { "<name>": { "built": true } }`, and run as `"dependenciesMeta": { "<name>": { "built": true } }`, and run
`yarn install` again. `yarn install` again.
7. Add `.yarn/*` and `!.yarn/releases` above the secret patterns in 7. Add `.yarn/*` and `!.yarn/releases` to `.gitignore` and
`.gitignore` and `.dockerignore`. Docker obeys the last matching `.dockerignore`, `.yarn/` to `.prettierignore`, and
line, and the secret patterns must win. Add `.yarn/` to `.yarn/releases/** binary` to `.gitattributes`.
`.prettierignore`, and `.yarn/releases/** binary` to
`.gitattributes`.
8. Run `yarn install --immutable`. It must pass and change nothing. 8. Run `yarn install --immutable`. It must pass and change nothing.
- [ ] Python: `pyproject.toml` - [ ] Python: `pyproject.toml`
- [ ] Docs/writing: `.prettierrc`, `.prettierignore` (same URLs as above) - [ ] 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 - [ ] `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 `yarn.lock` is in the tree. node and yarn come from the distribution when
they are missing. It installs the node packages with they are missing. It installs the node packages with
`yarn install --immutable`. The distribution's yarn only starts the `yarn install --immutable`. Whichever yarn the distribution packages hands
release file that `yarnPath` in `.yarnrc.yml` names, and that file does the work to the release file that `yarnPath` in `.yarnrc.yml` names.
the work.
- [ ] A `Dockerfile` stage that needs yarn may start from any distribution that - [ ] A `Dockerfile` stage that needs yarn may start from any distribution that
packages node and yarn. It copies `package.json`, `yarn.lock`, packages node and yarn. It copies `package.json`, `yarn.lock`,
`.yarnrc.yml` and `.yarn/releases/` before it runs `script/bootstrap`. A `.yarnrc.yml` and `.yarn/releases/` before it runs `script/bootstrap`. A
@@ -267,21 +263,21 @@ with your task.
- [ ] Pre-commit hook is installed (`make hooks`) - [ ] Pre-commit hook is installed (`make hooks`)
- [ ] No secrets in the repo (`.env`, keys, credentials) - [ ] No secrets in the repo (`.env`, keys, credentials)
- [ ] No mutable references (tags, `@latest`) to what the repo downloads and - [ ] No mutable references in Dockerfiles or scripts (tags, `@latest`) — all
installs itself in scripts and Dockerfiles — all pinned by cryptographic pinned by cryptographic hash with version/date comment. The one exception
hash with version/date comment. Distribution packages are outside this is a package from the distribution's own package index, which needs no
rule. They need no version and no hash, and updating and upgrading them is version and no hash. Pulling that index is also allowed. In a
fine too. In a `Dockerfile`, the distribution is the pinned base image's. `Dockerfile`, the distribution is the pinned base image's. On a host, it
On a host, it is the host's, and the host's package manager stands for it, is the host's, and the host's package manager stands for it, nix and brew
nix and brew included. A package source that a `Dockerfile` adds itself is included. A package source that a `Dockerfile` adds itself does not get
not the distribution's own, so its packages are pinned this exception
- [ ] node, yarn, npm, python, pip and venv come from the distribution wherever - [ ] node, yarn, npm, python, pip and venv come from the distribution wherever
possible, never from nvm, pyenv, corepack or `npm install -g yarn`. If the possible, never from nvm, pyenv, corepack or `npm install -g yarn`. If the
distribution's version is too old, use a newer distribution. node means distribution's version is too old, use a newer distribution. node means
the distribution's newest Node package, such as `nodejs-current` on Alpine the distribution's newest Node package, such as `nodejs-current` on Alpine
3.24 3.24
- [ ] yarn's release file is in git under `.yarn/releases/`, written once by - [ ] yarn's release file is in git under `.yarn/releases/`, written once by
`yarn set version` with an exact version (repositories with `yarn.lock`) `yarn set version` with an exact version
- [ ] Using `yarn`, not `npm` (JS projects) - [ ] Using `yarn`, not `npm` (JS projects)
# Directory Structure # Directory Structure
+16 -18
View File
@@ -1,6 +1,6 @@
--- ---
title: New Repo Checklist title: New Repo Checklist
last_modified: 2026-11-06 last_modified: 2026-11-04
--- ---
Use this checklist when creating a new repository from scratch. Follow the steps Use this checklist when creating a new repository from scratch. Follow the steps
@@ -178,12 +178,11 @@ Template files can be fetched from:
- [ ] Go: `go mod init sneak.berlin/go/<name>`, `.golangci.yml` (fetch from - [ ] Go: `go mod init sneak.berlin/go/<name>`, `.golangci.yml` (fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml` and, `https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml` and,
in the same commit, set the lint phase digest to the one named in the in the same commit, set the lint phase digest to the one named in the
`.golangci.yml` section of `REPO_POLICIES.md`) `.golangci.yml` paragraph of `REPO_POLICIES.md`)
- [ ] JS, or any repository that runs prettier through yarn: make the first - [ ] JS: make the first `yarn.lock` with the steps below.
`yarn.lock` with the steps below. `script/bootstrap` installs no yarn `script/bootstrap` installs no yarn until `yarn.lock` exists, so run
until `yarn.lock` exists, so run them with any distribution's node and them with any distribution's node and yarn, such as Alpine's
yarn, such as Alpine's (`apk add nodejs-current yarn`). Use `yarnpkg` (`apk add nodejs-current yarn`). Use `yarnpkg` on Debian and Ubuntu.
on Debian and Ubuntu.
1. Read the version from `yarnPath` in 1. Read the version from `yarnPath` in
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.yarnrc.yml`. `https://git.eeqj.de/sneak/prompts/raw/branch/main/.yarnrc.yml`.
2. Run `yarn set version <version>`. It writes 2. Run `yarn set version <version>`. It writes
@@ -211,9 +210,9 @@ are thin shims calling them. Model scripts:
- It installs node, yarn and the node packages only when `yarn.lock` is in - 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 the tree. node and yarn come from that package manager when they are
missing. missing.
- It installs the node packages with `yarn install --immutable`. The - It installs the node packages with `yarn install --immutable`. Whichever
distribution's yarn only starts the release file that `yarnPath` in yarn the distribution packages hands the work to the release file that
`.yarnrc.yml` names, and that file does the work. `yarnPath` in `.yarnrc.yml` names.
- A non-server repo's development environment stage runs it, with only - A non-server repo's development environment stage runs it, with only
system packages installed inline above it. system packages installed inline above it.
- A gate phase or the build stage installs what its base image lacks, either - A gate phase or the build stage installs what its base image lacks, either
@@ -285,20 +284,19 @@ are thin shims calling them. Model scripts:
final stage is missing its `COPY --from=` edge to the gate phases. final stage is missing its `COPY --from=` edge to the gate phases.
- [ ] No secrets in repo, and none in the build context: enumerate a probe image - [ ] No secrets in repo, and none in the build context: enumerate a probe image
rather than reading `.dockerignore` rather than reading `.dockerignore`
- [ ] No mutable references to what the repo downloads and installs itself in - [ ] No mutable image/package references. The one exception is a package from
scripts and Dockerfiles. Distribution packages are outside this rule. They the distribution's own package index, which needs no version and no hash.
need no version and no hash, and updating and upgrading them is fine too. Pulling that index is also allowed. In a `Dockerfile`, the distribution is
In a `Dockerfile`, the distribution is the pinned base image's. On a host, the pinned base image's. On a host, it is the host's, and the host's
it is the host's, and the host's package manager stands for it, nix and package manager stands for it, nix and brew included. A package source
brew included. A package source that a `Dockerfile` adds itself is not the that a `Dockerfile` adds itself does not get this exception
distribution's own, so its packages are pinned
- [ ] node, yarn, npm, python, pip and venv come from the distribution wherever - [ ] node, yarn, npm, python, pip and venv come from the distribution wherever
possible, never from nvm, pyenv, corepack or `npm install -g yarn`. If the possible, never from nvm, pyenv, corepack or `npm install -g yarn`. If the
distribution's version is too old, use a newer distribution. node means distribution's version is too old, use a newer distribution. node means
the distribution's newest Node package, such as `nodejs-current` on Alpine the distribution's newest Node package, such as `nodejs-current` on Alpine
3.24 3.24
- [ ] yarn's release file is in git under `.yarn/releases/`, written once by - [ ] yarn's release file is in git under `.yarn/releases/`, written once by
`yarn set version` with an exact version (repositories with `yarn.lock`) `yarn set version` with an exact version
- [ ] No unnecessary files in repo root - [ ] No unnecessary files in repo root
- [ ] All dates written as YYYY-MM-DD - [ ] All dates written as YYYY-MM-DD
+925 -854
View File
File diff suppressed because it is too large. Load diff
+5 -5
View File
@@ -4,10 +4,10 @@
# installed tools are skipped. Base tools, node and yarn come from nix, # 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 # apt, brew, or apk (detected in that order), with no version and no
# hash, because the distribution is trusted. It installs node, yarn and # hash, because the distribution is trusted. It installs node, yarn and
# the node packages only when yarn.lock is in the tree. The # the node packages only when yarn.lock is in the tree. Whichever yarn
# distribution's yarn only starts the release file that yarnPath in # the distribution packages hands the work to the release file that
# .yarnrc.yml names, and that file does the work. So git pins the yarn # yarnPath in .yarnrc.yml names, so git pins the yarn that runs. Assumes
# that runs. Assumes nothing is present. # nothing is present.
set -eu set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
@@ -80,7 +80,7 @@ main() {
# A Dockerfile stage that needs no yarn copies no yarn.lock. # A Dockerfile stage that needs no yarn copies no yarn.lock.
if [ -f yarn.lock ]; then 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 if missing yarn && missing yarnpkg; then
pkg_install yarn yarnpkg yarn yarn pkg_install yarn yarnpkg yarn yarn
fi fi