next → main: version from git, runtimes from the distribution, yarn 4 in the tree, golangci-lint v2.15.0, Dockerfile and ignore-file fixes #68

Open
clawbot wants to merge 46 commits from next into main
pull from: next
Collaborator

On next, for the files every repository vendors:

  • Runtimes and pinning, on your ruling at #146 (comment): node, yarn, npm, python, pip and venv come from the distribution, with no version and no hash, as does any other package from its own index. If one is too old, use a newer distribution, never a download. Third-party packages stay pinned by hash, and Docker Hub, PyPI, the npm registry and go get stay untrusted. script/bootstrap drops nvm and corepack, and installs node and yarn only when yarn.lock is in the tree. This repository's Dockerfile starts from Alpine; the Python example starts from debian:trixie-slim. A Python package with no wheel for the image now stops the install, so pip downloads no build tools without a hash check.

  • Yarn, on your ruling at #149 (comment): each repository commits yarn 4's release file under .yarn/releases/ and installs with --immutable. The distribution's yarn only starts that file, and that file does the work. Node is the distribution's newest Node package, such as nodejs-current on Alpine. This repository's yarn.lock is converted; the checklists give the steps for the others.

  • REPO_POLICIES.md is rewritten sentence by sentence, and you merged it. No rule changed in that rewrite.

  • Check builds keep Go's build cache, Go's downloaded modules, golangci-lint's cache, the system-package install and a Python test phase's install from Pipfile.lock. script/ passes a new CACHEBUST build argument in place of --no-cache; in each Dockerfile stage those installs come before ARG CACHEBUST and every lint, test and build step after it; both go test runs carry -count=1. A repository takes the scripts and the Dockerfile order in one commit.

  • The image version comes from git describe inside the build. .dockerignore sends .git without any git config, reversing the rule that excluded .git; a build from a linked worktree passes --build-arg VERSION.

  • Dockerfile example: -race works, the builder trusts /src, lint-phase libraries use apt-get, stages say when they run script/bootstrap; the lint and test builds write no image.

  • golangci-lint v2.15.0 lints Go 1.27; exhaustruct_v5 is disabled and canonicalheader is on; the depguard test-support exemption covers only the files directly in a directory ending in test; a line that trips two linters reports both; the lint digest and .golangci.yml change in one commit. .golangci.yml accepts one local replace, homoicon's copy of sobek, on your decision; every other local replace is still refused.

  • Ignore files keep out more secrets; a repository's own .gitignore and .editorconfig entries survive a re-vendor.

  • CI: the check job has a 20-minute limit, cancels a replaced run, leaves no token in .git/config, and fetches tags.

  • Go: fx owns signals and the exit code; runtime.GOARCH at run time; host tools via commit-pinned go install.

  • script/bootstrap refreshes apt's package lists once; script/ stops when script/projectname fails.

  • New prompts/API_POLICIES.md: an API response never sends a value as a string with a label or unit in it; the unit goes in the field name, as in execution_time_ms. REPO_POLICIES.md and the README name it.

  • A first test run that timed out gets no verbose rerun, so a hung package uses the 90-second backstop once. Each repository's Dockerfile takes the new recipe at its next re-vendor.

  • An agent whose work is confined to one package may run go test directly on it while iterating; the full make check is still required before every push. The Makefile is unchanged.

  • Also: one root AGENTS.md; checks proven by a planted defect; no CHECK_EPOCH; TODO.md union merge; the canonical package.json is private instead of declaring MIT.

  • Go code is formatted with gofmt, then golines -m 77. The policy documents say so and pin golines v0.13.0; lll stays at 88, since golines leaves long strings and comments alone. The template repositories' scripts follow in their own PRs.

  • New script/update-policy and make update-policy: one command fetches the policy files a repository vendors, keeps its own entries, commits them by name and pushes. It stops on any line the canonical file has never held. A repository with its own .golangci.yml settings, such as sneak/homoicon, stops until those are dealt with. The canonical .dockerignore gains the closing comment the script needs.

To know before merging

  • A repository that re-vendors script/bootstrap converts its yarn.lock in the same commit. yarn 1 ignores --immutable and exits 0 on a tree with no .yarnrc.yml, so without the conversion its install is not frozen.
  • Two judgement calls you can reverse: .yarnrc.yml sets enableTelemetry: false, and a conversion deletes the approvedGitRepositories, enableScripts and npmMinimalAgeGate settings it writes, because they relax yarn 4's defaults.
  • Consuming repositories re-vendor these, tracked in #78.

Recorded gap: a submodule with a config segment in its name.
Unverified: the kept caches have not yet run on the shared CI runner. The brew path of script/bootstrap was not run. On nix, nixpkgs' yarn runs on its own fixed Node, older than nodejs_latest.

Model: opus-5-5

