REPO_POLICIES.md lists the files every repo must contain at minimum. This
repo is missing four of them, and its .gitignore is missing the mandated
content. All five are verbatim drops of canonical files from the prompts
repo, so this is one cohesive commit.
Missing: REPO_POLICIES.md
Required in the minimum file list. Fetch from https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md.
It must retain its YAML front matter (title: and last_modified: YYYY-MM-DD) so it can be kept in sync as policies evolve.
Missing: .editorconfig
"All repos should have an .editorconfig enforcing the project's indentation
settings." Canonical contents:
"Documentation and writing repos (Markdown, HTML, CSS) should also have .prettierrc and .prettierignore." A Hugo site is exactly that. Canonical
contents:
.prettierrc:
{"tabWidth":4,"proseWrap":"always"}
.prettierignore:
node_modules/
yarn.lock
Note that script/fmt and script/fmt-check already pass --tab-width 4 --prose-wrap always on the command line. Keep those flags — the canonical
scripts duplicate them deliberately so the scripts work standalone when copied
as a template. Adding .prettierrc must not change what make fmt does.
Incomplete: .gitignore
Current contents are Hugo outputs only (/public/, /resources/, .hugo_build.lock). Policy: ".gitignore should be comprehensive from the
start: OS files (.DS_Store), editor files (.swp, *~), language build
artifacts, and node_modules/", and separately "Never commit secrets. .env
files, credentials, API keys, and private keys must be in .gitignore. No
exceptions."
Append the canonical block, keeping the existing Hugo lines:
REPO_POLICIES.md exists at the repo root, is byte-identical to the
upstream canonical copy, and head -5 REPO_POLICIES.md contains last_modified:.
.editorconfig, .prettierrc, .prettierignore exist at the repo root
with exactly the contents quoted above.
.gitignore retains its three existing Hugo lines and gains the canonical
block above; it contains the literal patterns .env, *.key, and *.pem.
make fmt produces no diff after the change (adding .prettierrc must be a
no-op against the flags already passed by script/fmt). If it does produce
a diff, commit the reformat as its own separate commit — formatting churn
must not be mixed with functional changes.
make check passes and script/cibuild succeeds.
TODO.md is updated in the same commit as the work, per the Workflow block.
Explicitly out of scope — no Hugo equivalent, do not add
Each of the following is either language-specific or not required by any org
standard. Do not create them:
go.mod, go.sum, .golangci.yml — Go module and Go linter files; there is
no Go code here.
package.json, yarn.lock — only required for JS repos. script/fmt gets
prettier via npx --yes prettier@<pinned>, so there is no dependency
manifest to maintain. Adding one would pull in the whole node/nvm/corepack
pinning apparatus for a single formatter.
pyproject.toml — wrong language.
CONTRIBUTING.md, SECURITY.md, .gitattributes, CHANGELOG.md, AGENTS.md, root CLAUDE.md, renovate/dependabot config, issue/PR
templates, CODEOWNERS — none of these appear in any org standard. They
were checked for explicitly and are not mandated.
.claude/ scaffolding — the memory policy states "Repos with no memories yet
need no .claude/ scaffolding." This repo has none.
Per-file license headers / SPDX identifiers — not mandated anywhere.
## Problem
`REPO_POLICIES.md` lists the files every repo must contain at minimum. This
repo is missing four of them, and its `.gitignore` is missing the mandated
content. All five are verbatim drops of canonical files from the `prompts`
repo, so this is one cohesive commit.
### Missing: `REPO_POLICIES.md`
Required in the minimum file list. Fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md`.
It must retain its YAML front matter (`title:` and
`last_modified: YYYY-MM-DD`) so it can be kept in sync as policies evolve.
### Missing: `.editorconfig`
"All repos should have an `.editorconfig` enforcing the project's indentation
settings." Canonical contents:
```
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[Makefile]
indent_style = tab
```
### Missing: `.prettierrc` and `.prettierignore`
"Documentation and writing repos (Markdown, HTML, CSS) should also have
`.prettierrc` and `.prettierignore`." A Hugo site is exactly that. Canonical
contents:
`.prettierrc`:
```json
{
"tabWidth": 4,
"proseWrap": "always"
}
```
`.prettierignore`:
```
node_modules/
yarn.lock
```
Note that `script/fmt` and `script/fmt-check` already pass `--tab-width 4
--prose-wrap always` on the command line. Keep those flags — the canonical
scripts duplicate them deliberately so the scripts work standalone when copied
as a template. Adding `.prettierrc` must not change what `make fmt` does.
### Incomplete: `.gitignore`
Current contents are Hugo outputs only (`/public/`, `/resources/`,
`.hugo_build.lock`). Policy: "`.gitignore` should be comprehensive from the
start: OS files (`.DS_Store`), editor files (`.swp`, `*~`), language build
artifacts, and `node_modules/`", and separately "Never commit secrets. `.env`
files, credentials, API keys, and private keys must be in `.gitignore`. No
exceptions."
Append the canonical block, keeping the existing Hugo lines:
```
# OS
.DS_Store
Thumbs.db
# Editors
*.swp
*.swo
*~
*.bak
.idea/
.vscode/
*.sublime-*
# Node
node_modules/
# Environment / secrets
.env
.env.*
*.pem
*.key
```
## Definition of done
1. `REPO_POLICIES.md` exists at the repo root, is byte-identical to the
upstream canonical copy, and `head -5 REPO_POLICIES.md` contains
`last_modified:`.
2. `.editorconfig`, `.prettierrc`, `.prettierignore` exist at the repo root
with exactly the contents quoted above.
3. `.gitignore` retains its three existing Hugo lines and gains the canonical
block above; it contains the literal patterns `.env`, `*.key`, and `*.pem`.
4. `make fmt` produces no diff after the change (adding `.prettierrc` must be a
no-op against the flags already passed by `script/fmt`). If it does produce
a diff, commit the reformat as its own separate commit — formatting churn
must not be mixed with functional changes.
5. `make check` passes and `script/cibuild` succeeds.
6. `TODO.md` is updated in the same commit as the work, per the Workflow block.
## Explicitly out of scope — no Hugo equivalent, do not add
Each of the following is either language-specific or not required by any org
standard. Do not create them:
- `go.mod`, `go.sum`, `.golangci.yml` — Go module and Go linter files; there is
no Go code here.
- `package.json`, `yarn.lock` — only required for JS repos. `script/fmt` gets
prettier via `npx --yes prettier@<pinned>`, so there is no dependency
manifest to maintain. Adding one would pull in the whole node/nvm/corepack
pinning apparatus for a single formatter.
- `pyproject.toml` — wrong language.
- `CONTRIBUTING.md`, `SECURITY.md`, `.gitattributes`, `CHANGELOG.md`,
`AGENTS.md`, root `CLAUDE.md`, renovate/dependabot config, issue/PR
templates, `CODEOWNERS` — **none of these appear in any org standard.** They
were checked for explicitly and are not mandated.
- `.claude/` scaffolding — the memory policy states "Repos with no memories yet
need no `.claude/` scaffolding." This repo has none.
- Per-file license headers / SPDX identifiers — not mandated anywhere.
Ref: `REPO_POLICIES.md` minimum file list; `EXISTING_REPO_CHECKLIST.md`.
Additional scope for this issue, found while testing the script/cibuild cache
behaviour for #23.
git status --porcelain in a clean checkout reports:
?? .claude/
.claude/ is agent tooling scaffolding (it holds worktrees/ among other
things). It is in neither .gitignore nor .dockerignore, which has two
consequences:
It can be committed by accident. Anything doing a broad git add picks
it up. REPO_POLICIES.md forbids git add -A/git add . precisely to
limit this class of mistake, but the ignore file is the belt to that
braces.
It is in the Docker build context. The Dockerfile does COPY . . and .dockerignore currently lists only .git, public, resources, and .hugo_build.lock. So .claude/ — including worktrees/, which can hold
entire additional checkouts of this repo — gets copied into the image. That
inflates the context and the image, slows every build, and in the worst case
copies unrelated in-flight work into a build artifact.
Add .claude/ to both files as part of this issue:
.gitignore — alongside the canonical block already specified above.
.dockerignore — which should also gain the editor/OS patterns for the same
reason; there is no value in shipping .DS_Store or .idea/ into the build
context either.
Updated definition-of-done items for this issue:
.gitignore contains .claude/, and git status --porcelain is empty in a
clean checkout with agent tooling present.
.dockerignore contains .claude/ and the OS/editor patterns. Verify by
building and confirming the context size drops (or at minimum that .claude/ is absent from the image — docker run --rm <image> ls -a /src
should not list it).
Note the ordering interaction with #23: until #23 lands, a script/cibuild run
may be served entirely from cache, so a build used to verify item 8 must be run
with the cache defeated or it will prove nothing.
Additional scope for this issue, found while testing the `script/cibuild` cache
behaviour for #23.
`git status --porcelain` in a clean checkout reports:
```
?? .claude/
```
`.claude/` is agent tooling scaffolding (it holds `worktrees/` among other
things). It is in **neither `.gitignore` nor `.dockerignore`**, which has two
consequences:
1. **It can be committed by accident.** Anything doing a broad `git add` picks
it up. `REPO_POLICIES.md` forbids `git add -A`/`git add .` precisely to
limit this class of mistake, but the ignore file is the belt to that
braces.
2. **It is in the Docker build context.** The `Dockerfile` does `COPY . .` and
`.dockerignore` currently lists only `.git`, `public`, `resources`, and
`.hugo_build.lock`. So `.claude/` — including `worktrees/`, which can hold
entire additional checkouts of this repo — gets copied into the image. That
inflates the context and the image, slows every build, and in the worst case
copies unrelated in-flight work into a build artifact.
Add `.claude/` to both files as part of this issue:
- `.gitignore` — alongside the canonical block already specified above.
- `.dockerignore` — which should also gain the editor/OS patterns for the same
reason; there is no value in shipping `.DS_Store` or `.idea/` into the build
context either.
Updated definition-of-done items for this issue:
7. `.gitignore` contains `.claude/`, and `git status --porcelain` is empty in a
clean checkout with agent tooling present.
8. `.dockerignore` contains `.claude/` and the OS/editor patterns. Verify by
building and confirming the context size drops (or at minimum that
`.claude/` is absent from the image — `docker run --rm <image> ls -a /src`
should not list it).
Note the ordering interaction with #23: until #23 lands, a `script/cibuild` run
may be served entirely from cache, so a build used to verify item 8 must be run
with the cache defeated or it will prove nothing.
Blocked on #23 — do not land the .dockerignore portion of this issue
first.
Adding .claude/ to .dockerignore removes a live source of build-context
churn. .claude/worktrees/ is created and destroyed constantly by tooling, and
that churn intermittently invalidates COPY . ., forcing the RUN make check
layer to actually execute. Excluding it makes Docker's cache stickier and more
consistent — correct in itself, but it removes an accidental safety net while #23's unearned-green hole is still open. The gate would get quietly weaker,
with nothing in the build output to signal the change.
Required sequencing:
#23 lands first (cache-busting so make check cannot be served from cache).
Then this issue's .dockerignore change.
Then re-verify #23's two-consecutive-runs check, because this change alters
the cache behaviour that #23 was validated against.
The rest of this issue — REPO_POLICIES.md, .editorconfig, .prettierrc, .prettierignore, and the .gitignore additions including .claude/ — has no
such dependency and can proceed at any time. If it is convenient to start
this issue before #23 is done, split it: land everything except the .dockerignore edit, and leave that for a follow-up commit after #23. Adding .claude/ to .gitignore is unaffected; only the Docker build context matters
here.
For the record, the fleet-wide version of this warning describes repos with no.dockerignore at all being accidentally protected because .git churns
in the context. That is not this repo — lora.vegas already excludes .git,
which is why the cache hole reproduces here cleanly. The dependency above is
real but arrives via .claude/, not .git.
**Blocked on #23 — do not land the `.dockerignore` portion of this issue
first.**
Adding `.claude/` to `.dockerignore` removes a live source of build-context
churn. `.claude/worktrees/` is created and destroyed constantly by tooling, and
that churn intermittently invalidates `COPY . .`, forcing the `RUN make check`
layer to actually execute. Excluding it makes Docker's cache stickier and more
consistent — correct in itself, but it removes an accidental safety net while
#23's unearned-green hole is still open. The gate would get quietly weaker,
with nothing in the build output to signal the change.
Required sequencing:
1. #23 lands first (cache-busting so `make check` cannot be served from cache).
2. Then this issue's `.dockerignore` change.
3. Then re-verify #23's two-consecutive-runs check, because this change alters
the cache behaviour that #23 was validated against.
The rest of this issue — `REPO_POLICIES.md`, `.editorconfig`, `.prettierrc`,
`.prettierignore`, and the `.gitignore` additions including `.claude/` — has no
such dependency and can proceed at any time. **If it is convenient to start
this issue before #23 is done, split it: land everything except the
`.dockerignore` edit, and leave that for a follow-up commit after #23.** Adding
`.claude/` to `.gitignore` is unaffected; only the Docker build context matters
here.
For the record, the fleet-wide version of this warning describes repos with
**no** `.dockerignore` at all being accidentally protected because `.git` churns
in the context. That is not this repo — lora.vegas already excludes `.git`,
which is why the cache hole reproduces here cleanly. The dependency above is
real but arrives via `.claude/`, not `.git`.
Correction to the .dockerignore work — pattern semantics. This is a
security point, not a tidiness one.
.dockerignore does not use .gitignore semantics. It matches with Go's filepath.Match rules, extended with **. Two consequences that break the
naive approach:
* does not cross /. So *.key matches server.key at the context
root and does not match certs/server.key.
Unprefixed patterns are anchored at the context root. node_modules/
excludes only a top-level node_modules, not a nested one.
Another repo on the roster proved this empirically by planting files at depth
and watching config/.env, certs/server.key, and nested node_modules all
reach the build context despite patterns that appeared to cover them.
For this repo that matters directly, because the Dockerfile does COPY . .
and the whole point of the secrets patterns is that a stray key or .env must
never be baked into an image. A .dockerignore that silently protects only the
root is worse than an obviously incomplete one, because it reads as complete.
Requirement: every depth-independent pattern in .dockerignore gets a **/ prefix. So **/.env, **/*.key, **/*.pem, **/node_modules, **/.DS_Store, **/.claude, and so on. Patterns that genuinely are
root-anchored — public, resources — stay unprefixed, deliberately, since
those are specifically the Hugo output directories at the repo root.
Note this applies only to .dockerignore. .gitignore has different
semantics where an unanchored pattern already matches at any depth, so the
canonical .gitignore block quoted in the issue body is correct as written and
must not be given **/ prefixes.
Added definition-of-done item:
Verify the .dockerignore patterns by planting throwaway files at least two
directories deep — e.g. a/b/.env, a/b/server.key, a/b/node_modules/x
— then confirming they are absent from the built image (docker run --rm <image> find /src -name '.env' -o -name '*.key' returns nothing). Delete
the throwaway files afterwards. Testing only at the repo root does not
exercise the bug and will produce a false pass.
Combined with the earlier ordering note, the .dockerignore portion of this
issue now has two constraints: it lands after#23, and it must use **/
prefixes verified at depth. The rest of this issue is unaffected and can
proceed independently.
**Correction to the `.dockerignore` work — pattern semantics. This is a
security point, not a tidiness one.**
`.dockerignore` does **not** use `.gitignore` semantics. It matches with Go's
`filepath.Match` rules, extended with `**`. Two consequences that break the
naive approach:
- `*` does **not** cross `/`. So `*.key` matches `server.key` at the context
root and does **not** match `certs/server.key`.
- Unprefixed patterns are **anchored at the context root**. `node_modules/`
excludes only a top-level `node_modules`, not a nested one.
Another repo on the roster proved this empirically by planting files at depth
and watching `config/.env`, `certs/server.key`, and nested `node_modules` all
reach the build context despite patterns that appeared to cover them.
For this repo that matters directly, because the `Dockerfile` does `COPY . .`
and the whole point of the secrets patterns is that a stray key or `.env` must
never be baked into an image. A `.dockerignore` that silently protects only the
root is worse than an obviously incomplete one, because it reads as complete.
**Requirement:** every depth-independent pattern in `.dockerignore` gets a
`**/` prefix. So `**/.env`, `**/*.key`, `**/*.pem`, `**/node_modules`,
`**/.DS_Store`, `**/.claude`, and so on. Patterns that genuinely are
root-anchored — `public`, `resources` — stay unprefixed, deliberately, since
those are specifically the Hugo output directories at the repo root.
Note this applies **only to `.dockerignore`**. `.gitignore` has different
semantics where an unanchored pattern already matches at any depth, so the
canonical `.gitignore` block quoted in the issue body is correct as written and
must **not** be given `**/` prefixes.
**Added definition-of-done item:**
9. Verify the `.dockerignore` patterns by planting throwaway files at least two
directories deep — e.g. `a/b/.env`, `a/b/server.key`, `a/b/node_modules/x`
— then confirming they are absent from the built image (`docker run --rm
<image> find /src -name '.env' -o -name '*.key'` returns nothing). Delete
the throwaway files afterwards. Testing only at the repo root does not
exercise the bug and will produce a false pass.
Tracked upstream as `prompts` #29.
Combined with the earlier ordering note, the `.dockerignore` portion of this
issue now has two constraints: it lands **after** #23, and it must use `**/`
prefixes verified at depth. The rest of this issue is unaffected and can
proceed independently.
Branching from current main (ccdedc3), which already contains #23 / PR #30, so the ordering
constraint from the second comment is satisfied and the .dockerignore change
can land in this same commit rather than a follow-up.
One commit, ... (closes #8):
REPO_POLICIES.md — curl the raw canonical from prompts verbatim (byte
copy, not a reflow), front matter intact. Note it lands inside script/fmt-check's *.md glob, so it must already satisfy --tab-width 4 --prose-wrap always; if it does not, item 4 of the DoD and
"byte-identical" collide and I will report that rather than reformat it
silently.
.editorconfig, .prettierrc, .prettierignore — exactly the contents
quoted in the body.
.gitignore — keep the three Hugo lines, append the canonical block
verbatim, plus a .claude/ entry. No **/ prefixes here — unanchored .gitignore patterns already match at any depth.
.dockerignore — every depth-independent pattern gets **/
(**/.env, **/.env.*, **/*.key, **/*.pem, **/node_modules, **/.DS_Store, **/.claude, editor patterns, ...). public and resources stay unprefixed: they are the root Hugo output dirs by
intention. .git and .hugo_build.lock keep their current form.
Not touched, per the out-of-scope list: LICENSE (that is #10, still awaiting an owner
decision — the README License section stays as-is), go.mod, package.json, yarn.lock, CONTRIBUTING.md, SECURITY.md, .gitattributes, CHANGELOG.md, AGENTS.md, root agent-memory file, renovate/dependabot, .claude/ scaffolding.
Verification
Depth-planted .dockerignore proof (DoD 9): plant a/b/.env, a/b/server.key, a/b/node_modules/x and a .claude/ marker, build, then docker run --rm <image> find /src -name '.env' -o -name '*.key' and expect
empty. Root-only testing does not exercise the bug. Throwaways deleted
after, and I will also confirm the pre-change .dockerignorefails this
same probe, so the test is shown to have teeth.
Re-run #23's proof against the new context (third comment's step 3): two
consecutive script/cibuild runs on an unchanged tree, both pasted, both
showing the two Hugo builds plus the prettier line, with RUN script/bootstrap
still CACHED in the second.
make fmt diff-free, make check green, make docker still builds.
No docker builder prune in any form — shared host. Cache defeat is scoped
to --no-cache-filter / a throwaway tag.
TODO.md updated in the same commit. script/bootstrap and deploy.yml are
untouched, so the live deploy path is unaffected and no temporary branch
trigger is needed.
I will also reconcile a wording conflict this introduces: REPO_POLICIES.md
says make check "must not modify any files in the repo", while script/check's header says "must not modify any tracked files". Both will
be present in the tree; I will call out which is authoritative in the PR rather
than leave them contradicting.
## Implementation plan
Branching from current `main` (`ccdedc3`), which already contains #23 /
[PR #30](https://git.eeqj.de/sneak/lora.vegas/pulls/30), so the ordering
constraint from the second comment is satisfied and the `.dockerignore` change
can land in this same commit rather than a follow-up.
One commit, `... (closes #8)`:
1. `REPO_POLICIES.md` — `curl` the raw canonical from `prompts` verbatim (byte
copy, not a reflow), front matter intact. Note it lands inside
`script/fmt-check`'s `*.md` glob, so it must already satisfy
`--tab-width 4 --prose-wrap always`; if it does not, item 4 of the DoD and
"byte-identical" collide and I will report that rather than reformat it
silently.
2. `.editorconfig`, `.prettierrc`, `.prettierignore` — exactly the contents
quoted in the body.
3. `.gitignore` — keep the three Hugo lines, append the canonical block
verbatim, plus a `.claude/` entry. **No `**/` prefixes here** — unanchored
`.gitignore` patterns already match at any depth.
4. `.dockerignore` — every depth-independent pattern gets `**/`
(`**/.env`, `**/.env.*`, `**/*.key`, `**/*.pem`, `**/node_modules`,
`**/.DS_Store`, `**/.claude`, editor patterns, ...). `public` and
`resources` stay unprefixed: they are the root Hugo output dirs by
intention. `.git` and `.hugo_build.lock` keep their current form.
Not touched, per the out-of-scope list: `LICENSE` (that is
[#10](https://git.eeqj.de/sneak/lora.vegas/issues/10), still awaiting an owner
decision — the README License section stays as-is), `go.mod`, `package.json`,
`yarn.lock`, `CONTRIBUTING.md`, `SECURITY.md`, `.gitattributes`,
`CHANGELOG.md`, `AGENTS.md`, root agent-memory file, renovate/dependabot,
`.claude/` scaffolding.
### Verification
- **Depth-planted `.dockerignore` proof** (DoD 9): plant `a/b/.env`,
`a/b/server.key`, `a/b/node_modules/x` and a `.claude/` marker, build, then
`docker run --rm <image> find /src -name '.env' -o -name '*.key'` and expect
empty. Root-only testing does not exercise the bug. Throwaways deleted
after, and I will also confirm the pre-change `.dockerignore` *fails* this
same probe, so the test is shown to have teeth.
- **Re-run #23's proof against the new context** (third comment's step 3): two
consecutive `script/cibuild` runs on an unchanged tree, both pasted, both
showing the two Hugo builds plus the prettier line, with `RUN script/bootstrap`
still `CACHED` in the second.
- `make fmt` diff-free, `make check` green, `make docker` still builds.
- No `docker builder prune` in any form — shared host. Cache defeat is scoped
to `--no-cache-filter` / a throwaway tag.
`TODO.md` updated in the same commit. `script/bootstrap` and `deploy.yml` are
untouched, so the live deploy path is unaffected and no temporary branch
trigger is needed.
I will also reconcile a wording conflict this introduces: `REPO_POLICIES.md`
says `make check` "must not modify any files in the repo", while
`script/check`'s header says "must not modify any **tracked** files". Both will
be present in the tree; I will call out which is authoritative in the PR rather
than leave them contradicting.
Done in PR #31 (branch policy-dotfiles), two commits off ccdedc3.
What was built:REPO_POLICIES.md (byte-identical canonical copy, front
matter intact), .editorconfig, .prettierrc, .prettierignore (canonical
contents), .gitignore extended with the canonical block plus .claude/, and .dockerignore rewritten with **/-prefixed depth-independent patterns. LICENSE and the README License section untouched, per the out-of-scope list
and #10. script/bootstrap
and deploy.yml untouched, so the live deploy path is unaffected.
DoD 9 (depth-planted .dockerignore): passes, with a counterfactual so the
test is shown to have teeth. 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. With the canonical patterns left unprefixed,
all six depth-planted paths reach /src in the image:
With the shipped **/ form, docker run --rm lora.vegas find /src -name '.env' -o -name '*.key' -o -name '*.pem' -o -name 'node_modules' -o -name '.claude'
returns nothing, and ls -a /src does not list .claude. The emptied a
directory shell still appears — .dockerignore excludes matching files, and a
directory left empty is still created in the context — but nothing inside it
survived. Throwaways and probe images deleted; no docker builder prune was
run at any point.
Comment 3's step 3 (#23
re-verification): passes against the new, smaller context. Two consecutive script/cibuild runs on an unchanged tree; run 2 has COPY . .CACHED and RUN script/bootstrapCACHED, yet the check layer still executes — two Hugo
builds and All matched files use Prettier code style! in both runs. Full
output is pasted in the PR. A bare docker build . still fails on the CHECK_EPOCH guard.
Two DoD conflicts surfaced, neither resolved silently:
The canonical REPO_POLICIES.md is not clean under this repo's prettier
settings — prettier@3.4.2 inserts a blank line before a nested list that
follows a paragraph, in five places — so DoD 1 (byte-identical) and DoD 4
(make fmt diff-free) cannot both hold. Resolved as DoD 4 anticipates:
commit 1 adds the byte-identical copy, commit 2 is the five-blank-line
reformat on its own. Syncing that back upstream is now a Future Step.
REPO_POLICIES.md says make check "must not modify any files in the
repo"; script/check's header says "must not modify any tracked files". make check writes public/ and .hugo_build.lock, both gitignored, so git status --porcelain is empty but files are created. script/check's
wording is the accurate one; the policy sentence is unsatisfiable for any
repo whose check builds artifacts. Called out in the PR for a decision
rather than patched locally, since the canonical copy should not diverge.
make fmt is a no-op at HEAD, make check is green and leaves the tree
clean, make docker builds. TODO.md updated in the functional commit.
Done in [PR #31](https://git.eeqj.de/sneak/lora.vegas/pulls/31) (branch
`policy-dotfiles`), two commits off `ccdedc3`.
**What was built:** `REPO_POLICIES.md` (byte-identical canonical copy, front
matter intact), `.editorconfig`, `.prettierrc`, `.prettierignore` (canonical
contents), `.gitignore` extended with the canonical block plus `.claude/`, and
`.dockerignore` rewritten with `**/`-prefixed depth-independent patterns.
`LICENSE` and the README License section untouched, per the out-of-scope list
and [#10](https://git.eeqj.de/sneak/lora.vegas/issues/10). `script/bootstrap`
and `deploy.yml` untouched, so the live deploy path is unaffected.
**DoD 9 (depth-planted `.dockerignore`):** passes, with a counterfactual so the
test is shown to have teeth. 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`. With the canonical patterns left unprefixed,
all six depth-planted paths reach `/src` in the image:
```
/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
```
With the shipped `**/` form, `docker run --rm lora.vegas find /src -name '.env'
-o -name '*.key' -o -name '*.pem' -o -name 'node_modules' -o -name '.claude'`
returns nothing, and `ls -a /src` does not list `.claude`. The emptied `a`
directory shell still appears — `.dockerignore` excludes matching files, and a
directory left empty is still created in the context — but nothing inside it
survived. Throwaways and probe images deleted; no `docker builder prune` was
run at any point.
**Comment 3's step 3 ([#23](https://git.eeqj.de/sneak/lora.vegas/issues/23)
re-verification):** passes against the new, smaller context. Two consecutive
`script/cibuild` runs on an unchanged tree; run 2 has `COPY . .` `CACHED` and
`RUN script/bootstrap` `CACHED`, yet the check layer still executes — two Hugo
builds and `All matched files use Prettier code style!` in both runs. Full
output is pasted in the PR. A bare `docker build .` still fails on the
`CHECK_EPOCH` guard.
**Two DoD conflicts surfaced, neither resolved silently:**
1. The canonical `REPO_POLICIES.md` is not clean under this repo's prettier
settings — prettier@3.4.2 inserts a blank line before a nested list that
follows a paragraph, in five places — so DoD 1 (byte-identical) and DoD 4
(`make fmt` diff-free) cannot both hold. Resolved as DoD 4 anticipates:
commit 1 adds the byte-identical copy, commit 2 is the five-blank-line
reformat on its own. Syncing that back upstream is now a Future Step.
2. `REPO_POLICIES.md` says `make check` "must not modify any files in the
repo"; `script/check`'s header says "must not modify any **tracked** files".
`make check` writes `public/` and `.hugo_build.lock`, both gitignored, so
`git status --porcelain` is empty but files are created. `script/check`'s
wording is the accurate one; the policy sentence is unsatisfiable for any
repo whose check builds artifacts. Called out in the PR for a decision
rather than patched locally, since the canonical copy should not diverge.
`make fmt` is a no-op at `HEAD`, `make check` is green and leaves the tree
clean, `make docker` builds. `TODO.md` updated in the functional commit.
Two defects in the canonical upstream files, found while implementing this.
Recording here because they affect every repo that syncs them and I have no
write access to prompts. @sneak — these want fixing at source.
1. prompts/prompts/REPO_POLICIES.md is not prettier-clean. prettier@3.4.2
with the shared settings (tab width 4, proseWrap: always) wants a blank line
before a nested list following a paragraph, in five places. So any repo that
both commits the canonical file byte-identical and runs prettier over its
own markdown in make check has two requirements that cannot both hold. PR #31
handles it by splitting into a byte-identical commit plus a reformat commit,
but every future sync into any repo re-introduces the conflict. Fix: reformat
the file upstream with the shared settings; downstream copies then become
straight byte syncs.
2. REPO_POLICIES.md and the canonical script/check contradict each
other. The policy says make check "must not modify any files in the repo"; script/check's header says "must not modify any tracked files". The
policy version is unsatisfiable for any repo whose check builds artifacts —
this one writes public/ and .hugo_build.lock, both gitignored. It also
cannot be used as a literal compliance probe, since it would fail every repo
that builds anything. Fix: change the policy line to "tracked files", matching script/check.
PR #31 treats script/check as authoritative locally and deliberately does not patch the committed canonical copy — diverging it downstream would
defeat the purpose of committing it. That is the right call, but it leaves this
repo carrying a canonical file that contradicts a canonical script until
upstream is fixed.
**Two defects in the canonical upstream files, found while implementing this.**
Recording here because they affect every repo that syncs them and I have no
write access to `prompts`. @sneak — these want fixing at source.
**1. `prompts/prompts/REPO_POLICIES.md` is not prettier-clean.** prettier@3.4.2
with the shared settings (tab width 4, `proseWrap: always`) wants a blank line
before a nested list following a paragraph, in five places. So any repo that
both commits the canonical file byte-identical **and** runs prettier over its
own markdown in `make check` has two requirements that cannot both hold. PR #31
handles it by splitting into a byte-identical commit plus a reformat commit,
but every future sync into any repo re-introduces the conflict. Fix: reformat
the file upstream with the shared settings; downstream copies then become
straight byte syncs.
**2. `REPO_POLICIES.md` and the canonical `script/check` contradict each
other.** The policy says `make check` "must not modify any files in the repo";
`script/check`'s header says "must not modify any **tracked** files". The
policy version is unsatisfiable for any repo whose check builds artifacts —
this one writes `public/` and `.hugo_build.lock`, both gitignored. It also
cannot be used as a literal compliance probe, since it would fail every repo
that builds anything. Fix: change the policy line to "tracked files", matching
`script/check`.
PR #31 treats `script/check` as authoritative locally and deliberately does
**not** patch the committed canonical copy — diverging it downstream would
defeat the purpose of committing it. That is the right call, but it leaves this
repo carrying a canonical file that contradicts a canonical script until
upstream is fixed.
Neither blocks PR #31.
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.
Problem
REPO_POLICIES.mdlists the files every repo must contain at minimum. Thisrepo is missing four of them, and its
.gitignoreis missing the mandatedcontent. All five are verbatim drops of canonical files from the
promptsrepo, so this is one cohesive commit.
Missing:
REPO_POLICIES.mdRequired in the minimum file list. Fetch from
https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md.It must retain its YAML front matter (
title:andlast_modified: YYYY-MM-DD) so it can be kept in sync as policies evolve.Missing:
.editorconfig"All repos should have an
.editorconfigenforcing the project's indentationsettings." Canonical contents:
Missing:
.prettierrcand.prettierignore"Documentation and writing repos (Markdown, HTML, CSS) should also have
.prettierrcand.prettierignore." A Hugo site is exactly that. Canonicalcontents:
.prettierrc:.prettierignore:Note that
script/fmtandscript/fmt-checkalready pass--tab-width 4 --prose-wrap alwayson the command line. Keep those flags — the canonicalscripts duplicate them deliberately so the scripts work standalone when copied
as a template. Adding
.prettierrcmust not change whatmake fmtdoes.Incomplete:
.gitignoreCurrent contents are Hugo outputs only (
/public/,/resources/,.hugo_build.lock). Policy: ".gitignoreshould be comprehensive from thestart: OS files (
.DS_Store), editor files (.swp,*~), language buildartifacts, and
node_modules/", and separately "Never commit secrets..envfiles, credentials, API keys, and private keys must be in
.gitignore. Noexceptions."
Append the canonical block, keeping the existing Hugo lines:
Definition of done
REPO_POLICIES.mdexists at the repo root, is byte-identical to theupstream canonical copy, and
head -5 REPO_POLICIES.mdcontainslast_modified:..editorconfig,.prettierrc,.prettierignoreexist at the repo rootwith exactly the contents quoted above.
.gitignoreretains its three existing Hugo lines and gains the canonicalblock above; it contains the literal patterns
.env,*.key, and*.pem.make fmtproduces no diff after the change (adding.prettierrcmust be ano-op against the flags already passed by
script/fmt). If it does producea diff, commit the reformat as its own separate commit — formatting churn
must not be mixed with functional changes.
make checkpasses andscript/cibuildsucceeds.TODO.mdis updated in the same commit as the work, per the Workflow block.Explicitly out of scope — no Hugo equivalent, do not add
Each of the following is either language-specific or not required by any org
standard. Do not create them:
go.mod,go.sum,.golangci.yml— Go module and Go linter files; there isno Go code here.
package.json,yarn.lock— only required for JS repos.script/fmtgetsprettier via
npx --yes prettier@<pinned>, so there is no dependencymanifest to maintain. Adding one would pull in the whole node/nvm/corepack
pinning apparatus for a single formatter.
pyproject.toml— wrong language.CONTRIBUTING.md,SECURITY.md,.gitattributes,CHANGELOG.md,AGENTS.md, rootCLAUDE.md, renovate/dependabot config, issue/PRtemplates,
CODEOWNERS— none of these appear in any org standard. Theywere checked for explicitly and are not mandated.
.claude/scaffolding — the memory policy states "Repos with no memories yetneed no
.claude/scaffolding." This repo has none.Ref:
REPO_POLICIES.mdminimum file list;EXISTING_REPO_CHECKLIST.md.Additional scope for this issue, found while testing the
script/cibuildcachebehaviour for #23.
git status --porcelainin a clean checkout reports:.claude/is agent tooling scaffolding (it holdsworktrees/among otherthings). It is in neither
.gitignorenor.dockerignore, which has twoconsequences:
git addpicksit up.
REPO_POLICIES.mdforbidsgit add -A/git add .precisely tolimit this class of mistake, but the ignore file is the belt to that
braces.
DockerfiledoesCOPY . .and.dockerignorecurrently lists only.git,public,resources, and.hugo_build.lock. So.claude/— includingworktrees/, which can holdentire additional checkouts of this repo — gets copied into the image. That
inflates the context and the image, slows every build, and in the worst case
copies unrelated in-flight work into a build artifact.
Add
.claude/to both files as part of this issue:.gitignore— alongside the canonical block already specified above..dockerignore— which should also gain the editor/OS patterns for the samereason; there is no value in shipping
.DS_Storeor.idea/into the buildcontext either.
Updated definition-of-done items for this issue:
.gitignorecontains.claude/, andgit status --porcelainis empty in aclean checkout with agent tooling present.
.dockerignorecontains.claude/and the OS/editor patterns. Verify bybuilding and confirming the context size drops (or at minimum that
.claude/is absent from the image —docker run --rm <image> ls -a /srcshould not list it).
Note the ordering interaction with #23: until #23 lands, a
script/cibuildrunmay be served entirely from cache, so a build used to verify item 8 must be run
with the cache defeated or it will prove nothing.
Blocked on #23 — do not land the
.dockerignoreportion of this issuefirst.
Adding
.claude/to.dockerignoreremoves a live source of build-contextchurn.
.claude/worktrees/is created and destroyed constantly by tooling, andthat churn intermittently invalidates
COPY . ., forcing theRUN make checklayer to actually execute. Excluding it makes Docker's cache stickier and more
consistent — correct in itself, but it removes an accidental safety net while
#23's unearned-green hole is still open. The gate would get quietly weaker,
with nothing in the build output to signal the change.
Required sequencing:
make checkcannot be served from cache)..dockerignorechange.the cache behaviour that #23 was validated against.
The rest of this issue —
REPO_POLICIES.md,.editorconfig,.prettierrc,.prettierignore, and the.gitignoreadditions including.claude/— has nosuch dependency and can proceed at any time. If it is convenient to start
this issue before #23 is done, split it: land everything except the
.dockerignoreedit, and leave that for a follow-up commit after #23. Adding.claude/to.gitignoreis unaffected; only the Docker build context mattershere.
For the record, the fleet-wide version of this warning describes repos with
no
.dockerignoreat all being accidentally protected because.gitchurnsin the context. That is not this repo — lora.vegas already excludes
.git,which is why the cache hole reproduces here cleanly. The dependency above is
real but arrives via
.claude/, not.git.Correction to the
.dockerignorework — pattern semantics. This is asecurity point, not a tidiness one.
.dockerignoredoes not use.gitignoresemantics. It matches with Go'sfilepath.Matchrules, extended with**. Two consequences that break thenaive approach:
*does not cross/. So*.keymatchesserver.keyat the contextroot and does not match
certs/server.key.node_modules/excludes only a top-level
node_modules, not a nested one.Another repo on the roster proved this empirically by planting files at depth
and watching
config/.env,certs/server.key, and nestednode_modulesallreach the build context despite patterns that appeared to cover them.
For this repo that matters directly, because the
DockerfiledoesCOPY . .and the whole point of the secrets patterns is that a stray key or
.envmustnever be baked into an image. A
.dockerignorethat silently protects only theroot is worse than an obviously incomplete one, because it reads as complete.
Requirement: every depth-independent pattern in
.dockerignoregets a**/prefix. So**/.env,**/*.key,**/*.pem,**/node_modules,**/.DS_Store,**/.claude, and so on. Patterns that genuinely areroot-anchored —
public,resources— stay unprefixed, deliberately, sincethose are specifically the Hugo output directories at the repo root.
Note this applies only to
.dockerignore..gitignorehas differentsemantics where an unanchored pattern already matches at any depth, so the
canonical
.gitignoreblock quoted in the issue body is correct as written andmust not be given
**/prefixes.Added definition-of-done item:
.dockerignorepatterns by planting throwaway files at least twodirectories deep — e.g.
a/b/.env,a/b/server.key,a/b/node_modules/x— then confirming they are absent from the built image (
docker run --rm <image> find /src -name '.env' -o -name '*.key'returns nothing). Deletethe throwaway files afterwards. Testing only at the repo root does not
exercise the bug and will produce a false pass.
Tracked upstream as
prompts#29.Combined with the earlier ordering note, the
.dockerignoreportion of thisissue now has two constraints: it lands after #23, and it must use
**/prefixes verified at depth. The rest of this issue is unaffected and can
proceed independently.
Implementation plan
Branching from current
main(ccdedc3), which already contains #23 /PR #30, so the ordering
constraint from the second comment is satisfied and the
.dockerignorechangecan land in this same commit rather than a follow-up.
One commit,
... (closes #8):REPO_POLICIES.md—curlthe raw canonical frompromptsverbatim (bytecopy, not a reflow), front matter intact. Note it lands inside
script/fmt-check's*.mdglob, so it must already satisfy--tab-width 4 --prose-wrap always; if it does not, item 4 of the DoD and"byte-identical" collide and I will report that rather than reformat it
silently.
.editorconfig,.prettierrc,.prettierignore— exactly the contentsquoted in the body.
.gitignore— keep the three Hugo lines, append the canonical blockverbatim, plus a
.claude/entry. No**/prefixes here — unanchored.gitignorepatterns already match at any depth..dockerignore— every depth-independent pattern gets**/(
**/.env,**/.env.*,**/*.key,**/*.pem,**/node_modules,**/.DS_Store,**/.claude, editor patterns, ...).publicandresourcesstay unprefixed: they are the root Hugo output dirs byintention.
.gitand.hugo_build.lockkeep their current form.Not touched, per the out-of-scope list:
LICENSE(that is#10, still awaiting an owner
decision — the README License section stays as-is),
go.mod,package.json,yarn.lock,CONTRIBUTING.md,SECURITY.md,.gitattributes,CHANGELOG.md,AGENTS.md, root agent-memory file, renovate/dependabot,.claude/scaffolding.Verification
.dockerignoreproof (DoD 9): planta/b/.env,a/b/server.key,a/b/node_modules/xand a.claude/marker, build, thendocker run --rm <image> find /src -name '.env' -o -name '*.key'and expectempty. Root-only testing does not exercise the bug. Throwaways deleted
after, and I will also confirm the pre-change
.dockerignorefails thissame probe, so the test is shown to have teeth.
consecutive
script/cibuildruns on an unchanged tree, both pasted, bothshowing the two Hugo builds plus the prettier line, with
RUN script/bootstrapstill
CACHEDin the second.make fmtdiff-free,make checkgreen,make dockerstill builds.docker builder prunein any form — shared host. Cache defeat is scopedto
--no-cache-filter/ a throwaway tag.TODO.mdupdated in the same commit.script/bootstrapanddeploy.ymlareuntouched, so the live deploy path is unaffected and no temporary branch
trigger is needed.
I will also reconcile a wording conflict this introduces:
REPO_POLICIES.mdsays
make check"must not modify any files in the repo", whilescript/check's header says "must not modify any tracked files". Both willbe present in the tree; I will call out which is authoritative in the PR rather
than leave them contradicting.
Done in PR #31 (branch
policy-dotfiles), two commits offccdedc3.What was built:
REPO_POLICIES.md(byte-identical canonical copy, frontmatter intact),
.editorconfig,.prettierrc,.prettierignore(canonicalcontents),
.gitignoreextended with the canonical block plus.claude/, and.dockerignorerewritten with**/-prefixed depth-independent patterns.LICENSEand the README License section untouched, per the out-of-scope listand #10.
script/bootstrapand
deploy.ymluntouched, so the live deploy path is unaffected.DoD 9 (depth-planted
.dockerignore): passes, with a counterfactual so thetest is shown to have teeth. 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. With the canonical patterns left unprefixed,all six depth-planted paths reach
/srcin the image:With the shipped
**/form,docker run --rm lora.vegas find /src -name '.env' -o -name '*.key' -o -name '*.pem' -o -name 'node_modules' -o -name '.claude'returns nothing, and
ls -a /srcdoes not list.claude. The emptiedadirectory shell still appears —
.dockerignoreexcludes matching files, and adirectory left empty is still created in the context — but nothing inside it
survived. Throwaways and probe images deleted; no
docker builder prunewasrun at any point.
Comment 3's step 3 (#23
re-verification): passes against the new, smaller context. Two consecutive
script/cibuildruns on an unchanged tree; run 2 hasCOPY . .CACHEDandRUN script/bootstrapCACHED, yet the check layer still executes — two Hugobuilds and
All matched files use Prettier code style!in both runs. Fulloutput is pasted in the PR. A bare
docker build .still fails on theCHECK_EPOCHguard.Two DoD conflicts surfaced, neither resolved silently:
REPO_POLICIES.mdis not clean under this repo's prettiersettings — prettier@3.4.2 inserts a blank line before a nested list that
follows a paragraph, in five places — so DoD 1 (byte-identical) and DoD 4
(
make fmtdiff-free) cannot both hold. Resolved as DoD 4 anticipates:commit 1 adds the byte-identical copy, commit 2 is the five-blank-line
reformat on its own. Syncing that back upstream is now a Future Step.
REPO_POLICIES.mdsaysmake check"must not modify any files in therepo";
script/check's header says "must not modify any tracked files".make checkwritespublic/and.hugo_build.lock, both gitignored, sogit status --porcelainis empty but files are created.script/check'swording is the accurate one; the policy sentence is unsatisfiable for any
repo whose check builds artifacts. Called out in the PR for a decision
rather than patched locally, since the canonical copy should not diverge.
make fmtis a no-op atHEAD,make checkis green and leaves the treeclean,
make dockerbuilds.TODO.mdupdated in the functional commit.Two defects in the canonical upstream files, found while implementing this.
Recording here because they affect every repo that syncs them and I have no
write access to
prompts. @sneak — these want fixing at source.1.
prompts/prompts/REPO_POLICIES.mdis not prettier-clean. prettier@3.4.2with the shared settings (tab width 4,
proseWrap: always) wants a blank linebefore a nested list following a paragraph, in five places. So any repo that
both commits the canonical file byte-identical and runs prettier over its
own markdown in
make checkhas two requirements that cannot both hold. PR #31handles it by splitting into a byte-identical commit plus a reformat commit,
but every future sync into any repo re-introduces the conflict. Fix: reformat
the file upstream with the shared settings; downstream copies then become
straight byte syncs.
2.
REPO_POLICIES.mdand the canonicalscript/checkcontradict eachother. The policy says
make check"must not modify any files in the repo";script/check's header says "must not modify any tracked files". Thepolicy version is unsatisfiable for any repo whose check builds artifacts —
this one writes
public/and.hugo_build.lock, both gitignored. It alsocannot be used as a literal compliance probe, since it would fail every repo
that builds anything. Fix: change the policy line to "tracked files", matching
script/check.PR #31 treats
script/checkas authoritative locally and deliberately doesnot patch the committed canonical copy — diverging it downstream would
defeat the purpose of committing it. That is the right call, but it leaves this
repo carrying a canonical file that contradicts a canonical script until
upstream is fixed.
Neither blocks PR #31.