3 Commits
Author SHA1 Message Date
sneak 4928c6a890 Fold the August fleet findings into the policies, or drop them (closes #62)
check / check (push) Failing after 1s
Two findings from 2026-08-09 were rules a repository must follow that
`prompts/REPO_POLICIES.md` did not yet state, and are now added where a
reader would look: a new or changed check is proven by planting a defect
it must catch; and a change to a separate workflow limited to `main` by
a `branches` list is first run from the feature branch, added to that
list and removed again before merging. The issue records why every
other finding was dropped. The warning against `golangci-lint config
verify` is dropped because sneak ruled on
#40 (2026-08-10) that there is
no config check step and the config is assumed valid; a vendored
`.golangci.yml` stays byte-identical to the canonical copy.

Model: opus-5-5
2026-10-04 12:33:00 +00:00
clawbot 13125ac6f5 Merge TODO.md with git's union merge (closes #98)
check / check (push) Failing after 1s
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
2026-10-04 14:14:51 +02:00
clawbot 61a9afbb4f Keep a submodule's own .git/config out of the build context (closes #88)
check / check (push) Failing after 2s
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
2026-10-04 12:48:55 +02:00
6 changed files with 79 additions and 17 deletions
+10 -3
View File
@@ -18,9 +18,16 @@
# does not need .git/config; that file can hold a credential, such as a # does not need .git/config; that file can hold a credential, such as a
# password in a remote URL or the token the CI checkout step stores there. # password in a remote URL or the token the CI checkout step stores there.
# Each submodule keeps a config with the same exposure in its git directory # Each submodule keeps a config with the same exposure in its git directory
# under .git/modules/, nested again for a submodule's own submodules. # under .git/modules/, nested again for a submodule's own submodules, or in
.git/config # its own .git directory when it keeps one.
.git/modules/**/config # KNOWN GAP: a submodule whose name has a `config` segment (`config`,
# `deploy/config`, `config/lib`) loses its whole git directory, because
# `**/.git/modules/**/config` also matches that segment's directory
# under .git/modules/. Go's version stamping then fails the build;
# nothing leaks. Name such a submodule without that segment:
# `git submodule add --name`.
**/.git/config
**/.git/modules/**/config
# Agent scratch: one full checkout of the repo per in-flight agent. # Agent scratch: one full checkout of the repo per in-flight agent.
# Anchored because it occurs once where agents run at the repo root. # Anchored because it occurs once where agents run at the repo root.
+4
View File
@@ -0,0 +1,4 @@
# Every PR adds an entry at the top of TODO.md's Completed Steps; union keeps
# both sides instead of conflicting. Git never reports a conflict here: read
# the merged entries after every merge or rebase.
TODO.md merge=union
+28
View File
@@ -21,6 +21,34 @@ fmt-check, and commit.
# Completed Steps # Completed Steps
- 2026-10-04: Went through the fleet findings recorded on 2026-08-09 (issue 62)
and added the two rules `REPO_POLICIES.md` did not yet state: a new or changed
check is proven by planting a defect it must catch; and a change to a separate
workflow limited to `main` is first run from the feature branch, added to that
workflow's `branches` list and removed again before merging. The other
findings were already stated, replaced by `--no-cache`, about git worktrees,
or about how agents work together. The warning against
`golangci-lint config verify` is dropped because sneak ruled on
https://git.eeqj.de/sneak/prompts/issues/40 (2026-08-10) that there is no
config check step and the config is assumed valid; a vendored `.golangci.yml`
stays byte-identical to the canonical copy. The issue gives each reason.
- 2026-10-04: Added a root `.gitattributes` that merges `TODO.md` with git's
union merge (issue 98), so two branches that each add an entry at the top of
Completed Steps merge without a conflict. Git now never reports a conflict in
`TODO.md`: a real conflict elsewhere keeps both versions of the line, and when
two new entries share an identical line, one is inserted into the middle of
the other, which a rebase can do to an entry already on `next`. Read the
merged entries after every merge or rebase. This applies to this repository
only; no canonical file changed.
- 2026-10-04: The canonical `.dockerignore` now also keeps out the git `config`
of a submodule that keeps its own `.git` directory, which still reached the
image (issue 88): both git patterns now carry the `**/` prefix. A submodule
whose name has a `config` segment (`config`, `deploy/config`, `config/lib`)
still loses its whole git directory, so Go's version stamping fails the build;
the file records this as a `KNOWN GAP:` with the remedy,
`git submodule add --name`. Closing it would take a wildcard re-include, which
makes BuildKit walk every excluded directory, such as `node_modules`, on every
build. `REPO_POLICIES.md` and both checklists say so in the same words.
- 2026-10-04: The note under the canonical Go `Dockerfile` example in - 2026-10-04: The note under the canonical Go `Dockerfile` example in
`REPO_POLICIES.md` now installs lint-phase system libraries with `apt-get` `REPO_POLICIES.md` now installs lint-phase system libraries with `apt-get`
under their Debian package names (issue 83). The `golangci/golangci-lint` under their Debian package names (issue 83). The `golangci/golangci-lint`
+9 -4
View File
@@ -63,10 +63,15 @@ with your task.
here run anywhere other than the repo root, the anchored entry misses here run anywhere other than the repo root, the anchored entry misses
`services/api/.claude/`: add anchored entries for those directories. `services/api/.claude/`: add anchored entries for those directories.
- [ ] If the repo embeds a version in a binary: `.dockerignore` lets `.git` into - [ ] If the repo embeds a version in a binary: `.dockerignore` lets `.git` into
the build context. It keeps out `.git/config` and each submodule's the build context. It keeps out every git `config` at any depth
`config` under `.git/modules/` at any depth (`.git/modules/**/config`), (`**/.git/config`, `**/.git/modules/**/config`): the repository's own,
which `git describe` does not need and which can hold a credential: a each submodule's under `.git/modules/`, and that of a submodule keeping
password in a remote URL, or the token the CI checkout step stores there. its own `.git` directory. `git describe` does not need them, and each can
hold a credential: a password in a remote URL, or the token the CI
checkout step stores there. A submodule whose name has a `config` segment
(`config`, `deploy/config`, `config/lib`) loses its whole git directory to
`**/.git/modules/**/config`, and Go's version stamping then fails the
build: give it a name without that segment (`git submodule add --name`).
The stage that compiles has `git` (the Debian Go image has it; an alpine The stage that compiles has `git` (the Debian Go image has it; an alpine
one needs `apk add --no-cache git`) and takes the version from the one needs `apk add --no-cache git`) and takes the version from the
`VERSION` build argument when one is given, otherwise from `VERSION` build argument when one is given, otherwise from
+9 -4
View File
@@ -71,10 +71,15 @@ Template files can be fetched from:
will run them in subdirectories, `services/api/.claude/` needs its own will run them in subdirectories, `services/api/.claude/` needs its own
anchored entry. anchored entry.
- If the image embeds a version in a binary: `.dockerignore` lets `.git` - If the image embeds a version in a binary: `.dockerignore` lets `.git`
into the build context. It keeps out `.git/config` and each submodule's into the build context. It keeps out every git `config` at any depth
`config` under `.git/modules/` at any depth (`.git/modules/**/config`), (`**/.git/config`, `**/.git/modules/**/config`): the repository's own,
which `git describe` does not need and which can hold a credential: a each submodule's under `.git/modules/`, and that of a submodule keeping
password in a remote URL, or the token the CI checkout step stores there. its own `.git` directory. `git describe` does not need them, and each can
hold a credential: a password in a remote URL, or the token the CI
checkout step stores there. A submodule whose name has a `config` segment
(`config`, `deploy/config`, `config/lib`) loses its whole git directory to
`**/.git/modules/**/config`, and Go's version stamping then fails the
build: give it a name without that segment (`git submodule add --name`).
The stage that compiles has `git` (the Debian Go image has it; an alpine The stage that compiles has `git` (the Debian Go image has it; an alpine
one needs `apk add --no-cache git`) and takes the version from the one needs `apk add --no-cache git`) and takes the version from the
`VERSION` build argument when one is given, otherwise from `VERSION` build argument when one is given, otherwise from
+19 -6
View File
@@ -156,6 +156,9 @@ style conventions are in separate documents:
not evidence that anything ran: a sub-second build reporting success is a not evidence that anything ran: a sub-second build reporting success is a
cache hit, not a result. Never invalidate by pruning — `docker builder prune` cache hit, not a result. Never invalidate by pruning — `docker builder prune`
and friends destroy a build cache shared with every other build on the host. and friends destroy a build cache shared with every other build on the host.
When a check is added or changed, prove it works by planting a defect it must
catch and watching the run fail on it, then revert the defect. A green run
alone shows neither that the check ran nor that it covers what it should.
- **The gate phases are separate stages, and the build stage depends on both.** - **The gate phases are separate stages, and the build stage depends on both.**
The lint phase is based on the `golangci/golangci-lint` image (pinned by The lint phase is based on the `golangci/golangci-lint` image (pinned by
@@ -248,11 +251,16 @@ style conventions are in separate documents:
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
``` ```
- `.dockerignore` lets `.git` into the build context. It keeps out - `.dockerignore` lets `.git` into the build context. It keeps out every git
`.git/config` and each submodule's `config` under `.git/modules/` at any `config` at any depth (`**/.git/config`, `**/.git/modules/**/config`): the
depth (`.git/modules/**/config`), which `git describe` does not need and repository's own, each submodule's under `.git/modules/`, and that of a
which can hold a credential: a password in a remote URL, or the token the submodule keeping its own `.git` directory. `git describe` does not need
CI checkout step stores there. The stage that compiles has `git` (the them, and each can hold a credential: a password in a remote URL, or the
token the CI checkout step stores there. A submodule whose name has a
`config` segment (`config`, `deploy/config`, `config/lib`) loses its whole
git directory to `**/.git/modules/**/config`, and Go's version stamping
then fails the build: give it a name without that segment
(`git submodule add --name`). The stage that compiles has `git` (the
Debian Go image has it; an alpine one needs `apk add --no-cache git`) and Debian Go image has it; an alpine one needs `apk add --no-cache git`) and
takes the version from the `VERSION` build argument when one is given, takes the version from the `VERSION` build argument when one is given,
otherwise from `git describe --tags --always`. That gives the tag on a otherwise from `git describe --tags --always`. That gives the tag on a
@@ -274,7 +282,12 @@ style conventions are in separate documents:
carry the same guarantee, because its gate phases may come from the cache. The carry the same guarantee, because its gate phases may come from the cache. The
image build is uncached and so runs the gate phases a second time. That is the image build is uncached and so runs the gate phases a second time. That is the
price of the rule above, and it is worth paying: the image that ships is built price of the rule above, and it is worth paying: the image that ships is built
from a run of its own gates rather than from a cache entry. from a run of its own gates rather than from a cache entry. A separate
workflow limited to `main` by a `branches` list under `on: push` cannot be
checked by review: to try a change to it, add the feature branch to that list
and push, then remove the branch from the list again before merging. Keep any
job in it that publishes behind `if: github.ref_name == 'main'`, so the run
from the feature branch publishes nothing.
- Use platform-standard formatters: `black` for Python, `prettier` for - Use platform-standard formatters: `black` for Python, `prettier` for
JS/CSS/Markdown/HTML, `go fmt` for Go. Always use default configuration with JS/CSS/Markdown/HTML, `go fmt` for Go. Always use default configuration with