On `next`, for the files every repository vendors: - Runtimes and pinning, on your ruling at https://git.eeqj.de/sneak/prompts/issues/146#issuecomment-166033: node, yarn, npm, python, pip and venv come from the distribution, with no version and no hash, as does any other package from its own index. If one is too old, use a newer distribution, never a download. Third-party packages stay pinned by hash, and Docker Hub, PyPI, the npm registry and `go get` stay untrusted. `script/bootstrap` drops nvm and corepack, and installs node and yarn only when `yarn.lock` is in the tree. This repository's `Dockerfile` starts from Alpine; the Python example starts from `debian:trixie-slim`. A Python package with no wheel for the image now stops the install, so pip downloads no build tools without a hash check. - Yarn, on your ruling at https://git.eeqj.de/sneak/prompts/issues/149#issuecomment-167558: each repository commits yarn 4's release file under `.yarn/releases/` and installs with `--immutable`. The distribution's yarn only starts that file, and that file does the work. Node is the distribution's newest Node package, such as `nodejs-current` on Alpine. This repository's `yarn.lock` is converted; the checklists give the steps for the others. - `REPO_POLICIES.md` is rewritten sentence by sentence, and you merged it. No rule changed in that rewrite. - Check builds keep Go's build cache, Go's downloaded modules, golangci-lint's cache, the system-package install and a Python `test` phase's install from `Pipfile.lock`. `script/` passes a new `CACHEBUST` build argument in place of `--no-cache`; in each `Dockerfile` stage those installs come before `ARG CACHEBUST` and every lint, test and build step after it; both `go test` runs carry `-count=1`. A repository takes the scripts and the `Dockerfile` order in one commit. - The image version comes from `git describe` inside the build. `.dockerignore` sends `.git` without any git `config`, reversing the rule that excluded `.git`; a build from a linked worktree passes `--build-arg VERSION`. - Dockerfile example: `-race` works, the builder trusts `/src`, lint-phase libraries use `apt-get`, stages say when they run `script/bootstrap`; the lint and test builds write no image. - golangci-lint v2.15.0 lints Go 1.27; `exhaustruct_v5` is disabled and `canonicalheader` is on; the `depguard` test-support exemption covers only the files directly in a directory ending in `test`; a line that trips two linters reports both; the lint digest and `.golangci.yml` change in one commit. `.golangci.yml` accepts one local `replace`, homoicon's copy of sobek, on your decision; every other local `replace` is still refused. - Ignore files keep out more secrets; a repository's own `.gitignore` and `.editorconfig` entries survive a re-vendor. - CI: the check job has a 20-minute limit, cancels a replaced run, leaves no token in `.git/config`, and fetches tags. - Go: fx owns signals and the exit code; `runtime.GOARCH` at run time; host tools via commit-pinned `go install`. - `script/bootstrap` refreshes apt's package lists once; `script/` stops when `script/projectname` fails. - New `prompts/API_POLICIES.md`: an API response never sends a value as a string with a label or unit in it; the unit goes in the field name, as in `execution_time_ms`. `REPO_POLICIES.md` and the README name it. - A first test run that timed out gets no verbose rerun, so a hung package uses the 90-second backstop once. Each repository's `Dockerfile` takes the new recipe at its next re-vendor. - An agent whose work is confined to one package may run `go test` directly on it while iterating; the full `make check` is still required before every push. The `Makefile` is unchanged. - Also: one root `AGENTS.md`; checks proven by a planted defect; no `CHECK_EPOCH`; `TODO.md` union merge; the canonical `package.json` is private instead of declaring MIT. - Go code is formatted with `gofmt`, then `golines -m 77`. The policy documents say so and pin `golines` v0.13.0; `lll` stays at 88, since `golines` leaves long strings and comments alone. The template repositories' scripts follow in their own PRs. - New `script/update-policy` and `make update-policy`: one command fetches the policy files a repository vendors, keeps its own entries, commits them by name and pushes. It stops on any line the canonical file has never held. A repository with its own `.golangci.yml` settings, such as `sneak/homoicon`, stops until those are dealt with. The canonical `.dockerignore` gains the closing comment the script needs. **To know before merging** - A repository that re-vendors `script/bootstrap` converts its `yarn.lock` in the same commit. yarn 1 ignores `--immutable` and exits 0 on a tree with no `.yarnrc.yml`, so without the conversion its install is not frozen. - Two judgement calls you can reverse: `.yarnrc.yml` sets `enableTelemetry: false`, and a conversion deletes the `approvedGitRepositories`, `enableScripts` and `npmMinimalAgeGate` settings it writes, because they relax yarn 4's defaults. - Consuming repositories re-vendor these, tracked in https://git.eeqj.de/sneak/prompts/issues/78. Recorded gap: a submodule with a `config` segment in its name. Unverified: the kept caches have not yet run on the shared CI runner. The brew path of `script/bootstrap` was not run. On nix, nixpkgs' yarn runs on its own fixed Node, older than `nodejs_latest`. Model: opus-5-5
clawbot added the merge-ready label 2026-10-02 01:04:09 +02:00
sneak was assigned by clawbot 2026-10-02 01:04:09 +02:00
clawbot added 1 commit 2026-10-02 01:04:09 +02:00
The Go styleguide and the HTTP server conventions no longer pass the
build architecture in through the Makefile. The Buildarch variable,
globals field and BUILDARCH Makefile lines are removed from every
example; the styleguide example prints runtime.GOARCH, and the
logger's Identify logs "arch", runtime.GOARCH. The styleguide item
gains one sentence saying so.

Model: opus-5-5
clawbot added 1 commit 2026-10-02 04:38:12 +02:00
The canonical documents told every repo to exclude .git from the build
context, default ARG VERSION to dev and never run git describe in a
build stage, so an image built from a clone with no build argument
reported dev. .dockerignore now sends .git but keeps out .git/config,
which can hold a credential. The Dockerfile example installs git, takes
the VERSION build argument when one is given and otherwise
git describe --tags --always, and fails when .git exists but the version
is empty, dev or unknown. The policy and both checklists state the rule
in the same words, including that a plain docker build . with no build
arguments must succeed.

Model: opus-5-5
clawbot changed title from next → main: read the architecture at run time to next → main: architecture at run time, version from git 2026-10-02 04:38:22 +02:00
clawbot added 1 commit 2026-10-03 17:39:43 +02:00
The canonical .gitignore matched only .env, .env.*, *.pem and *.key, so prod.env, .envrc, *.p12 and *.pfx bundles, and the SSH private keys id_rsa, id_dsa, id_ecdsa and id_ed25519 could be committed. The secrets section now covers the same shapes as the canonical .dockerignore, written to gitignore's own rules: unanchored, no **/ prefix, character ranges for case. example.env and sample.env stay trackable through negations, and the comment tells a repository to add its own negation for any other committed template.

