chore: root .editorconfig and hardened .gitignore (closes #15) #35
Reference in New Issue
Block a user
Delete Branch "chore/dotfile-compliance"
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 #15 (two of three bullets; the third is deferred with reasoning, see
below).
1. Root
.editorconfiggit mv backend/.editorconfig .editorconfig— a pure relocation, zero contentchange. The file was already byte-identical to the org model
(
prompts/.editorconfig); I verified withcmprather than eyeballing it, bothbefore and after the move.
The bug this fixes is
root = truebeing one level down. That directive stopseditors walking further up, so the frontend —
src/,index.html,vite.config.js,nginx.conf,script/— had no indentation settings at all.At the repo root the same file covers the whole tree including
backend/, whichis why the subdirectory copy is deleted rather than duplicated. The
[Makefile]section still applies to
backend/Makefile: an.editorconfigpattern with no/in it matches at any depth.2.
.gitignoreCopied the org model verbatim from the local
promptsclone (cmp-verifiedagainst it), then re-appended the two entries this repo has that the model does
not:
dist/(Vite output) and*.log. Net additions over the previous 4-linefile:
Thumbs.db*.swp,*.swo,*~,*.bak,.idea/,.vscode/,*.sublime-*.env,.env.*,*.pem,*.keyThe secrets section is the part that matters. The backend loads
.envthroughgodotenv, and onlybackend/.gitignoreignored it — a.envat the repo rootwas untracked but unignored, i.e. one
git add -Afrom being committed.backend/.gitignoreis untouched: its entries (/netwatch-server,*.out,*.test,data/) are backend-specific paths, not duplicates of the root set.3.
.dockerignore.gitexclusion — DEFERRED, not skippedI did not add
.gitto.dockerignore, and I want that called out rather thanburied. Both images read git metadata at build time on
mainatfbfe1df:Dockerfile.backend:12isCOPY .git /repo/.git, feedinggit describe --always --dirtyinbackend/Makefile. A.dockerignoreentrymakes that
COPYfail — the build errors out, it does not merely lose aversion string.
DockerfiledoesCOPY . .thenRUN make check, whose test step is theproduction
vite build.vite.config.js:5-6callgit rev-parse --short HEADand
git rev-parse HEADat config-eval time, so Vite throws beforetransforming a single module.
Decoupling the builds from in-container git (build-arg version injection) is
owned by #17, whose definition of done already includes removing
COPY .git /repo/.git. Doing it here would either breakdocker build .anddocker build -f Dockerfile.backend .or drag #17's whole Dockerfile rework intoa dotfiles commit.
Because this PR's commit closes #15, I will file a small follow-up issue for the
residual bullet so it survives the close, and link it here — the frontend
vite.config.jshalf of the coupling is not currently covered by #17's scope,only the backend half is.
.dockerignoreis unmodified by this PR.Verification
make check— pass (vite build4 modules OK;prettier --check .clean for both the lint and fmt-check steps).
cd backend && make check— pass (go test ./...OK,golangci-lint run ./...reports0 issues.).make fmtrun before committing; prettier reportedTODO.mdunchanged, so themarkdown was already conformant.
git ls-filesbefore vs. after differs byexactly two lines —
+.editorconfig/-backend/.editorconfig— and nothingelse. Piping the full tracked set through
git check-ignore --stdinexits 1(zero matches); I sanity-checked that invocation against a known-ignored path
first so the clean result is not a false negative.
git status --shortisclean at HEAD.
.env/*.pem/*.key/.vscode//.idea//*.sublime-*/Thumbs.db/dist//*.logbeforehand: no hits, so no newpattern had anything to swallow.
.dockerignoreuntouched, so no Docker build behaviour changes and no dockerbuild was needed to clear this PR.
Notes for the reviewer
TODO.mdgets one additive entry at the top of Completed Steps and nothingelse. I deliberately left Status / Next Step / Future Steps alone: PR #31 is
open and merge-ready and rewrites exactly those sections, so editing them here
would manufacture a conflict. Whichever of the two lands second should get a
trivial two-adjacent-additions resolution at the top of Completed Steps.
.claudeentries in.dockerignoreand.prettierignoreare left inplace — they are #28's business, not this PR's.
What I built
Three files changed, 32 insertions / 2 deletions, no code touched.
backend/.editorconfig->.editorconfig— recorded by git as a purerename (
R backend/.editorconfig -> .editorconfig, 0 content lines changed).The file already matched the org model exactly, so nothing was rewritten.
.gitignore— the org model verbatim, plus# Build output/dist/and# Logs/*.logre-appended so nothing this repo already relied on is lost.TODO.md— one entry at the top of Completed Steps, in the same commit.How I verified it
The dangerous part — did any tracked file get swallowed? No.
git ls-filesbefore and after, diffed:That is the entire delta — the intended relocation and nothing else. 55 tracked
files before, 55 after.
Then the direct question, piping every tracked path through the ignore matcher:
Exit 1 from
check-ignoremeans zero paths matched. I did not take that onfaith — a wrong invocation also produces "no output", so I first ran the same
command against a known-ignorable path and confirmed it reports a hit
(
.gitignore:18:.env foo/.env). The clean result is therefore a real negative,not a silently broken command.
Before writing the new patterns I also grepped the tracked set for anything the
new sections could catch —
.env,*.pem,*.key,.vscode/,.idea/,*.sublime-*,Thumbs.db,dist/,*.log— and got no hits, so there wasnothing at risk to begin with.
git status --shortis empty at4a7bdf8.Gates.
make check: pass.script/test(vite build) transformed 4 modulesand emitted
dist/;script/lintandscript/fmt-checkboth reportAll matched files use Prettier code style!.cd backend && make check: pass.go test ./...green (handlersandreportbufOK, rest no test files);golangci-lint run ./...reports0 issues.make fmtwas run before committing. Prettier listedTODO.mdas(unchanged), so the markdown was already conformant rather than beingreformatted under me.
make/script/entrypoints only.What I did not do
.dockerignoreis untouched — the.gitbullet is deferred, with the reasoningin the PR body and a matching comment on #15. Follow-up filed as #36, which
also picks up the frontend
vite.config.jshalf of the git coupling that #17'sscope does not cover. No
docker buildwas run because no Docker input changed.Worked in a throwaway worktree, not the shared checkout; the worktree has been
removed. Branch is pushed.
Review of PR #35 (head
4a7bdf8)Verdict: PASS
Independently verified in a throwaway worktree at
4a7bdf8. Nothing in the PRbranch or the shared checkout was modified.
Independently verified
Root
.editorconfigcmp .editorconfig /home/user/dev/prompts/.editorconfig— exit 0, byte-identical. Not eyeballed.backend/.editorconfigis gone; git records the change asR100 backend/.editorconfig -> .editorconfig, i.e. a pure rename with zero content lines changed.The
[Makefile]-reaches-backend/Makefileclaim is correct, and I did not take it on the spec alone. Resolved the properties with the reference implementation (editorconfig-core-py 0.17.1) against the actual tree at4a7bdf8:backend/Makefile->indent_style = tabMakefile->indent_style = tabsrc/main.js,script/check,backend/internal/server/server.go->indent_style = space, indent_size = 4So
backend/Makefilekeeps its tab rule (no regression), and the rootMakefilegains one it never had. This matches the spec rule that a section name containing no/may match at any level below the.editorconfigfile..gitignorediffagainst/home/user/dev/prompts/.gitignore: the model content is present verbatim and in order; the only delta is the appended# Build output/dist/and# Logs/*.log. No model line dropped, no line reordered.node_modules/,dist/,.DS_Store,*.log) survive..env,.env.*,*.pem,*.key,Thumbs.db, and the full Editors section are present..env,.env.production,foo.pem,secret.key,Thumbs.db, and.vscode/settings.jsonat the worktree root:git status --shortoffered none of them, andgit check-ignore -vattributed each to the expected line (.gitignore:18:.env,:19:.env.*,:20:*.pem,:21:*.key,:3:Thumbs.db,:11:.vscode/). Scratch files removed; worktree clean afterwards.No tracked file became ignored (the way this change could have done real damage)
git ls-tree -r --name-onlyatfbfe1dfvs4a7bdf8: 55 files before, 55 after. Full delta is-backend/.editorconfig/+.editorconfigand nothing else.git ls-files -z | git check-ignore --stdin -z --no-index -vexits 1 with zero matched paths.foo/.env,build/x.pem, andsrc/.vscode/settings.jsonfirst; it exited 0 and reported all three with their matching rules. The command is therefore live, and the clean result over the tracked set is a real negative rather than a malformed command producing no output.Deferred
.dockerignorebullet.dockerignoreis genuinely unmodified:git diff fbfe1df..HEAD -- .dockerignoreis empty. It still readsnode_modules,dist,.DS_Store,*.log,.claude— untouched, so the pre-existing.claudeentry stays #28's business, as stated.Dockerfile.backend:12is literallyCOPY .git /repo/.git, andbackend/Makefile:2isVERSION := $(shell git describe --always --dirty). With.gitignored thatCOPYhas no source and the build errors — a hard failure, not a degraded version string, as claimed.Dockerfile:7isCOPY . .followed byRUN make checkat line 11;vite.config.js:5-6areexecSync("git rev-parse --short HEAD")andexecSync("git rev-parse HEAD")at module scope, evaluated when Vite loads the config. Vite would throw before transforming a module. The observation that the frontend inherits.gitviaCOPY . .rather than copying it deliberately is also correct.Gates
make check— pass.vite buildtransformed 4 modules;prettier --check .clean for both the lint and fmt-check steps.make fmtis therefore clean (fmt-check is the same prettier invocation andTODO.mdis not in.prettierignore).cd backend && make check— pass.go test ./...green,golangci-lint run ./...reports0 issues.make/script/entrypoints only.4a7bdf8:check / check (push)success.Commit and merge hygiene
fbfe1df.chore: root .editorconfig and hardened .gitignore (closes #15)— ends with the required(closes #15).origin/mainis stillfbfe1df; head is a direct descendant, andgit merge-treeagainstorigin/mainreports no conflict. Fast-forwardable.Co-Authored-By, no session links. The.claudementions in the PR body and.prettierignore/.dockerignoreare pre-existing filenames tracked in #28 and are not touched by this PR.backend/.gitignorecorrectly left alone.Follow-up scoping (#36)
COPY .git /repo/.gitis gone, version fromARG VERSION, andbackend/Makefiletoleratinggit describebeing unavailable — the backend half only. #17 contains no mention ofvite.config.js, the frontendDockerfile, or the.dockerignoreline itself; it only notes in passing that the.gitcopy blocks the.dockerignorechange. The claim is accurate: the frontend coupling and the.dockerignoreentry were owned by nobody. #36 picks up exactly that gap, sequences itself after #17, and explicitly tells the implementer not to redo #17's work. Correctly scoped, and the right call given the mandated(closes #15)title would otherwise vaporize the residual bullet.Findings
Blocking
None.
Minor (non-blocking, do not hold the merge for these)
TODO.mdFuture Steps, first bullet — now fully stale and left in place.The bullet reads "Compliance top-up as one small commit: add .editorconfig and add the hooks target to the Makefile". This PR completed the
.editorconfighalf, and thehookstarget already existed in the rootMakefileatfbfe1df(line 34), so the bullet was already half-stale onmainand is now entirely done while still advertising outstanding work. Acceptable would be deleting that one bullet in this commit. Note this would not have collided with PR #31: #31 rewritesStatusandNext Step, notFuture Steps. The conflict-avoidance rationale is sound for the sections it actually covers — I confirmed #31 does rewriteStatusandNext Step, so leaving those alone was the right call — but it does not extend toFuture Steps.PR body promises a link it never got. Section 3 says "I will file a small follow-up issue for the residual bullet ... and link it here". #36 was filed, but the body was never edited to carry the link; it exists only in the follow-up comment and on #15. Cosmetic.
Assessed and found correct (no action)
TODO.mdCompleted Stepsentry is accurate on every claim it makes, including the deferral reason, and is additive at the top of the section as described.indent_style = spaceunder the root.editorconfig, which fightsgofmt. This is zero delta —backend/.editorconfigwas byte-identical and produced exactly the same result before the move — and it is the org model's own content, so it is out of scope here and not a defect in this PR.Manager note — merge-ready
Independent adversarial review returned PASS with no blocking findings. Relabelled
needs-review->merge-readyand assigned to @sneak for the merge, sincemainis protected.Why I am accepting this
The two findings that could have made this dangerous were both tested rather than reasoned about:
.gitignorechange does real damage, and it was verified as a validated negative. The reviewer ran the sanity check first — confirmingfoo/.env,build/x.pem, andsrc/.vscode/settings.jsonare correctly reported with their matching rules — before accepting the zero-match result on the real tracked set. An empty result from a malformed command looks identical to a clean one, and both the implementer and the reviewer guarded against that independently. 55 tracked files before and after; the entire delta is the.editorconfigrelocation.[Makefile]matches at any depth" claim was checked empirically against the reference editorconfig implementation, not just read off the spec.backend/Makefileresolves toindent_style = tab, and the frontend files resolve to space/4. Had that claim been wrong,backend/Makefilewould have silently lost its tab rule — a subtle regression that would have surfaced later as a mangled Makefile. It is correct, and the rootMakefileactually gains a rule it never had.The secrets patterns were also functionally tested, not just diffed: scratch
.env,.env.production,foo.pem,secret.key,Thumbs.db, and.vscode/settings.jsonwere each confirmed withheld and attributed to the expected rule.The deferral is correct and I am endorsing it
.dockerignoreis genuinely unmodified, and the reviewer independently confirmed both halves of the stated blocker:Dockerfile.backend:12COPY .git /repo/.gitfeedinggit describe, andvite.config.js:5-6callingexecSync("git rev-parse ...")at module scope behindDockerfile'sCOPY . .+RUN make check. Adding the entry today breaks both builds outright.Deferring was the right call and it was disclosed in three places rather than buried. Follow-up #36 now carries it, attached to
1.0.0and sequenced after #17. Thevite.config.jshalf was a gap in my own issue-writing — I scoped #15 and #17 around the backend coupling only and missed that the frontend picks up.gitincidentally. Good catch by the implementer.Two minor findings I am not sending back for
TODO.md's firstFuture Stepsbullet is now fully stale ("add .editorconfig and add the hooks target to the Makefile"). The.editorconfighalf is done by this PR; thehookshalf was already wrong onmain, since the rootMakefilehas hadhooks: @script/install-precommitfor some time. The implementer's conflict-avoidance reasoning was sound for Status/Next Step — PR #31 rewrites exactly those — but the reviewer is right that it does not extend toFuture Steps, which #31 does not touch. Recorded on #24, which ownsTODO.mdaccuracy.Merge order
Merge #35 before #31. Both are merge-ready and both touch
TODO.md, but only as adjacent additions at the top of Completed Steps. Landing #35 first lets #31's rewrite of Status/Next Step absorb the trivial resolution in one pass. Either order works; this one is less fiddly.Checked against the cached-green risk —
merge-readystandsA fleet-wide warning landed after I labelled this:
script/cibuildis a baredocker build ., so on an unchanged tree Docker can serveRUN make checkfrom cache and exit 0 having run nothing. I reproduced that hole in this repo (514ms, 9 cached layers,make checkcached) and filed it as #37.It does not undermine this PR, for two independent reasons:
This PR's gates were direct host runs, not Docker builds. Root
make checkandcd backend && make checkwere executed on the host by both the implementer and the reviewer. Those invokescript/checkdirectly — there is no Docker layer cache in that path to serve a stale result from.This PR's changes necessarily bust the cache anyway. It adds
.editorconfigat the root and rewrites.gitignore, both inside the build context.COPY . .therefore produces a different layer, which invalidates theRUN make checklayer beneath it. A cached green is not reachable for this diff even via Docker.No Docker build was needed to clear this PR in the first place, since
.dockerignoreis untouched and no Docker input changed behaviourally — that reasoning still holds.For completeness: the equivalent re-verification for PR #31 was required, because that PR changes the lint gate itself. I ran a forced
--no-cachebuild there and confirmed the checks genuinely execute (74s, realgo testtimings,0 issues.). Both PRs remainmerge-readyon earned evidence.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.