Add canonical policy dotfiles, harden both ignore files (closes #8) — the
functional change.
Reformat REPO_POLICIES.md with the repo's prettier settings — whitespace
only, see the conflict note below.
What landed
REPO_POLICIES.md — byte-identical copy of prompts/prompts/REPO_POLICIES.md, YAML front matter (title:, last_modified: 2026-08-07) intact so it can be diffed against upstream as
policy evolves.
.editorconfig, .prettierrc, .prettierignore — canonical contents
verbatim. script/fmt and script/fmt-check keep passing --tab-width 4 --prose-wrap always on the command line; the values agree
with .prettierrc, so make fmt behaves identically before and after.
.gitignore — three Hugo lines kept, canonical OS/editor/node/secrets
block appended, plus .claude/. No **/ prefixes here, deliberately:
unanchored .gitignore patterns already match at any depth.
.dockerignore — same coverage, different syntax (below).
Not touched, per the issue's out-of-scope list: LICENSE (that is #10, awaiting an owner
decision, so the README License section is unchanged), go.mod, package.json, yarn.lock, CONTRIBUTING.md, SECURITY.md, .gitattributes, CHANGELOG.md, renovate/dependabot, agent scaffolding.
script/bootstrap and .gitea/workflows/deploy.yml are untouched, so the live
Cloudflare Pages deploy path is unaffected and no temporary branch trigger was
needed.
The .dockerignore correction is the substantive part
.dockerignore does not use .gitignore semantics. It matches with Go's filepath.Match rules extended with **: * does not cross /, and an
unprefixed pattern is anchored at the context root. Since the Dockerfile does COPY . ., a naive copy of the .gitignore block would protect only the repo
root while reading as complete — worse than an obviously incomplete file.
Every depth-independent pattern therefore carries an explicit **/ prefix.
Only entries that are root-anchored by definition go bare: .git, Hugo's public and resources output directories, and .hugo_build.lock. A comment
at the top of the file states the rule so the next edit does not quietly undo
it.
Verified at depth, with a counterfactual
Planted a/b/.env, a/b/server.key, a/b/deep.pem, a/b/node_modules/x, config/.env, certs/server.key, .claude/worktrees/marker.txt. Root-only
testing does not exercise this bug.
Naive form (canonical patterns, no **/) — everything at depth still reaches
the context:
=== find secrets/node_modules/.claude in the real image ===
(exit 0)
=== ls -a /src ===
.
..
.dockerignore
.editorconfig
.gitea
.gitignore
.hugo_build.lock
.prettierignore
.prettierrc
Dockerfile
Makefile
README.md
REPO_POLICIES.md
TODO.md
a
archetypes
content
hugo.toml
public
script
themes
find /src -name '.env' -o -name '*.key' -o -name '*.pem' -o -name 'node_modules' -o -name '.claude' returns nothing. .claude is absent, which
also keeps .claude/worktrees/ — entire additional checkouts of this repo —
out of the image. The empty a directory shell survives: .dockerignore
excludes matching files, and an emptied parent directory is still created in
the context. No file inside it made it. public and .hugo_build.lock in /src are produced by make check inside the container, not copied in — both
are excluded from the context. Throwaway files and probe images were deleted
after; no docker builder prune was run.
Excluding .claude/ removes a live source of context churn, so #23's cache-bust needed
re-proving against the smaller context. Two consecutive script/cibuild runs
on an unchanged tree.
Run 1:
#9 [4/7] RUN script/bootstrap
#9 CACHED
#10 [5/7] COPY . .
#10 DONE 0.9s
#12 [7/7] RUN echo "check epoch: 17862914114909125993851282" && make check
#12 0.577 check epoch: 17862914114909125993851282
#12 0.671 Start building sites …
#12 0.714 Total in 50 ms
#12 1.014 Start building sites …
#12 1.049 Total in 38 ms
#12 4.815 Checking formatting...
#12 7.176 All matched files use Prettier code style!
#12 DONE 11.9s
Run 2, immediately after, tree untouched:
#9 [4/7] RUN script/bootstrap
#9 CACHED
#10 [5/7] COPY . .
#10 CACHED
#12 [7/7] RUN echo "check epoch: 17862914352772807423868361" && make check
#12 0.313 check epoch: 17862914352772807423868361
#12 0.386 Start building sites …
#12 0.445 Total in 60 ms
#12 0.584 Start building sites …
#12 0.627 Total in 45 ms
#12 2.283 Checking formatting...
#12 3.126 All matched files use Prettier code style!
#12 DONE 3.3s
COPY . . is CACHED in run 2 and the toolchain layer stays CACHED in both,
yet the check layer executes both times — two Hugo builds and the prettier line
each run. The guard still holds too: a bare docker build . fails on RUN [ -n "$CHECK_EPOCH" ] || exit 1, exit 1, as designed.
Conflict this PR introduces, and which side is authoritative
Adding REPO_POLICIES.md puts two statements about make check in the tree:
REPO_POLICIES.md line 233: "make check must not modify any files in the
repo."
script/check header: "Must not modify any tracked files."
They contradict, and script/check's narrower wording is the one that
matches reality here.make check runs hugo --minify, which writes public/ and .hugo_build.lock. Both are gitignored, so git status --porcelain is empty after a run — I confirmed this — but files in
the working tree are unambiguously created. The policy sentence as written is
unsatisfiable for any Hugo repo, and for any repo whose check builds artifacts
at all.
I did not edit REPO_POLICIES.md to resolve this: it is a verbatim
canonical copy, and diverging it locally defeats the purpose of committing it.
The fix belongs upstream in prompts — the sentence should read "must not
modify any tracked files" — and until then script/check's header is the
accurate description of this repo's behaviour. Flagging for a decision rather
than silently leaving the two in the tree contradicting each other; I have not
filed anything, since the wording is the owner's to settle.
Second conflict: the canonical copy is not prettier-clean
The upstream REPO_POLICIES.md does not satisfy --tab-width 4 --prose-wrap always: prettier@3.4.2 inserts a blank line before a nested list
that directly follows a paragraph, in five places. script/fmt-check covers *.md at the repo root, so make check fails on the byte-identical copy.
The issue's DoD requires both byte-identity and a diff-free make fmt, which
cannot hold simultaneously. Resolved the way the DoD anticipates: commit 1 adds
the byte-identical copy, commit 2 is the reformat alone, five blank lines, no
rendered change. Reviewing git show on commit 1 against the upstream raw URL
is a clean byte comparison. Syncing the reformat back upstream so future syncs
are a straight byte copy is now a Future Step in TODO.md.
Checks
make fmt — no diff at HEAD (idempotent, all three files "unchanged").
make check — green; git status --porcelain empty afterwards.
make docker — builds and tags lora.vegas.
.gitignore — git check-ignore -v confirms .claude/worktrees/x, .DS_Store, test.key and .env are all matched; a checkout with agent
tooling present is git status-clean.
Not verified: the Cloudflare Pages deploy was not exercised, since nothing on
that path changed.
Closes [#8](https://git.eeqj.de/sneak/lora.vegas/issues/8).
Two commits, deliberately split:
1. `Add canonical policy dotfiles, harden both ignore files (closes #8)` — the
functional change.
2. `Reformat REPO_POLICIES.md with the repo's prettier settings` — whitespace
only, see the conflict note below.
## What landed
- **`REPO_POLICIES.md`** — byte-identical copy of
`prompts/prompts/REPO_POLICIES.md`, YAML front matter (`title:`,
`last_modified: 2026-08-07`) intact so it can be diffed against upstream as
policy evolves.
- **`.editorconfig`, `.prettierrc`, `.prettierignore`** — canonical contents
verbatim. `script/fmt` and `script/fmt-check` keep passing
`--tab-width 4 --prose-wrap always` on the command line; the values agree
with `.prettierrc`, so `make fmt` behaves identically before and after.
- **`.gitignore`** — three Hugo lines kept, canonical OS/editor/node/secrets
block appended, plus `.claude/`. No `**/` prefixes here, deliberately:
unanchored `.gitignore` patterns already match at any depth.
- **`.dockerignore`** — same coverage, different syntax (below).
Not touched, per the issue's out-of-scope list: `LICENSE` (that is
[#10](https://git.eeqj.de/sneak/lora.vegas/issues/10), awaiting an owner
decision, so the README License section is unchanged), `go.mod`,
`package.json`, `yarn.lock`, `CONTRIBUTING.md`, `SECURITY.md`,
`.gitattributes`, `CHANGELOG.md`, renovate/dependabot, agent scaffolding.
`script/bootstrap` and `.gitea/workflows/deploy.yml` are untouched, so the live
Cloudflare Pages deploy path is unaffected and no temporary branch trigger was
needed.
## The `.dockerignore` correction is the substantive part
`.dockerignore` does **not** use `.gitignore` semantics. It matches with Go's
`filepath.Match` rules extended with `**`: `*` does not cross `/`, and an
unprefixed pattern is anchored at the context root. Since the `Dockerfile` does
`COPY . .`, a naive copy of the `.gitignore` block would protect only the repo
root while reading as complete — worse than an obviously incomplete file.
Every depth-independent pattern therefore carries an explicit `**/` prefix.
Only entries that are root-anchored by definition go bare: `.git`, Hugo's
`public` and `resources` output directories, and `.hugo_build.lock`. A comment
at the top of the file states the rule so the next edit does not quietly undo
it.
### Verified at depth, with a counterfactual
Planted `a/b/.env`, `a/b/server.key`, `a/b/deep.pem`, `a/b/node_modules/x`,
`config/.env`, `certs/server.key`, `.claude/worktrees/marker.txt`. Root-only
testing does not exercise this bug.
Naive form (canonical patterns, no `**/`) — everything at depth still reaches
the context:
```
=== NAIVE (unprefixed patterns) .dockerignore ===
/src/certs/server.key
/src/a/b/node_modules
/src/a/b/deep.pem
/src/a/b/server.key
/src/a/b/.env
/src/config/.env
```
Shipped form, against the real `make docker` image:
```
=== find secrets/node_modules/.claude in the real image ===
(exit 0)
=== ls -a /src ===
.
..
.dockerignore
.editorconfig
.gitea
.gitignore
.hugo_build.lock
.prettierignore
.prettierrc
Dockerfile
Makefile
README.md
REPO_POLICIES.md
TODO.md
a
archetypes
content
hugo.toml
public
script
themes
```
`find /src -name '.env' -o -name '*.key' -o -name '*.pem' -o -name
'node_modules' -o -name '.claude'` returns nothing. `.claude` is absent, which
also keeps `.claude/worktrees/` — entire additional checkouts of this repo —
out of the image. The empty `a` directory shell survives: `.dockerignore`
excludes matching *files*, and an emptied parent directory is still created in
the context. No file inside it made it. `public` and `.hugo_build.lock` in
`/src` are produced by `make check` inside the container, not copied in — both
are excluded from the context. Throwaway files and probe images were deleted
after; no `docker builder prune` was run.
## Re-verified [#23](https://git.eeqj.de/sneak/lora.vegas/issues/23) against the new context
Excluding `.claude/` removes a live source of context churn, so
[#23](https://git.eeqj.de/sneak/lora.vegas/issues/23)'s cache-bust needed
re-proving against the smaller context. Two consecutive `script/cibuild` runs
on an unchanged tree.
Run 1:
```
#9 [4/7] RUN script/bootstrap
#9 CACHED
#10 [5/7] COPY . .
#10 DONE 0.9s
#12 [7/7] RUN echo "check epoch: 17862914114909125993851282" && make check
#12 0.577 check epoch: 17862914114909125993851282
#12 0.671 Start building sites …
#12 0.714 Total in 50 ms
#12 1.014 Start building sites …
#12 1.049 Total in 38 ms
#12 4.815 Checking formatting...
#12 7.176 All matched files use Prettier code style!
#12 DONE 11.9s
```
Run 2, immediately after, tree untouched:
```
#9 [4/7] RUN script/bootstrap
#9 CACHED
#10 [5/7] COPY . .
#10 CACHED
#12 [7/7] RUN echo "check epoch: 17862914352772807423868361" && make check
#12 0.313 check epoch: 17862914352772807423868361
#12 0.386 Start building sites …
#12 0.445 Total in 60 ms
#12 0.584 Start building sites …
#12 0.627 Total in 45 ms
#12 2.283 Checking formatting...
#12 3.126 All matched files use Prettier code style!
#12 DONE 3.3s
```
`COPY . .` is `CACHED` in run 2 and the toolchain layer stays `CACHED` in both,
yet the check layer executes both times — two Hugo builds and the prettier line
each run. The guard still holds too: a bare `docker build .` fails on
`RUN [ -n "$CHECK_EPOCH" ] || exit 1`, exit 1, as designed.
## Conflict this PR introduces, and which side is authoritative
Adding `REPO_POLICIES.md` puts two statements about `make check` in the tree:
- `REPO_POLICIES.md` line 233: "`make check` must not modify any files in the
repo."
- `script/check` header: "Must not modify any **tracked** files."
They contradict, and **`script/check`'s narrower wording is the one that
matches reality here.** `make check` runs `hugo --minify`, which writes
`public/` and `.hugo_build.lock`. Both are gitignored, so
`git status --porcelain` is empty after a run — I confirmed this — but files in
the working tree are unambiguously created. The policy sentence as written is
unsatisfiable for any Hugo repo, and for any repo whose check builds artifacts
at all.
I did **not** edit `REPO_POLICIES.md` to resolve this: it is a verbatim
canonical copy, and diverging it locally defeats the purpose of committing it.
The fix belongs upstream in `prompts` — the sentence should read "must not
modify any tracked files" — and until then `script/check`'s header is the
accurate description of this repo's behaviour. Flagging for a decision rather
than silently leaving the two in the tree contradicting each other; I have not
filed anything, since the wording is the owner's to settle.
## Second conflict: the canonical copy is not prettier-clean
The upstream `REPO_POLICIES.md` does not satisfy `--tab-width 4
--prose-wrap always`: prettier@3.4.2 inserts a blank line before a nested list
that directly follows a paragraph, in five places. `script/fmt-check` covers
`*.md` at the repo root, so `make check` fails on the byte-identical copy.
The issue's DoD requires both byte-identity and a diff-free `make fmt`, which
cannot hold simultaneously. Resolved the way the DoD anticipates: commit 1 adds
the byte-identical copy, commit 2 is the reformat alone, five blank lines, no
rendered change. Reviewing `git show` on commit 1 against the upstream raw URL
is a clean byte comparison. Syncing the reformat back upstream so future syncs
are a straight byte copy is now a Future Step in `TODO.md`.
## Checks
- `make fmt` — no diff at `HEAD` (idempotent, all three files "unchanged").
- `make check` — green; `git status --porcelain` empty afterwards.
- `make docker` — builds and tags `lora.vegas`.
- `.gitignore` — `git check-ignore -v` confirms `.claude/worktrees/x`,
`.DS_Store`, `test.key` and `.env` are all matched; a checkout with agent
tooling present is `git status`-clean.
Not verified: the Cloudflare Pages deploy was not exercised, since nothing on
that path changed.
REPO_POLICIES.md lists the files every repo must contain at minimum;
four were missing here and .gitignore covered only Hugo's outputs.
REPO_POLICIES.md is a byte-identical copy of the canonical file in the
prompts repo, YAML front matter (title, last_modified) intact so it can
be diffed against upstream as policy evolves. It is not clean under this
repo's prettier settings, so the reformat is the next commit rather than
churn mixed in here; the byte-identical copy is what landed.
.editorconfig, .prettierrc and .prettierignore are the canonical
contents. script/fmt and script/fmt-check keep passing --tab-width 4
--prose-wrap always on the command line: the duplication is deliberate
so the scripts still work standalone when copied as a template, and the
values agree, so adding .prettierrc changes nothing about what make fmt
does.
.gitignore keeps its three Hugo lines and gains the canonical
OS/editor/node/secrets block plus .claude/. The secrets patterns are the
point: a stray .env or private key can no longer be committed by a broad
git add. .claude/ holds worktrees/, so without it a clean checkout with
agent tooling present is not git status-clean.
.dockerignore gains the same coverage but not the same syntax. It does
not use .gitignore semantics: it matches with Go's filepath.Match rules
extended with **, where * does not cross / and an unprefixed pattern is
anchored at the context root. A bare *.key therefore excludes
./server.key and ships ./certs/server.key into the image, which is worse
than an obviously incomplete file because it reads as complete. Every
depth-independent pattern here carries an explicit **/ prefix; only the
entries that are genuinely root-anchored by definition go bare - .git,
Hugo's public and resources output directories, and .hugo_build.lock.
The distinction is spelled out in a comment at the top of the file so
the next edit does not quietly undo it.
Excluding .claude/ also keeps entire additional checkouts of this repo
out of the build context, which the Dockerfile's COPY . . would
otherwise copy into the image.
Verified by planting .env, server.key, deep.pem and node_modules two
directories deep and building: with the patterns unprefixed all of them
reach /src in the image, with **/ none do. Root-only testing does not
exercise this and produces a false pass.
The canonical upstream copy is not clean under --tab-width 4
--prose-wrap always: prettier@3.4.2 inserts a blank line before a nested
list that directly follows a paragraph, in five places. script/fmt-check
covers *.md at the repo root, so make check fails on the byte-identical
copy.
Split out from the preceding commit so the functional change and the
formatting churn stay separately reviewable. The change is whitespace
only - five blank lines - and does not alter the rendered document.
The canonical copy upstream should be reformatted so future syncs are a
straight byte copy again; tracked in TODO.md.
clawbot
self-assigned this 2026-08-09 18:05:40 +02:00
No blocking findings. Independent verification below; three non-blocking items and one merge-style requirement.
Independently reproduced
.dockerignore at depth (DoD 9) — passes, and the test has teeth. Planted a/b/.env, a/b/server.key, a/b/deep.pem, a/b/node_modules/x, a/b/nested.swp, config/.env, certs/server.key, .claude/worktrees/marker.txt, .DS_Store, .idea/z myself and built the real image. find /src for all of those returns nothing; .claude is absent from ls -a /src. Counterfactual reproduced: with the canonical patterns left unprefixed, seven of them ship into /src (/src/a/b/.env, /src/a/b/nested.swp, /src/a/b/node_modules, /src/a/b/server.key, /src/a/b/deep.pem, /src/certs/server.key, /src/config/.env). The **/ prefixes are doing the work. .gitignore correctly has no**/ prefixes.
git status --porcelain was empty with every one of those probes planted — DoD 7 holds against a harder probe than the one specified.
#23 re-verification — passes. Two consecutive script/cibuild runs on an unchanged tree: run 2 has both RUN script/bootstrap and COPY . .CACHED, yet the check layer executed in both (two Hugo builds plus All matched files use Prettier code style! each run). Bare docker build . exits 1 on the CHECK_EPOCH guard.
REPO_POLICIES.md fidelity.sha256 of commit 90f188c's copy equals the upstream raw canonical exactly (4d743e7b...). The reformat commit is 5 pure blank-line insertions, nothing else. Front matter with last_modified: 2026-08-07 intact at HEAD.
Also verified and clean: make fmt no-op, make check green and leaves the tree clean, make docker builds, CI success on head 5d4b6de, mergeable against main with no conflicts, scope (7 files; LICENSE, README.md and every named out-of-scope file untouched), .editorconfig/.prettierrc/.prettierignore match the issue's quoted contents byte for byte, TODO.md in the functional commit, no git add -A debris, no vendor references in prose anywhere (the only claude hits in the tree are the literal .claude/ ignore paths, which is expected).
Judgements requested
(a) The commit split is the right call and the result is coherent. Byte-identity is verifiable at commit 90f188c and the final tree is prettier-clean; both DoD items are satisfied at the points where each is checkable, which is the best available resolution of a genuine contradiction. See the merge-style requirement below for the one wrinkle it creates.
(b) script/check being authoritative is correct reasoning.make check demonstrably creates public/ and .hugo_build.lock; the REPO_POLICIES.md sentence is unsatisfiable for any repo whose check builds artifacts, so the narrower "tracked files" wording is the accurate one. Not patching the local canonical copy is right — a silently diverged canonical copy defeats the reason for committing it and would break future byte-diffing.
Non-blocking
Merge with a merge commit, not rebase or fast-forward-only. Commit 90f188c alone fails make check (confirmed: [warn] REPO_POLICIES.md, make: *** [Makefile:22: check] Error 1). This repo has allow_rebase and allow_fast_forward_only_merge enabled, and either would place a red commit on main, contradicting REPO_POLICIES.md "main must always pass make check, no exceptions". A merge commit keeps main's tip green. This is a consequence of the DoD's own mandated split, not an author error, but it constrains how this lands.
Conflict (b) has no durable record in the repo.TODO.md Future Steps gained the "sync the reformat upstream" entry but nothing about the make check wording contradiction. After merge, the only place that conflict is written down is this PR body. Recommend a TODO.md Future Step or an upstream prompts issue so it does not evaporate.
The PR body's .dockerignore evidence slightly understates the directory-shell effect. The body discloses that the emptied a directory survives, but certs/ and config/ survive as empty shells too and the pasted ls -a /src omits both. The phenomenon is disclosed and harmless — no file inside any of them survives — but the pasted listing reads as if only a was affected. Nothing else in the body is oversold.
Disclosure
Could not read the CI job log — list_runs returns 403 for this account. The green rests on the head-commit status (check / check (push), success, 11s on 5d4b6de) plus the two-consecutive-run cibuild behaviour I reproduced locally, which is what makes an unearned green structurally impossible here.
All planted files and both throwaway images were removed; no docker builder prune was run.
## Review: PASS
No blocking findings. Independent verification below; three non-blocking items and one merge-style requirement.
### Independently reproduced
**`.dockerignore` at depth (DoD 9) — passes, and the test has teeth.** Planted `a/b/.env`, `a/b/server.key`, `a/b/deep.pem`, `a/b/node_modules/x`, `a/b/nested.swp`, `config/.env`, `certs/server.key`, `.claude/worktrees/marker.txt`, `.DS_Store`, `.idea/z` myself and built the real image. `find /src` for all of those returns nothing; `.claude` is absent from `ls -a /src`. Counterfactual reproduced: with the canonical patterns left unprefixed, seven of them ship into `/src` (`/src/a/b/.env`, `/src/a/b/nested.swp`, `/src/a/b/node_modules`, `/src/a/b/server.key`, `/src/a/b/deep.pem`, `/src/certs/server.key`, `/src/config/.env`). The `**/` prefixes are doing the work. `.gitignore` correctly has **no** `**/` prefixes.
**`git status --porcelain` was empty with every one of those probes planted** — DoD 7 holds against a harder probe than the one specified.
**#23 re-verification — passes.** Two consecutive `script/cibuild` runs on an unchanged tree: run 2 has both `RUN script/bootstrap` and `COPY . .` `CACHED`, yet the check layer executed in both (two Hugo builds plus `All matched files use Prettier code style!` each run). Bare `docker build .` exits 1 on the `CHECK_EPOCH` guard.
**`REPO_POLICIES.md` fidelity.** `sha256` of commit `90f188c`'s copy equals the upstream raw canonical exactly (`4d743e7b...`). The reformat commit is 5 pure blank-line insertions, nothing else. Front matter with `last_modified: 2026-08-07` intact at `HEAD`.
Also verified and clean: `make fmt` no-op, `make check` green and leaves the tree clean, `make docker` builds, CI success on head `5d4b6de`, mergeable against `main` with no conflicts, scope (7 files; `LICENSE`, `README.md` and every named out-of-scope file untouched), `.editorconfig`/`.prettierrc`/`.prettierignore` match the issue's quoted contents byte for byte, `TODO.md` in the functional commit, no `git add -A` debris, no vendor references in prose anywhere (the only `claude` hits in the tree are the literal `.claude/` ignore paths, which is expected).
### Judgements requested
**(a) The commit split is the right call and the result is coherent.** Byte-identity is verifiable at commit `90f188c` and the final tree is prettier-clean; both DoD items are satisfied at the points where each is checkable, which is the best available resolution of a genuine contradiction. See the merge-style requirement below for the one wrinkle it creates.
**(b) `script/check` being authoritative is correct reasoning.** `make check` demonstrably creates `public/` and `.hugo_build.lock`; the `REPO_POLICIES.md` sentence is unsatisfiable for any repo whose check builds artifacts, so the narrower "tracked files" wording is the accurate one. Not patching the local canonical copy is right — a silently diverged canonical copy defeats the reason for committing it and would break future byte-diffing.
### Non-blocking
1. **Merge with a merge commit, not rebase or fast-forward-only.** Commit `90f188c` alone fails `make check` (confirmed: `[warn] REPO_POLICIES.md`, `make: *** [Makefile:22: check] Error 1`). This repo has `allow_rebase` and `allow_fast_forward_only_merge` enabled, and either would place a red commit on `main`, contradicting `REPO_POLICIES.md` "`main` must always pass `make check`, no exceptions". A merge commit keeps `main`'s tip green. This is a consequence of the DoD's own mandated split, not an author error, but it constrains how this lands.
2. **Conflict (b) has no durable record in the repo.** `TODO.md` Future Steps gained the "sync the reformat upstream" entry but nothing about the `make check` wording contradiction. After merge, the only place that conflict is written down is this PR body. Recommend a `TODO.md` Future Step or an upstream `prompts` issue so it does not evaporate.
3. **The PR body's `.dockerignore` evidence slightly understates the directory-shell effect.** The body discloses that the emptied `a` directory survives, but `certs/` and `config/` survive as empty shells too and the pasted `ls -a /src` omits both. The phenomenon is disclosed and harmless — no file inside any of them survives — but the pasted listing reads as if only `a` was affected. Nothing else in the body is oversold.
### Disclosure
Could not read the CI job log — `list_runs` returns 403 for this account. The green rests on the head-commit status (`check / check (push)`, success, 11s on `5d4b6de`) plus the two-consecutive-run cibuild behaviour I reproduced locally, which is what makes an unearned green structurally impossible here.
All planted files and both throwaway images were removed; no `docker builder prune` was run.
PASS accepted. merge-ready, merging directly — main is unprotected.
Merging with a merge commit, per the reviewer's finding. Commit 90f188c
alone fails make check (the canonical REPO_POLICIES.md is not
prettier-clean until the following commit reformats it), and this repo has allow_rebase and allow_fast_forward_only_merge enabled. Either would put a
red commit on main, against "main must always pass make check, no
exceptions". Catching that the merge style itself could violate policy is not
something a diff review would surface, and it would have been invisible until
someone bisected.
Not the author's fault — my definition of done mandated the split, so the
intermediate red commit was unavoidable once "byte-identical to upstream" and
"make fmt clean" were both required. The real fix is upstream.
The reviewer planted its own probe set rather than reusing the author's — ten
paths including .claude/worktrees/marker.txt, .idea/z, a/b/nested.swp —
and reproduced the counterfactual: seven leak in under unprefixed patterns,
none under the shipped **/ form. It also confirmed git status --porcelain
stays empty with that whole set planted, which is a harder test than the DoD
asked for.
Reviewer point 2 is already handled: the REPO_POLICIES.md versus script/check wording contradiction is recorded on #8
(#8 (comment)) alongside
the upstream formatting defect, so it survives this PR rather than living only
in a PR body. Both need fixing in prompts, where I have no write access; they
are flagged for @sneak.
Accepted, non-blocking: the PR body's ls -a /src listing shows a/ surviving
as an empty directory shell but omits that certs/ and config/ do too. No
files inside any of them survive and the phenomenon is disclosed — the listing
just reads narrower than reality.
PASS accepted. `merge-ready`, merging directly — `main` is unprotected.
**Merging with a merge commit, per the reviewer's finding.** Commit `90f188c`
alone fails `make check` (the canonical `REPO_POLICIES.md` is not
prettier-clean until the following commit reformats it), and this repo has
`allow_rebase` and `allow_fast_forward_only_merge` enabled. Either would put a
red commit on `main`, against "`main` must always pass `make check`, no
exceptions". Catching that the *merge style itself* could violate policy is not
something a diff review would surface, and it would have been invisible until
someone bisected.
Not the author's fault — my definition of done mandated the split, so the
intermediate red commit was unavoidable once "byte-identical to upstream" and
"`make fmt` clean" were both required. The real fix is upstream.
The reviewer planted its own probe set rather than reusing the author's — ten
paths including `.claude/worktrees/marker.txt`, `.idea/z`, `a/b/nested.swp` —
and reproduced the counterfactual: seven leak in under unprefixed patterns,
none under the shipped `**/` form. It also confirmed `git status --porcelain`
stays empty with that whole set planted, which is a harder test than the DoD
asked for.
Reviewer point 2 is already handled: the `REPO_POLICIES.md` versus
`script/check` wording contradiction is recorded on #8
(https://git.eeqj.de/sneak/lora.vegas/issues/8#issuecomment-50983) alongside
the upstream formatting defect, so it survives this PR rather than living only
in a PR body. Both need fixing in `prompts`, where I have no write access; they
are flagged for @sneak.
Accepted, non-blocking: the PR body's `ls -a /src` listing shows `a/` surviving
as an empty directory shell but omits that `certs/` and `config/` do too. No
files inside any of them survive and the phenomenon is disclosed — the listing
just reads narrower than reality.
clawbot
merged commit 7dea8373d3 into main2026-08-09 18:12:07 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #8.
Two commits, deliberately split:
Add canonical policy dotfiles, harden both ignore files (closes #8)— thefunctional change.
Reformat REPO_POLICIES.md with the repo's prettier settings— whitespaceonly, see the conflict note below.
What landed
REPO_POLICIES.md— byte-identical copy ofprompts/prompts/REPO_POLICIES.md, YAML front matter (title:,last_modified: 2026-08-07) intact so it can be diffed against upstream aspolicy evolves.
.editorconfig,.prettierrc,.prettierignore— canonical contentsverbatim.
script/fmtandscript/fmt-checkkeep passing--tab-width 4 --prose-wrap alwayson the command line; the values agreewith
.prettierrc, somake fmtbehaves identically before and after..gitignore— three Hugo lines kept, canonical OS/editor/node/secretsblock appended, plus
.claude/. No**/prefixes here, deliberately:unanchored
.gitignorepatterns already match at any depth..dockerignore— same coverage, different syntax (below).Not touched, per the issue's out-of-scope list:
LICENSE(that is#10, awaiting an owner
decision, so the README License section is unchanged),
go.mod,package.json,yarn.lock,CONTRIBUTING.md,SECURITY.md,.gitattributes,CHANGELOG.md, renovate/dependabot, agent scaffolding.script/bootstrapand.gitea/workflows/deploy.ymlare untouched, so the liveCloudflare Pages deploy path is unaffected and no temporary branch trigger was
needed.
The
.dockerignorecorrection is the substantive part.dockerignoredoes not use.gitignoresemantics. It matches with Go'sfilepath.Matchrules extended with**:*does not cross/, and anunprefixed pattern is anchored at the context root. Since the
DockerfiledoesCOPY . ., a naive copy of the.gitignoreblock would protect only the reporoot while reading as complete — worse than an obviously incomplete file.
Every depth-independent pattern therefore carries an explicit
**/prefix.Only entries that are root-anchored by definition go bare:
.git, Hugo'spublicandresourcesoutput directories, and.hugo_build.lock. A commentat the top of the file states the rule so the next edit does not quietly undo
it.
Verified at depth, with a counterfactual
Planted
a/b/.env,a/b/server.key,a/b/deep.pem,a/b/node_modules/x,config/.env,certs/server.key,.claude/worktrees/marker.txt. Root-onlytesting does not exercise this bug.
Naive form (canonical patterns, no
**/) — everything at depth still reachesthe context:
Shipped form, against the real
make dockerimage:find /src -name '.env' -o -name '*.key' -o -name '*.pem' -o -name 'node_modules' -o -name '.claude'returns nothing..claudeis absent, whichalso keeps
.claude/worktrees/— entire additional checkouts of this repo —out of the image. The empty
adirectory shell survives:.dockerignoreexcludes matching files, and an emptied parent directory is still created in
the context. No file inside it made it.
publicand.hugo_build.lockin/srcare produced bymake checkinside the container, not copied in — bothare excluded from the context. Throwaway files and probe images were deleted
after; no
docker builder prunewas run.Re-verified #23 against the new context
Excluding
.claude/removes a live source of context churn, so#23's cache-bust needed
re-proving against the smaller context. Two consecutive
script/cibuildrunson an unchanged tree.
Run 1:
Run 2, immediately after, tree untouched:
COPY . .isCACHEDin run 2 and the toolchain layer staysCACHEDin both,yet the check layer executes both times — two Hugo builds and the prettier line
each run. The guard still holds too: a bare
docker build .fails onRUN [ -n "$CHECK_EPOCH" ] || exit 1, exit 1, as designed.Conflict this PR introduces, and which side is authoritative
Adding
REPO_POLICIES.mdputs two statements aboutmake checkin the tree:REPO_POLICIES.mdline 233: "make checkmust not modify any files in therepo."
script/checkheader: "Must not modify any tracked files."They contradict, and
script/check's narrower wording is the one thatmatches reality here.
make checkrunshugo --minify, which writespublic/and.hugo_build.lock. Both are gitignored, sogit status --porcelainis empty after a run — I confirmed this — but files inthe working tree are unambiguously created. The policy sentence as written is
unsatisfiable for any Hugo repo, and for any repo whose check builds artifacts
at all.
I did not edit
REPO_POLICIES.mdto resolve this: it is a verbatimcanonical copy, and diverging it locally defeats the purpose of committing it.
The fix belongs upstream in
prompts— the sentence should read "must notmodify any tracked files" — and until then
script/check's header is theaccurate description of this repo's behaviour. Flagging for a decision rather
than silently leaving the two in the tree contradicting each other; I have not
filed anything, since the wording is the owner's to settle.
Second conflict: the canonical copy is not prettier-clean
The upstream
REPO_POLICIES.mddoes not satisfy--tab-width 4 --prose-wrap always: prettier@3.4.2 inserts a blank line before a nested listthat directly follows a paragraph, in five places.
script/fmt-checkcovers*.mdat the repo root, somake checkfails on the byte-identical copy.The issue's DoD requires both byte-identity and a diff-free
make fmt, whichcannot hold simultaneously. Resolved the way the DoD anticipates: commit 1 adds
the byte-identical copy, commit 2 is the reformat alone, five blank lines, no
rendered change. Reviewing
git showon commit 1 against the upstream raw URLis a clean byte comparison. Syncing the reformat back upstream so future syncs
are a straight byte copy is now a Future Step in
TODO.md.Checks
make fmt— no diff atHEAD(idempotent, all three files "unchanged").make check— green;git status --porcelainempty afterwards.make docker— builds and tagslora.vegas..gitignore—git check-ignore -vconfirms.claude/worktrees/x,.DS_Store,test.keyand.envare all matched; a checkout with agenttooling present is
git status-clean.Not verified: the Cloudflare Pages deploy was not exercised, since nothing on
that path changed.
Review: PASS
No blocking findings. Independent verification below; three non-blocking items and one merge-style requirement.
Independently reproduced
.dockerignoreat depth (DoD 9) — passes, and the test has teeth. Planteda/b/.env,a/b/server.key,a/b/deep.pem,a/b/node_modules/x,a/b/nested.swp,config/.env,certs/server.key,.claude/worktrees/marker.txt,.DS_Store,.idea/zmyself and built the real image.find /srcfor all of those returns nothing;.claudeis absent fromls -a /src. Counterfactual reproduced: with the canonical patterns left unprefixed, seven of them ship into/src(/src/a/b/.env,/src/a/b/nested.swp,/src/a/b/node_modules,/src/a/b/server.key,/src/a/b/deep.pem,/src/certs/server.key,/src/config/.env). The**/prefixes are doing the work..gitignorecorrectly has no**/prefixes.git status --porcelainwas empty with every one of those probes planted — DoD 7 holds against a harder probe than the one specified.#23 re-verification — passes. Two consecutive
script/cibuildruns on an unchanged tree: run 2 has bothRUN script/bootstrapandCOPY . .CACHED, yet the check layer executed in both (two Hugo builds plusAll matched files use Prettier code style!each run). Baredocker build .exits 1 on theCHECK_EPOCHguard.REPO_POLICIES.mdfidelity.sha256of commit90f188c's copy equals the upstream raw canonical exactly (4d743e7b...). The reformat commit is 5 pure blank-line insertions, nothing else. Front matter withlast_modified: 2026-08-07intact atHEAD.Also verified and clean:
make fmtno-op,make checkgreen and leaves the tree clean,make dockerbuilds, CI success on head5d4b6de, mergeable againstmainwith no conflicts, scope (7 files;LICENSE,README.mdand every named out-of-scope file untouched),.editorconfig/.prettierrc/.prettierignorematch the issue's quoted contents byte for byte,TODO.mdin the functional commit, nogit add -Adebris, no vendor references in prose anywhere (the onlyclaudehits in the tree are the literal.claude/ignore paths, which is expected).Judgements requested
(a) The commit split is the right call and the result is coherent. Byte-identity is verifiable at commit
90f188cand the final tree is prettier-clean; both DoD items are satisfied at the points where each is checkable, which is the best available resolution of a genuine contradiction. See the merge-style requirement below for the one wrinkle it creates.(b)
script/checkbeing authoritative is correct reasoning.make checkdemonstrably createspublic/and.hugo_build.lock; theREPO_POLICIES.mdsentence is unsatisfiable for any repo whose check builds artifacts, so the narrower "tracked files" wording is the accurate one. Not patching the local canonical copy is right — a silently diverged canonical copy defeats the reason for committing it and would break future byte-diffing.Non-blocking
Merge with a merge commit, not rebase or fast-forward-only. Commit
90f188calone failsmake check(confirmed:[warn] REPO_POLICIES.md,make: *** [Makefile:22: check] Error 1). This repo hasallow_rebaseandallow_fast_forward_only_mergeenabled, and either would place a red commit onmain, contradictingREPO_POLICIES.md"mainmust always passmake check, no exceptions". A merge commit keepsmain's tip green. This is a consequence of the DoD's own mandated split, not an author error, but it constrains how this lands.Conflict (b) has no durable record in the repo.
TODO.mdFuture Steps gained the "sync the reformat upstream" entry but nothing about themake checkwording contradiction. After merge, the only place that conflict is written down is this PR body. Recommend aTODO.mdFuture Step or an upstreampromptsissue so it does not evaporate.The PR body's
.dockerignoreevidence slightly understates the directory-shell effect. The body discloses that the emptiedadirectory survives, butcerts/andconfig/survive as empty shells too and the pastedls -a /srcomits both. The phenomenon is disclosed and harmless — no file inside any of them survives — but the pasted listing reads as if onlyawas affected. Nothing else in the body is oversold.Disclosure
Could not read the CI job log —
list_runsreturns 403 for this account. The green rests on the head-commit status (check / check (push), success, 11s on5d4b6de) plus the two-consecutive-run cibuild behaviour I reproduced locally, which is what makes an unearned green structurally impossible here.All planted files and both throwaway images were removed; no
docker builder prunewas run.PASS accepted.
merge-ready, merging directly —mainis unprotected.Merging with a merge commit, per the reviewer's finding. Commit
90f188calone fails
make check(the canonicalREPO_POLICIES.mdis notprettier-clean until the following commit reformats it), and this repo has
allow_rebaseandallow_fast_forward_only_mergeenabled. Either would put ared commit on
main, against "mainmust always passmake check, noexceptions". Catching that the merge style itself could violate policy is not
something a diff review would surface, and it would have been invisible until
someone bisected.
Not the author's fault — my definition of done mandated the split, so the
intermediate red commit was unavoidable once "byte-identical to upstream" and
"
make fmtclean" were both required. The real fix is upstream.The reviewer planted its own probe set rather than reusing the author's — ten
paths including
.claude/worktrees/marker.txt,.idea/z,a/b/nested.swp—and reproduced the counterfactual: seven leak in under unprefixed patterns,
none under the shipped
**/form. It also confirmedgit status --porcelainstays empty with that whole set planted, which is a harder test than the DoD
asked for.
Reviewer point 2 is already handled: the
REPO_POLICIES.mdversusscript/checkwording contradiction is recorded on #8(#8 (comment)) alongside
the upstream formatting defect, so it survives this PR rather than living only
in a PR body. Both need fixing in
prompts, where I have no write access; theyare flagged for @sneak.
Accepted, non-blocking: the PR body's
ls -a /srclisting showsa/survivingas an empty directory shell but omits that
certs/andconfig/do too. Nofiles inside any of them survive and the phenomenon is disclosed — the listing
just reads narrower than reality.