Judgement call: the existing entries were rewritten with character ranges, which only widens them, and the bare .env line is dropped because *.env covers it.

Model: opus-5-5
clawbot changed title from next → main: architecture at run time, version from git to next → main: architecture at run time, version from git, secrets in .gitignore 2026-10-03 17:41:27 +02:00
clawbot added 1 commit 2026-10-04 03:32:11 +02:00
The canonical golangci-lint moves from v2.12.2 to v2.14.0, built with go1.27: v2.12.2 refuses a module whose `go` directive names 1.27 or later. The policy now states the rule: the `go` directive must not name a newer Go minor version than the one golangci-lint was built with.

From v2.13.0, `default: all` turns on `exhaustruct_v5`, the successor of the deprecated `exhaustruct`. `.golangci.yml` disables it beside the old name, which stays listed or its deprecation warning returns.

v2.12.2 rejects the new `.golangci.yml`, so a repo changes the lint phase digest and re-vendors `.golangci.yml` in one commit; both checklists point to that rule.

Model: opus-5-5
clawbot changed title from next → main: architecture at run time, version from git, secrets in .gitignore to next → main: architecture at run time, version from git, secrets in .gitignore, golangci-lint v2.14.0 2026-10-04 03:32:46 +02:00
clawbot added 1 commit 2026-10-04 04:31:52 +02:00
The canonical Go `Dockerfile` example in `prompts/REPO_POLICIES.md` had two defects.

The test phase ran `go test -race` on the alpine Go image, which has no C compiler, so `-race` failed before any test ran. The test phase now uses the Debian Go image, pinned by digest like the others.

A build context sent as a tar stream keeps the sender's file owners, so git refused the checkout and the version step failed the build. The builder stage now runs `git config --system --add safe.directory /src`; the policy and both checklists say why in the same words.

The builder's `apk add --no-cache git` line is unchanged: whether it must be pinned is the open owner question on #72.

Model: opus-5-5
clawbot added 1 commit 2026-10-04 05:31:52 +02:00
The canonical `.dockerignore` kept out `.git/config`, which can hold a credential, but not the `config` in each submodule's git directory under `.git/modules/`, nested again for a submodule's own submodules. It now also lists `.git/modules/**/config`, with one sentence in the comment above; `prompts/REPO_POLICIES.md` and both checklists say so in the same words.

The pattern stays under `.git/modules/` because `.git/**/config` would also drop a branch or tag named `config`, which `git describe` may need.

Known gap: a submodule whose name has a `config` path segment loses its whole git directory, so Go's version stamping fails the build loudly; tracked separately.

Model: opus-5-5
clawbot added 1 commit 2026-10-04 07:02:31 +02:00
The Makefile examples in `prompts/CODE_STYLEGUIDE_GO.md` and `prompts/GO_HTTP_SERVER_CONVENTIONS.md` now read `VERSION ?= $(or $(shell git describe --tags --always 2>/dev/null),dev)`.

When `git describe` prints nothing (outside a git checkout, or where git is missing or refuses the checkout) the old line stamped an empty version without a word; it now falls back to `dev`, the same way in every repo. The comment above each line says so.

In a Docker build stage with `.git` present, a real version needs git installed and the checkout trusted, as the canonical `Dockerfile` does; the `Dockerfile` already fails when the version comes out empty, `dev` or `unknown`.

Model: opus-5-5
clawbot added 1 commit 2026-10-04 07:31:51 +02:00
The note under the canonical Go `make test` example in `prompts/REPO_POLICIES.md` named a cache-busting build argument that `--no-cache` replaced, and said Go's test result cache survived in earlier image layers. Neither is true of the current files.

The note now says where that cache can replay a pass: on a developer's machine, where the Makefile target runs, so both invocations there keep `-count=1`. The `test` phase of the `Dockerfile` has nothing to replay, since its base image holds no result for the repo's tests and no step before `go test` runs one, so it needs no `-count=1`. Go stores only passing results, so neither run can report a stored pass.

Model: opus-5-5
clawbot added 1 commit 2026-10-04 08:02:26 +02:00
Writes down sneak's 2026-08-22 ruling: the in-repo memory rule that older vendored copies of `REPO_POLICIES.md` still carry was retired, not lost.

`prompts/REPO_POLICIES.md` gains one bullet after the files a new repo must contain: guidance for coding agents lives in one `AGENTS.md` at the repository root, never under a file or directory named after one agent tool, and never split into separate memory files. `AGENTS.md` joins the files allowed in the repo root, which would otherwise forbid it. Both checklists get the matching item; the existing-repo one says to move such a file's content into `AGENTS.md` and delete it.

The Dockerfile finding at the end of the issue is tracked in #90.

Model: opus-5-5
clawbot added 1 commit 2026-10-04 08:31:48 +02:00
`package.json` now carries `"license": "MIT"`, matching `LICENSE`. Without it yarn printed `warning package.json: No license field` and `warning No license field` each time `script/bootstrap` ran inside the Docker phases of `make check`. No other yarn warning appears in the bootstrap output.

Model: opus-5-5
clawbot added 1 commit 2026-10-04 09:14:54 +02:00
`ssh-keygen` names the private key of a key backed by a hardware security key `id_ecdsa_sk` or `id_ed25519_sk`. The canonical `.gitignore` and `.dockerignore` listed only `id_rsa`, `id_dsa`, `id_ecdsa` and `id_ed25519`, so a repository could commit these files or copy them into an image.

Both names are added beside their plain counterparts in each file's own style: unanchored in `.gitignore`, `**/`-prefixed in `.dockerignore`, case-folded with character ranges in both. A pattern matches the whole file name, so the `.pub` halves stay trackable and still reach the build context.

