Add canonical policy dotfiles, harden both ignore files (closes #8) #31
Reference in New Issue
Block a user
Delete Branch "policy-dotfiles"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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.