Model: opus-5-5
clawbot added 1 commit 2026-10-04 09:49:16 +02:00
Writes sneak's 2026-09-09 ruling ("commit pinned installation, not pulled into deps") into the `prompts/REPO_POLICIES.md` bullet that says `script/bootstrap` installs a pinned tool by comparing versions: a Go tool a repo needs on the host is installed with `go install` pinned to a commit hash, naming the tool's main package, and is never tracked as a `go.mod` tool dependency or through a `tools.go` file, either of which pulls the tool's own dependencies into the repo's `go.mod` and `go.sum`.

golangci-lint is unchanged: no repo installs it on the host, and it stays pinned by its image digest.

Model: opus-5-5
clawbot added 1 commit 2026-10-04 11:02:19 +02:00
The server lifecycle example in `prompts/GO_HTTP_SERVER_CONVENTIONS.md` dropped its exit code, installed its own SIGINT/SIGTERM handler beside the one fx's `Run()` installs, and exited from a goroutine when Sentry could not start, so no stop hook ran.

fx now owns signals and the exit code: `main` calls `Run()`; a listen error shuts fx down with `fx.ExitCode(1)` through `fx.Shutdowner`; `enableSentry()` returns its error from the start hook; the stop hook shuts the HTTP server down within 5 seconds, flushes Sentry, and fails when requests are still running. The start hook builds the HTTP server before the listen goroutine so the stop hook can reach it. A new paragraph says who owns signals and the exit code.

Model: opus-5-5
clawbot added 1 commit 2026-10-04 11:31:50 +02:00
A key point under the canonical Go `Dockerfile` example in `prompts/REPO_POLICIES.md` said to install lint-phase system libraries with `apk add`. The lint phase is built on the golangci-lint image, which is Debian-based and has no `apk`, and `vips-dev` is the alpine package name. The point now gives the `apt-get` command with the Debian package name (`libvips-dev`) and removes the package lists in the same `RUN`.

The other `apk` mentions are about the alpine build stage or `script/bootstrap` on the host and stay. Nothing is pinned or unpinned; that is the open owner question on #72.

Model: opus-5-5
clawbot added 1 commit 2026-10-04 12:48:57 +02:00
A submodule that keeps its own `.git` directory, instead of one under `.git/modules/`, still shipped `sub/.git/config` into the build context, credential included. Both git patterns in the canonical `.dockerignore` now start with `**/`: `**/.git/config` and `**/.git/modules/**/config`.

A submodule whose name has a `config` segment (`config`, `deploy/config`, `config/lib`) still loses its whole git directory, because the pattern also matches that directory, and Go's version stamping then fails the build loudly. The file records this as a known gap with the way around it, `git submodule add --name`; closing it needs a wildcard re-include that makes every build walk excluded directories. `prompts/REPO_POLICIES.md` and both checklists say the same.

Model: opus-5-5
clawbot added 1 commit 2026-10-04 14:14:55 +02:00
Merge TODO.md with git's union merge (closes #98)
check / check (push) Failing after 1s
13125ac6f5
Every PR here adds an entry at the top of Completed Steps in `TODO.md`, so each merge to `next` left the other open PRs conflicting there. A root `.gitattributes` now marks `TODO.md` with `merge=union`: when two branches insert at the same place, git keeps both sides. It applies to this repository only; nothing canonical changes.

Union never reports a conflict in `TODO.md`. When two new entries share an identical line, one can land inside the other, and a rebased entry sits below every entry that reached `next` after the branch was cut, so the merged entries are read after every merge or rebase. Whether Gitea's own conflict check applies the attribute is not known.

Model: opus-5-5
clawbot added 1 commit 2026-10-04 14:48:50 +02:00
The bullet in `prompts/REPO_POLICIES.md` that requires a `Dockerfile` said every Dockerfile installs its prerequisites by running `script/bootstrap`, which the canonical Go `Dockerfile` in the same file never does. Reading the example as the deliberate one, the bullet now says the gate phases and the build stage start from their pinned base images and install what those images lack either inline, as the Go example does for `git`, or by running `script/bootstrap`, as this repository's own `Dockerfile` does for its yarn packages; the development environment stage of a non-server repo runs `script/bootstrap`. The new repo checklist item says the same.

The `COPY --from=` lines and the build stage's `apk add` line are unchanged.

Model: opus-5-5
clawbot added 1 commit 2026-10-04 15:14:46 +02:00
Of the cross-repository findings recorded on 2026-08-09, two were rules a repository must follow that `prompts/REPO_POLICIES.md` did not yet state, and each now sits in the paragraph a reader would be in. A new or changed check is proven by planting a defect it must catch and watching the run fail, since a green run alone does not show the check ran. A separate workflow limited to `main` by a `branches` list is first run from the feature branch by adding that branch to the list and removing it before merging, with any publishing job kept behind `if: github.ref_name == 'main'`.

The other findings are dropped, each with its reason on the issue; the `config verify` warning goes by sneak's ruling on #40 that there is no config check step.

Model: opus-5-5
clawbot changed title from next → main: architecture at run time, version from git, secrets in .gitignore, golangci-lint v2.14.0 to next → main: version from git, golangci-lint v2.14.0, Dockerfile and ignore-file fixes 2026-10-04 15:15:16 +02:00
clawbot added needs-checks and removed merge-ready labels 2026-10-06 00:50:12 +02:00
sneak was unassigned by clawbot 2026-10-06 00:50:12 +02:00
clawbot self-assigned this 2026-10-06 00:50:12 +02:00
Author
Collaborator

Back to needs-checks: the check on next is red because the shared Actions runner fails every job in its checkout step, not because of anything on this branch. That fault is sneak/project-management#27. This PR goes back to merge-ready once the check on its head re-runs green.

Model: opus-5-5

Back to `needs-checks`: the check on `next` is red because the shared Actions runner fails every job in its checkout step, not because of anything on this branch. That fault is https://git.eeqj.de/sneak/project-management/issues/27. This PR goes back to `merge-ready` once the check on its head re-runs green. Model: opus-5-5
clawbot added 1 commit 2026-10-06 02:52:24 +02:00
`script/cibuild`, `script/docker`, `script/lint` and `script/test` passed the image tag from `script/projectname` inline in the `docker build` arguments. A failing command substitution inside an argument does not trip `set -e`, so the scripts went on to `docker build` with an empty, `-lint` or `-test` tag, against the rule their own comment states. Each now assigns `tag` on its own line before `docker build`, so the script stops where `script/projectname` fails. The snippets in `prompts/REPO_POLICIES.md` show the same form, and the policy states the own-line rule.

Consuming repositories pick this up on their next re-vendor; the defect failed closed.

Model: opus-5-5
clawbot added merge-ready and removed needs-checks labels 2026-10-06 03:15:24 +02:00
clawbot removed their assignment 2026-10-06 03:15:24 +02:00
sneak was assigned by clawbot 2026-10-06 03:15:24 +02:00
Author
Collaborator

Back to merge-ready: sneak ruled that the server-side check gates nothing and a local make check is trusted (sneak/keyfunc#28 (comment)).

Model: opus-5-5

Back to `merge-ready`: sneak ruled that the server-side check gates nothing and a local `make check` is trusted (https://git.eeqj.de/sneak/keyfunc/pulls/28#issuecomment-127051). Model: opus-5-5
clawbot added 1 commit 2026-10-06 04:15:18 +02:00
The canonical `.gitignore` had no place for a repository's own build outputs, and nothing said a repository's own `.editorconfig` sections survive a re-vendor, so re-vendoring dropped them: a Go repository lost `/bin/`, `*.test` and `*.out`, and its tabs for Go files.

`.gitignore` now ends with a section, like `.dockerignore`'s header, where a repository adds its own build outputs (a root binary written `/myapp`) and its own environment-template negations, which a re-vendor keeps. `.editorconfig` gains `[*.go]` with tabs and the same kind of closing note. `prompts/REPO_POLICIES.md`, both checklists and the Go styleguide say these two files are the canonical content followed by the repository's own entries. Closes #104 too.

Model: opus-5-5
clawbot added 1 commit 2026-10-06 05:15:44 +02:00
In golangci-lint v2.14.0, `canonicalheader` misses findings at random in a package that also calls `ResponseWriter.Header()`: on the same tree, repeated runs sometimes reported a non-canonical header key and sometimes reported nothing. So one commit could fail lint on one run and pass on the next, in every Go repository that vendors this file. Reproduced with the pinned image and the canonical config.

The canonical `.golangci.yml` now disables it, with a comment saying it comes back once a pinned golangci-lint release fixes it. New `.golangci.yml` sha256: `e49052a1418127b54b20cea530dfd3cc6ddfc126a9fd27fd570ccca1a3f18bc7`.

Model: opus-5-5
clawbot added 1 commit 2026-10-06 05:52:59 +02:00
The canonical `.gitea/workflows/check.yml` lacked two settings `dnswatcher` had added, so a byte-identical re-vendor removed them. A `concurrency` block grouped by workflow and branch, with `cancel-in-progress: true`, makes a new push cancel the older run on the same branch and leaves every other branch's runs alone; on 2026-10-02 45 stale runs had queued on the one shared runner. `persist-credentials: false` on the checkout step keeps the job's token out of `.git/config`; `script/cibuild` needs no token. Each has a one-line comment, and the policy's workflow bullet and both checklists describe the file as it now is.

Unverified: the two live checks, which wait on the shared runner.

Model: opus-5-5
clawbot added 1 commit 2026-10-06 06:33:23 +02:00
A plain `docker build .` from a checkout whose `.git` is a file (a linked worktree, or a repository checked out as a submodule) stops at the version check of the canonical `Dockerfile` example: that file points to a git directory outside the build context, so `git describe` prints nothing. The check is right to refuse an empty version; what was missing is what to do. `prompts/REPO_POLICIES.md` and both checklists now say such a build is given its version with `--build-arg VERSION=...`, as `script/docker` and `script/cibuild` already do. The check itself is unchanged.

Model: opus-5-5
clawbot added 1 commit 2026-10-06 07:31:24 +02:00
`pkg_install` in the canonical `script/bootstrap` ran `apt-get install` with no `apt-get update` before it. The Gitea runner image starts with empty package lists, so a repository's own `pkg_install` of anything the image lacks, Go included, failed with "Unable to locate package". The apt branch now refreshes the lists once, before the first install of a run, in the same `$SUDO env DEBIAN_FRONTEND=noninteractive` form, and skips the refresh on later calls. nix, brew and apk are unchanged. Repositories that refresh in their own section, or changed their copy, drop that at their next re-vendor.

Model: opus-5-5
clawbot added 1 commit 2026-10-06 08:15:43 +02:00
The policy said a repository whose version comes from git tags needs `fetch-depth: 0` on its CI checkout, because the checkout action clones shallow with no tags; every repository's version comes from `git describe --tags --always`, but the canonical `.gitea/workflows/check.yml` did not set it, so CI stamped a bare short commit id where a local build of a tagged repository stamps the tag. The checkout step now sets `fetch-depth: 0` with a one-line comment, and the workflow bullet of `prompts/REPO_POLICIES.md` and both checklists name it beside `persist-credentials: false` and the `concurrency` block, as part of what the workflow does.

Unverified: a live run, which waits on the shared runner.

Model: opus-5-5
clawbot added 1 commit 2026-10-07 11:02:14 +02:00
Repositories copy package.json to get prettier, and with "license": "MIT"
each of them declared MIT whatever its own licence is. "private": true
keeps yarn from printing "No license field" and makes no licence claim.
This repository's LICENSE and README License section are unchanged.

Model: opus-5-5
clawbot added 1 commit 2026-10-07 11:31:37 +02:00
The canonical check workflow gave its job no time limit, so a hung
script/cibuild held the shared runner until the runner's own default.
The check job now sets timeout-minutes: 20. script/cibuild runs three
Docker builds (the test phase, the lint phase, then the image, which
runs both again), each held to the 5-minute build limit, plus the
bootstrap. REPO_POLICIES.md and both checklists name the limit among
what the workflow sets.

Model: opus-5-5
clawbot added 1 commit 2026-10-07 12:02:04 +02:00
script/lint and script/test build their Dockerfile phase with
--output type=cacheonly in place of a tag. The phase still runs
uncached and a failing step still fails the build, but no image is
written: nothing used those images, and writing one out took about 16
seconds of a Go repository's test build. script/cibuild and
script/docker keep their tags. REPO_POLICIES.md, both checklists and
the README now say the gate builds write no image.

Model: opus-5-5
clawbot added 1 commit 2026-10-07 13:02:09 +02:00
The new-repository checklist gave script/cibuild's image build without a
tag, so a repository written from it left a dangling image behind on
every run, and it used $version without saying where it comes from. The
item now gives the build as the canonical script/cibuild runs it, with
-t "$tag", and names the two steps the script runs before it, each on
its own line: the version from git describe --tags --always --dirty
(unknown if empty) and the tag from script/projectname. The other docker
build commands the checklists and REPO_POLICIES.md give for script/
already matched their scripts.

Model: opus-5-5
clawbot added 1 commit 2026-10-08 04:14:23 +02:00
script/bootstrap used whatever node was installed, so on the CI runner
image, which ships node 24, the pinned yarn 1.22.22 printed the
deprecation warning DEP0169 on every bootstrap. It now uses the
installed node only when its major version is the pinned one, and
otherwise installs the pinned node with nvm, as it already did when node
was missing, with yarn and the packages under it. It prints the node
version it uses. Only the major is compared because the Dockerfile
stages use a node 22 alpine image that nvm cannot replace. script/fmt
and script/fmt-check carry the same test to pick their yarn: the yarn on
PATH, or yarn under the pinned node loaded through nvm.

Model: opus-5-5
clawbot added 1 commit 2026-10-15 16:50:32 +02:00
Keep Go's build cache in check builds (closes #113)
check / check (push) Canceled after 0s
e3f79c49da
The script/ builds pass a new CACHEBUST build argument on every run in
place of --no-cache, which also emptied Go's build cache mount. Every
Dockerfile stage declares ARG CACHEBUST first, so every RUN step still
reruns on an unchanged tree. The canonical Go Dockerfile mounts only
Go's build cache, on the steps that compile, so compiled packages
survive between check builds; both go test runs carry -count=1, so no
test result is reused. golangci-lint's own cache and the module cache
are not kept.

A repository re-vendors the scripts and adds the ARG lines in one
commit: a stage without the line is served from cache with no warning.

Unverified: not yet tried on the shared CI runner.

Model: opus-5-5
clawbot added 1 commit 2026-10-18 16:56:28 +02:00
On sneak's ruling in #130, check
builds keep three more things besides Go's build cache. In each stage of
the canonical Go Dockerfile, the system-package install and the module
download come before ARG CACHEBUST, so Docker keeps those layers until
the base image, the install line, go.mod or go.sum changes. Modules are
kept as a layer, not a cache mount, because Go does not check a cache
mount's files again. golangci-lint run also mounts golangci-lint's cache.
Every step that lints, tests or compiles still comes after ARG CACHEBUST,
both go test runs keep -count=1, and a step that also installs node
packages stays below the line. The policy, checklists, Go style guide,
README and script comments say the same.

Model: opus-5-5
clawbot added 1 commit 2026-10-21 17:01:41 +02:00
sneak decided that homoicon keeps a local copy of sobek, its JavaScript
engine, and fixes it in its own tree
(sneak/homoicon#1759 (comment)).
homoicon's go.mod now replaces sobek with ./third_party/sobek, which
gomoddirectives refuses. The canonical .golangci.yml gains an exclusion
for exactly that report: a local replace of github.com/grafana/sobek in
a repository's root go.mod. Every other local replace is still refused.
It is an exclusion rather than a replace-allow-list entry because
gomoddirectives does not check that list for a replace that points at a
local path. TODO.md records the change.

Model: opus-5-5
clawbot added 1 commit 2026-10-26 20:39:00 +01:00
A Python `test` phase that installs its packages from `Pipfile.lock` now does so above `ARG CACHEBUST`, through `script/bootstrap`, in a layer of its own. Check builds keep that layer as they keep Go's modules, and the tests still run after the line on every build. In that step `script/bootstrap` installs only system packages, pipenv and its dependencies by hash, and the lockfile's packages. The policy, both checklists and the README list the steps above the line the same way; the policy has a short example. This writes down sneak's ruling at sneak/rfscan#80 (comment).

rfscan installs pipenv by version, not by hash, so its `script/bootstrap` does not meet this rule yet.

Model: opus-5-5
clawbot added 1 commit 2026-10-27 18:49:50 +01:00
Add API_POLICIES.md (closes #137)
check / check (push) Canceled after 0s
430964ceac
prompts/API_POLICIES.md states sneak's rule from
sneak/routewatch#199: a value in an API
response is never a string with a label or unit in it. A native value
is sent as that type, with its unit in the field name.

The list at the top of REPO_POLICIES.md and the two code style
commands in the README now name the new file.

Model: opus-5-5
clawbot added 1 commit 2026-11-03 23:08:25 +01:00
A hung test used the 90-second backstop twice, because the verbose
rerun waited for the same hang. The Go recipe now looks in the first
run's output for the lines Go prints for a hang, and exits 1 with no
rerun. A failure with neither line in its output still reruns with -v.

The Python make test example takes the same timeout wrapper as its
Dockerfile example. The 60 and 90 second rule and the 5-minute rule say
a hung package uses the backstop once. The Go item in both repo
checklists now asks for this.

Model: opus-5-5
clawbot added 1 commit 2026-11-04 00:28:25 +01:00
A package from the distribution's own package index now needs no version
and no hash, and pulling that index is allowed, on sneak's answer at
#72 (comment). The
distribution is the pinned base image's in a Dockerfile and the host's
on a host. It is trusted, and everything else stays pinned by hash. A
package source that a Dockerfile adds itself does not get the exception.

The pinning rule of REPO_POLICIES.md and both checklists say so, and the
canonical Go Dockerfile's apk add line has a comment saying why it is
allowed.

Model: opus-5-5
clawbot added 1 commit 2026-11-04 01:28:12 +01:00
Moves the pin to v2.15.0 and enables canonicalheader again, because that
release fixes the random misses behind
#105.

The depguard exemption reached every directory below one ending in test,
and a whole checkout under such a directory. It now covers only the files
directly inside, and the directory a repository is cloned into does not
count. gomodguard_v2 also blocks github.com/go-redis/redis/v9, whose
pre-releases can still be imported.

uniq-by-line is now false, so a line that trips two linters reports both
findings.

Three comments are corrected. A new comment says the file has no
formatters section, so golangci-lint run does not run gofmt.

Model: opus-5-5
clawbot changed title from next → main: version from git, golangci-lint v2.14.0, Dockerfile and ignore-file fixes to next → main: version from git, golangci-lint v2.15.0, Dockerfile and ignore-file fixes 2026-11-04 01:28:49 +01:00
Author
Collaborator

Nothing more runs in this repository until you answer.

Waiting on you:

  • This PR. It now also carries #61, #72 and #142.
  • #147: the sentence-by-sentence rewrite of REPO_POLICIES.md. Its review passed and it is assigned to you. Its For sneak list names eight places where the text looks false or contradicts another rule; the rewrite kept them as they were.
  • #51 (comment): may workers rerun one package or test through a make target?

Open and not built, because they are outside the current order: #128, #139, #140, #141 and #146. The last was found while reviewing the pinning rule: script/bootstrap installs yarn and node by version, not by hash. #78 goes on as each repository re-vendors.

Model: opus-5-5

Nothing more runs in this repository until you answer. **Waiting on you:** - This PR. It now also carries https://git.eeqj.de/sneak/prompts/issues/61, https://git.eeqj.de/sneak/prompts/issues/72 and https://git.eeqj.de/sneak/prompts/issues/142. - https://git.eeqj.de/sneak/prompts/pulls/147: the sentence-by-sentence rewrite of `REPO_POLICIES.md`. Its review passed and it is assigned to you. Its `For sneak` list names eight places where the text looks false or contradicts another rule; the rewrite kept them as they were. - https://git.eeqj.de/sneak/prompts/issues/51#issuecomment-165252: may workers rerun one package or test through a make target? **Open and not built, because they are outside the current order:** https://git.eeqj.de/sneak/prompts/issues/128, https://git.eeqj.de/sneak/prompts/issues/139, https://git.eeqj.de/sneak/prompts/issues/140, https://git.eeqj.de/sneak/prompts/issues/141 and https://git.eeqj.de/sneak/prompts/issues/146. The last was found while reviewing the pinning rule: `script/bootstrap` installs yarn and node by version, not by hash. https://git.eeqj.de/sneak/prompts/issues/78 goes on as each repository re-vendors. Model: opus-5-5
sneak added 1 commit 2026-11-04 05:17:17 +01:00
Rewrites every sentence of `prompts/REPO_POLICIES.md` against the sentence rules, for #136. No rule's meaning, `must`/`should` or order changed. Code blocks, digests, URLs and commands are byte-identical. Both checklists carry the same rewrite of the paragraph they quote.

## For sneak

Kept as they are: each looks false or contradicts another rule. Line numbers are this branch's.

- Lines 196-197: the final stage carries the `COPY --from=` lines; in the `Dockerfile` (345-346) the build stage does, the runtime stage none.
- Lines 629-641 and 670-675: `make test` runs `go test` and `pytest` on the host; 80-81 make targets shims, 183-185 make `script/test` a Docker build.
- Lines 23-37 pin by hash; 103-112 install node (nvm) and yarn (corepack) by version. Tracked at #146.
- Line 131 counts four extensions; `script/lint`, `script/fmt`, `script/fmt-check`, `script/docker` (85-89) are not in the standard either.
- Lines 149-151: the Makefile is the single source of truth; 80-81 (implementation in `script/`), 211 (tool invoked in a phase), 616 (test command in the `Dockerfile`).
- Lines 64-68 require `make test` and `make lint`, which 183-185 make Docker builds; line 167 only says a repo should have a `Dockerfile`.
- Line 759 says byte-identical; the block at 767 has `tag="$(script/projectname)"`, both scripts have `tag="$("$SCRIPT_DIR/projectname")"`.
- Lines 834-839: `script/bootstrap` never tests presence, 860 excepts only node; `ensure_yarn` in the canonical `script/bootstrap` skips the install when yarn is present, without a version check.

## Disclosures

- Judgement call: short name lists stay inline under about 15 words.

Model: fable-5-1
Co-authored-by: sneak <sneak@sneak.berlin>
Reviewed-on: #147
Co-authored-by: clawbot <35+clawbot@noreply.example.org>
clawbot added 1 commit 2026-11-04 09:55:28 +01:00
Follows sneak's ruling at
#146 (comment). Runtimes
and the installer tools bundled with python come from the distribution,
with no version and no hash. Third-party packages stay pinned by hash.

script/bootstrap drops nvm and corepack. It installs node, yarn and the
node packages only when yarn.lock is in the tree, and stops there when
yarn's major version is not 1. This repository's Dockerfile starts from
Alpine 3.24. The Python example starts from debian:trixie-slim, keeps
pipenv at $HOME/.pipenv on a host and in an image, and sets
PIP_ONLY_BINARY=:all:, so pip downloads no build tools without a hash
check.

Model: opus-5-5
clawbot changed title from next → main: version from git, golangci-lint v2.15.0, Dockerfile and ignore-file fixes to next → main: version from git, runtimes from the distribution, golangci-lint v2.15.0, Dockerfile and ignore-file fixes 2026-11-04 09:56:32 +01:00
Author
Collaborator

#146 is on next. Nothing more runs in this repository until you answer.

Waiting on you:

  • This PR, now also carrying that change.
  • #149: stay on yarn 1, given that the newest Debian and Ubuntu package yarn 4?
  • #51 (comment): may workers rerun one package or test through a make target?

The For sneak list on #147 still stands, apart from its two items about the pinning rule and ensure_yarn, which this change settled.

Open and not built, because they are outside the current order: #128, #139 and #141. #150 waits on the yarn answer. #78 goes on as each repository re-vendors.

Model: opus-5-5

https://git.eeqj.de/sneak/prompts/issues/146 is on `next`. Nothing more runs in this repository until you answer. **Waiting on you:** - This PR, now also carrying that change. - https://git.eeqj.de/sneak/prompts/issues/149: stay on yarn 1, given that the newest Debian and Ubuntu package yarn 4? - https://git.eeqj.de/sneak/prompts/issues/51#issuecomment-165252: may workers rerun one package or test through a make target? The `For sneak` list on https://git.eeqj.de/sneak/prompts/pulls/147 still stands, apart from its two items about the pinning rule and `ensure_yarn`, which this change settled. **Open and not built, because they are outside the current order:** https://git.eeqj.de/sneak/prompts/issues/128, https://git.eeqj.de/sneak/prompts/issues/139 and https://git.eeqj.de/sneak/prompts/issues/141. https://git.eeqj.de/sneak/prompts/issues/150 waits on the yarn answer. https://git.eeqj.de/sneak/prompts/issues/78 goes on as each repository re-vendors. Model: opus-5-5
clawbot added 1 commit 2026-11-04 19:32:39 +01:00
Follows sneak's ruling at
#51 (comment). 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. `go build`,
`go vet` and the linter still go through make targets. The full `make check`
is still required before every push. The `Makefile` gains nothing.

The rule on Docker now says the lint and test gates run there, because a run
while iterating is not a gate.

Model: opus-5-5
clawbot added 1 commit 2026-11-04 21:32:57 +01:00
Follows sneak's ruling at
#149 (comment). This
repository now carries yarn 4.18.1 under .yarn/releases. The
distribution's yarn only starts that file, and that file does the work.
yarn.lock is converted once, and script/bootstrap installs with
--immutable instead of stopping on yarn's major version. Node is the
distribution's newest Node package: nodejs-current on Alpine and
nodejs_latest on nix. 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
clawbot changed title from next → main: version from git, runtimes from the distribution, golangci-lint v2.15.0, Dockerfile and ignore-file fixes to next → main: version from git, runtimes from the distribution, yarn 4 in the tree, golangci-lint v2.15.0, Dockerfile and ignore-file fixes 2026-11-04 21:33:32 +01:00
Author
Collaborator

#149 and #51 are on next. Nothing more runs in this repository until you answer or the order changes.

Waiting on you:

  • This PR, now also carrying both of those.
  • The For sneak list on #147, apart from its two items about the pinning rule and ensure_yarn.

Open and not built, because they are outside the current order: #128, #139, #141 and #153. #78 goes on as each repository re-vendors.

Model: opus-5-5

https://git.eeqj.de/sneak/prompts/issues/149 and https://git.eeqj.de/sneak/prompts/issues/51 are on `next`. Nothing more runs in this repository until you answer or the order changes. **Waiting on you:** - This PR, now also carrying both of those. - The `For sneak` list on https://git.eeqj.de/sneak/prompts/pulls/147, apart from its two items about the pinning rule and `ensure_yarn`. **Open and not built, because they are outside the current order:** https://git.eeqj.de/sneak/prompts/issues/128, https://git.eeqj.de/sneak/prompts/issues/139, https://git.eeqj.de/sneak/prompts/issues/141 and https://git.eeqj.de/sneak/prompts/issues/153. https://git.eeqj.de/sneak/prompts/issues/78 goes on as each repository re-vendors. Model: opus-5-5
clawbot added 1 commit 2026-11-06 01:18:46 +01:00
clawbot added 1 commit 2026-11-06 01:18:54 +01:00
clawbot added 1 commit 2026-11-07 05:21:34 +01:00
clawbot added 1 commit 2026-11-09 11:00:11 +01:00
One command fetches the policy files a repo vendors from `prompts`, keeps the repo's own entries, commits them by name and pushes. A line the canonical file has never held stops the script before anything is written, as does a failed fetch. The canonical `.dockerignore` gains the closing comment the script needs. `test/update-policy.sh` runs in the `test` phase of `Dockerfile`.

Judgement call: `.gitea/workflows/check.yml` and `.prettierrc` are replaced whole.
Limitation: a repo with its own `.golangci.yml` settings, such as `sneak/homoicon`, stops until those are dealt with.

Model: opus-5-5
All checks were successful
check / check (push) Successful in 24s
Required
Details
You are not authorized to merge this pull request.
This pull request can be merged automatically.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin next:next
git checkout next
Sign in to join this conversation.