chore: root .editorconfig and hardened .gitignore (closes #15) #35

Open
clawbot wants to merge 1 commits from chore/dotfile-compliance into main
Collaborator

Closes #15 (two of three bullets; the third is deferred with reasoning, see
below).

1. Root .editorconfig

git mv backend/.editorconfig .editorconfig — a pure relocation, zero content
change. The file was already byte-identical to the org model
(prompts/.editorconfig); I verified with cmp rather than eyeballing it, both
before and after the move.

The bug this fixes is root = true being one level down. That directive stops
editors 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/, which
is why the subdirectory copy is deleted rather than duplicated. The [Makefile]
section still applies to backend/Makefile: an .editorconfig pattern with no
/ in it matches at any depth.

2. .gitignore

Copied the org model verbatim from the local prompts clone (cmp-verified
against 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-line
file:

  • Thumbs.db
  • the whole Editors section — *.swp, *.swo, *~, *.bak, .idea/,
    .vscode/, *.sublime-*
  • the whole Environment / secrets section — .env, .env.*, *.pem, *.key

The secrets section is the part that matters. The backend loads .env through
godotenv, and only backend/.gitignore ignored it — a .env at the repo root
was untracked but unignored, i.e. one git add -A from being committed.

backend/.gitignore is untouched: its entries (/netwatch-server, *.out,
*.test, data/) are backend-specific paths, not duplicates of the root set.

3. .dockerignore .git exclusion — DEFERRED, not skipped

I did not add .git to .dockerignore, and I want that called out rather than
buried. Both images read git metadata at build time on main at fbfe1df:

  • Dockerfile.backend:12 is COPY .git /repo/.git, feeding
    git describe --always --dirty in backend/Makefile. A .dockerignore entry
    makes that COPY fail — the build errors out, it does not merely lose a
    version string.
  • Dockerfile does COPY . . then RUN make check, whose test step is the
    production vite build. vite.config.js:5-6 call git rev-parse --short HEAD
    and git rev-parse HEAD at config-eval time, so Vite throws before
    transforming 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 break docker build . and
docker build -f Dockerfile.backend . or drag #17's whole Dockerfile rework into
a 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.js half of the coupling is not currently covered by #17's scope,
only the backend half is. .dockerignore is unmodified by this PR.

Verification

  • Root make checkpass (vite build 4 modules OK; prettier --check .
    clean for both the lint and fmt-check steps).
  • cd backend && make checkpass (go test ./... OK,
    golangci-lint run ./... reports 0 issues.).
  • make fmt run before committing; prettier reported TODO.md unchanged, so the
    markdown was already conformant.
  • No tracked file became ignored. git ls-files before vs. after differs by
    exactly two lines — +.editorconfig / -backend/.editorconfig — and nothing
    else. Piping the full tracked set through git check-ignore --stdin exits 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 --short is
    clean at HEAD.
  • Grepped the tracked set for .env/*.pem/*.key/.vscode//.idea//
    *.sublime-*/Thumbs.db/dist//*.log beforehand: no hits, so no new
    pattern had anything to swallow.
  • .dockerignore untouched, so no Docker build behaviour changes and no docker
    build was needed to clear this PR.

Notes for the reviewer

  • TODO.md gets one additive entry at the top of Completed Steps and nothing
    else. 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.
  • The .claude entries in .dockerignore and .prettierignore are left in
    place — they are #28's business, not this PR's.
Closes #15 (two of three bullets; the third is deferred with reasoning, see below). ## 1. Root `.editorconfig` `git mv backend/.editorconfig .editorconfig` — a pure relocation, zero content change. The file was already byte-identical to the org model (`prompts/.editorconfig`); I verified with `cmp` rather than eyeballing it, both before and after the move. The bug this fixes is `root = true` being one level down. That directive stops editors 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/`, which is why the subdirectory copy is deleted rather than duplicated. The `[Makefile]` section still applies to `backend/Makefile`: an `.editorconfig` pattern with no `/` in it matches at any depth. ## 2. `.gitignore` Copied the org model verbatim from the local `prompts` clone (`cmp`-verified against 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-line file: - `Thumbs.db` - the whole Editors section — `*.swp`, `*.swo`, `*~`, `*.bak`, `.idea/`, `.vscode/`, `*.sublime-*` - the whole Environment / secrets section — `.env`, `.env.*`, `*.pem`, `*.key` The secrets section is the part that matters. The backend loads `.env` through `godotenv`, and only `backend/.gitignore` ignored it — a `.env` at the repo root was untracked but unignored, i.e. one `git add -A` from being committed. `backend/.gitignore` is untouched: its entries (`/netwatch-server`, `*.out`, `*.test`, `data/`) are backend-specific paths, not duplicates of the root set. ## 3. `.dockerignore` `.git` exclusion — DEFERRED, not skipped I did not add `.git` to `.dockerignore`, and I want that called out rather than buried. Both images read git metadata at build time on `main` at `fbfe1df`: - `Dockerfile.backend:12` is `COPY .git /repo/.git`, feeding `git describe --always --dirty` in `backend/Makefile`. A `.dockerignore` entry makes that `COPY` fail — the build errors out, it does not merely lose a version string. - `Dockerfile` does `COPY . .` then `RUN make check`, whose test step is the production `vite build`. `vite.config.js:5-6` call `git rev-parse --short HEAD` and `git rev-parse HEAD` at config-eval time, so Vite throws before transforming 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 break `docker build .` and `docker build -f Dockerfile.backend .` or drag #17's whole Dockerfile rework into a 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.js` half of the coupling is not currently covered by #17's scope, only the backend half is. `.dockerignore` is unmodified by this PR. ## Verification - Root `make check` — **pass** (`vite build` 4 modules OK; `prettier --check .` clean for both the lint and fmt-check steps). - `cd backend && make check` — **pass** (`go test ./...` OK, `golangci-lint run ./...` reports `0 issues.`). - `make fmt` run before committing; prettier reported `TODO.md` unchanged, so the markdown was already conformant. - **No tracked file became ignored.** `git ls-files` before vs. after differs by exactly two lines — `+.editorconfig` / `-backend/.editorconfig` — and nothing else. Piping the full tracked set through `git check-ignore --stdin` exits 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 --short` is clean at HEAD. - Grepped the tracked set for `.env`/`*.pem`/`*.key`/`.vscode/`/`.idea/`/ `*.sublime-*`/`Thumbs.db`/`dist/`/`*.log` beforehand: no hits, so no new pattern had anything to swallow. - `.dockerignore` untouched, so no Docker build behaviour changes and no docker build was needed to clear this PR. ## Notes for the reviewer - `TODO.md` gets one additive entry at the top of Completed Steps and nothing else. 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. - The `.claude` entries in `.dockerignore` and `.prettierignore` are left in place — they are #28's business, not this PR's.
clawbot added the needs-review label 2026-08-09 07:00:01 +02:00
clawbot added 1 commit 2026-08-09 07:00:01 +02:00
chore: root .editorconfig and hardened .gitignore (closes #15)
All checks were successful
check / check (push) Successful in 26s
4a7bdf8f61
Three root-level dotfiles diverged from the org models. Two are fixed
here; the third is deferred for a reason spelled out below.

Move backend/.editorconfig to the repo root. The file is byte-identical
to the org model, so this is a pure relocation with no content change.
It carries root = true, which one level down was actively harmful: it
stopped editors walking further up, leaving the entire frontend
(src/, index.html, vite.config.js, nginx.conf, script/) with no
indentation settings at all. At the root the same file covers the whole
tree, backend included, so the subdirectory copy is redundant.

Replace .gitignore with the org model verbatim, then re-append the two
repo-specific entries the model does not carry: dist/ (Vite output) and
*.log. This adds the OS entry Thumbs.db, the entire Editors section
(*.swp, *.swo, *~, *.bak, .idea/, .vscode/, *.sublime-*), and the
Environment / secrets section (.env, .env.*, *.pem, *.key).

The secrets section is the substantive part. The backend loads .env via
godotenv and only backend/.gitignore ignored it, so a .env at the repo
root was untracked but unignored -- one git add -A away from being
committed. Policy allows no exceptions there.

Not done here: excluding .git from .dockerignore. Both images read git
metadata at build time. Dockerfile.backend has an explicit
COPY .git /repo/.git feeding git describe in backend/Makefile, and the
frontend Dockerfile's make check runs vite, whose config calls
git rev-parse at config-eval time. Ignoring .git breaks both builds
outright rather than degrading them, and decoupling them from git
metadata belongs to the Dockerfile rework in #17. Deferred deliberately,
not overlooked; tracked separately so it is not lost.

No tracked file becomes ignored by the new patterns: git ls-files
differs only by the .editorconfig relocation, and check-ignore over the
full tracked set matches nothing.
clawbot self-assigned this 2026-08-09 07:00:44 +02:00
Author
Collaborator

What I built

Three files changed, 32 insertions / 2 deletions, no code touched.

  1. backend/.editorconfig -> .editorconfig — recorded by git as a pure
    rename (R backend/.editorconfig -> .editorconfig, 0 content lines changed).
    The file already matched the org model exactly, so nothing was rewritten.
  2. .gitignore — the org model verbatim, plus # Build output / dist/ and
    # Logs / *.log re-appended so nothing this repo already relied on is lost.
  3. 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-files before and after, diffed:

1a2
> .editorconfig
14d14
< backend/.editorconfig

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:

git ls-files -z | git check-ignore --stdin -z --no-index -v
-> exit 1 (no matches)

Exit 1 from check-ignore means zero paths matched. I did not take that on
faith — 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 was
nothing at risk to begin with.

git status --short is empty at 4a7bdf8.

Gates.

  • Root make check: pass. script/test (vite build) transformed 4 modules
    and emitted dist/; script/lint and script/fmt-check both report
    All matched files use Prettier code style!.
  • cd backend && make check: pass. go test ./... green (handlers and
    reportbuf OK, rest no test files); golangci-lint run ./... reports
    0 issues.
  • make fmt was run before committing. Prettier listed TODO.md as
    (unchanged), so the markdown was already conformant rather than being
    reformatted under me.
  • All verification went through make / script/ entrypoints only.

What I did not do

.dockerignore is untouched — the .git bullet is deferred, with the reasoning
in the PR body and a matching comment on #15. Follow-up filed as #36, which
also picks up the frontend vite.config.js half of the git coupling that #17's
scope does not cover. No docker build was run because no Docker input changed.

Worked in a throwaway worktree, not the shared checkout; the worktree has been
removed. Branch is pushed.

## What I built Three files changed, 32 insertions / 2 deletions, no code touched. 1. **`backend/.editorconfig` -> `.editorconfig`** — recorded by git as a pure rename (`R backend/.editorconfig -> .editorconfig`, 0 content lines changed). The file already matched the org model exactly, so nothing was rewritten. 2. **`.gitignore`** — the org model verbatim, plus `# Build output` / `dist/` and `# Logs` / `*.log` re-appended so nothing this repo already relied on is lost. 3. **`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-files` before and after, diffed: ``` 1a2 > .editorconfig 14d14 < backend/.editorconfig ``` 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: ``` git ls-files -z | git check-ignore --stdin -z --no-index -v -> exit 1 (no matches) ``` Exit 1 from `check-ignore` means zero paths matched. I did not take that on faith — 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 was nothing at risk to begin with. `git status --short` is empty at `4a7bdf8`. **Gates.** - Root `make check`: **pass**. `script/test` (`vite build`) transformed 4 modules and emitted `dist/`; `script/lint` and `script/fmt-check` both report `All matched files use Prettier code style!`. - `cd backend && make check`: **pass**. `go test ./...` green (`handlers` and `reportbuf` OK, rest no test files); `golangci-lint run ./...` reports `0 issues.` - `make fmt` was run before committing. Prettier listed `TODO.md` as `(unchanged)`, so the markdown was already conformant rather than being reformatted under me. - All verification went through `make` / `script/` entrypoints only. ## What I did not do `.dockerignore` is untouched — the `.git` bullet is deferred, with the reasoning in the PR body and a matching comment on #15. Follow-up filed as **#36**, which also picks up the frontend `vite.config.js` half of the git coupling that #17's scope does not cover. No `docker build` was run because no Docker input changed. Worked in a throwaway worktree, not the shared checkout; the worktree has been removed. Branch is pushed.
Author
Collaborator

Review of PR #35 (head 4a7bdf8)

Verdict: PASS

Independently verified in a throwaway worktree at 4a7bdf8. Nothing in the PR
branch or the shared checkout was modified.

Independently verified

Root .editorconfig

  • cmp .editorconfig /home/user/dev/prompts/.editorconfig — exit 0, byte-identical. Not eyeballed.

  • backend/.editorconfig is gone; git records the change as R100 backend/.editorconfig -> .editorconfig, i.e. a pure rename with zero content lines changed.

  • The [Makefile]-reaches-backend/Makefile claim 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 at 4a7bdf8:

    • backend/Makefile -> indent_style = tab
    • Makefile -> indent_style = tab
    • src/main.js, script/check, backend/internal/server/server.go -> indent_style = space, indent_size = 4

    So backend/Makefile keeps its tab rule (no regression), and the root Makefile gains one it never had. This matches the spec rule that a section name containing no / may match at any level below the .editorconfig file.

.gitignore

  • diff against /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.
  • All four lines of the previous 4-line file (node_modules/, dist/, .DS_Store, *.log) survive.
  • .env, .env.*, *.pem, *.key, Thumbs.db, and the full Editors section are present.
  • Functional test, not a read-through. Created scratch .env, .env.production, foo.pem, secret.key, Thumbs.db, and .vscode/settings.json at the worktree root: git status --short offered none of them, and git check-ignore -v attributed 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-only at fbfe1df vs 4a7bdf8: 55 files before, 55 after. Full delta is -backend/.editorconfig / +.editorconfig and nothing else.
  • Full tracked set through the ignore matcher: git ls-files -z | git check-ignore --stdin -z --no-index -v exits 1 with zero matched paths.
  • I did not trust that empty result. Sanity-checked the identical invocation against foo/.env, build/x.pem, and src/.vscode/settings.json first; 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 .dockerignore bullet

  • .dockerignore is genuinely unmodified: git diff fbfe1df..HEAD -- .dockerignore is empty. It still reads node_modules, dist, .DS_Store, *.log, .claude — untouched, so the pre-existing .claude entry stays #28's business, as stated.
  • The stated blocker is real, both halves, confirmed by reading the files at head:
    • Dockerfile.backend:12 is literally COPY .git /repo/.git, and backend/Makefile:2 is VERSION := $(shell git describe --always --dirty). With .git ignored that COPY has no source and the build errors — a hard failure, not a degraded version string, as claimed.
    • Dockerfile:7 is COPY . . followed by RUN make check at line 11; vite.config.js:5-6 are execSync("git rev-parse --short HEAD") and execSync("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 .git via COPY . . rather than copying it deliberately is also correct.
  • Disclosed in both required places: PR body section 3, and the deferral comment on #15. Neither ticks the box quietly.

Gates

  • Root make check — pass. vite build transformed 4 modules; prettier --check . clean for both the lint and fmt-check steps. make fmt is therefore clean (fmt-check is the same prettier invocation and TODO.md is not in .prettierignore).
  • cd backend && make check — pass. go test ./... green, golangci-lint run ./... reports 0 issues.
  • Run through make / script/ entrypoints only.
  • CI on 4a7bdf8: check / check (push) success.

Commit and merge hygiene

  • Exactly one commit above fbfe1df.
  • Title: chore: root .editorconfig and hardened .gitignore (closes #15) — ends with the required (closes #15).
  • origin/main is still fbfe1df; head is a direct descendant, and git merge-tree against origin/main reports no conflict. Fast-forwardable.
  • No attribution trailers. Grepped the diff, the raw commit object (including author/committer identity), and the PR body — no vendor or assistant references, no Co-Authored-By, no session links. The .claude mentions in the PR body and .prettierignore/.dockerignore are pre-existing filenames tracked in #28 and are not touched by this PR.
  • No scope creep: three files, all inside #15's scope. backend/.gitignore correctly left alone.

Follow-up scoping (#36)

  • Checked #36's central claim against #17's actual text, not its summary. #17's definition of done contains COPY .git /repo/.git is gone, version from ARG VERSION, and backend/Makefile tolerating git describe being unavailable — the backend half only. #17 contains no mention of vite.config.js, the frontend Dockerfile, or the .dockerignore line itself; it only notes in passing that the .git copy blocks the .dockerignore change. The claim is accurate: the frontend coupling and the .dockerignore entry 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)

  1. TODO.md Future 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 .editorconfig half, and the hooks target already existed in the root Makefile at fbfe1df (line 34), so the bullet was already half-stale on main and 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 rewrites Status and Next Step, not Future Steps. The conflict-avoidance rationale is sound for the sections it actually covers — I confirmed #31 does rewrite Status and Next Step, so leaving those alone was the right call — but it does not extend to Future Steps.

  2. 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)

  • The TODO.md Completed Steps entry is accurate on every claim it makes, including the deferral reason, and is additive at the top of the section as described.
  • Backend Go files resolve to indent_style = space under the root .editorconfig, which fights gofmt. This is zero deltabackend/.editorconfig was 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.
## Review of PR #35 (head `4a7bdf8`) ## Verdict: PASS Independently verified in a throwaway worktree at `4a7bdf8`. Nothing in the PR branch or the shared checkout was modified. ## Independently verified **Root `.editorconfig`** - `cmp .editorconfig /home/user/dev/prompts/.editorconfig` — exit 0, byte-identical. Not eyeballed. - `backend/.editorconfig` is gone; git records the change as `R100 backend/.editorconfig -> .editorconfig`, i.e. a pure rename with zero content lines changed. - **The `[Makefile]`-reaches-`backend/Makefile` claim 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 at `4a7bdf8`: - `backend/Makefile` -> `indent_style = tab` - `Makefile` -> `indent_style = tab` - `src/main.js`, `script/check`, `backend/internal/server/server.go` -> `indent_style = space, indent_size = 4` So `backend/Makefile` keeps its tab rule (no regression), and the root `Makefile` gains one it never had. This matches the spec rule that a section name containing no `/` may match at any level below the `.editorconfig` file. **`.gitignore`** - `diff` against `/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. - All four lines of the previous 4-line file (`node_modules/`, `dist/`, `.DS_Store`, `*.log`) survive. - `.env`, `.env.*`, `*.pem`, `*.key`, `Thumbs.db`, and the full Editors section are present. - **Functional test, not a read-through.** Created scratch `.env`, `.env.production`, `foo.pem`, `secret.key`, `Thumbs.db`, and `.vscode/settings.json` at the worktree root: `git status --short` offered none of them, and `git check-ignore -v` attributed 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-only` at `fbfe1df` vs `4a7bdf8`: 55 files before, 55 after. Full delta is `-backend/.editorconfig` / `+.editorconfig` and nothing else. - Full tracked set through the ignore matcher: `git ls-files -z | git check-ignore --stdin -z --no-index -v` exits 1 with zero matched paths. - **I did not trust that empty result.** Sanity-checked the identical invocation against `foo/.env`, `build/x.pem`, and `src/.vscode/settings.json` first; 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 `.dockerignore` bullet** - `.dockerignore` is genuinely unmodified: `git diff fbfe1df..HEAD -- .dockerignore` is empty. It still reads `node_modules`, `dist`, `.DS_Store`, `*.log`, `.claude` — untouched, so the pre-existing `.claude` entry stays #28's business, as stated. - The stated blocker is real, both halves, confirmed by reading the files at head: - `Dockerfile.backend:12` is literally `COPY .git /repo/.git`, and `backend/Makefile:2` is `VERSION := $(shell git describe --always --dirty)`. With `.git` ignored that `COPY` has no source and the build errors — a hard failure, not a degraded version string, as claimed. - `Dockerfile:7` is `COPY . .` followed by `RUN make check` at line 11; `vite.config.js:5-6` are `execSync("git rev-parse --short HEAD")` and `execSync("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 `.git` via `COPY . .` rather than copying it deliberately is also correct. - Disclosed in both required places: PR body section 3, and the deferral comment on #15. Neither ticks the box quietly. **Gates** - Root `make check` — pass. `vite build` transformed 4 modules; `prettier --check .` clean for both the lint and fmt-check steps. `make fmt` is therefore clean (fmt-check is the same prettier invocation and `TODO.md` is not in `.prettierignore`). - `cd backend && make check` — pass. `go test ./...` green, `golangci-lint run ./...` reports `0 issues.` - Run through `make` / `script/` entrypoints only. - CI on `4a7bdf8`: `check / check (push)` **success**. **Commit and merge hygiene** - Exactly one commit above `fbfe1df`. - Title: `chore: root .editorconfig and hardened .gitignore (closes #15)` — ends with the required ` (closes #15)`. - `origin/main` is still `fbfe1df`; head is a direct descendant, and `git merge-tree` against `origin/main` reports no conflict. Fast-forwardable. - No attribution trailers. Grepped the diff, the raw commit object (including author/committer identity), and the PR body — no vendor or assistant references, no `Co-Authored-By`, no session links. The `.claude` mentions in the PR body and `.prettierignore`/`.dockerignore` are pre-existing filenames tracked in #28 and are not touched by this PR. - No scope creep: three files, all inside #15's scope. `backend/.gitignore` correctly left alone. **Follow-up scoping (#36)** - Checked #36's central claim against #17's actual text, not its summary. #17's definition of done contains `COPY .git /repo/.git` is gone, version from `ARG VERSION`, and `backend/Makefile` tolerating `git describe` being unavailable — the backend half only. #17 contains no mention of `vite.config.js`, the frontend `Dockerfile`, or the `.dockerignore` line itself; it only notes in passing that the `.git` copy blocks the `.dockerignore` change. **The claim is accurate:** the frontend coupling and the `.dockerignore` entry 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) 1. **`TODO.md` `Future 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 `.editorconfig` half, and the `hooks` target already existed in the root `Makefile` at `fbfe1df` (line 34), so the bullet was already half-stale on `main` and 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 rewrites `Status` and `Next Step`, not `Future Steps`. The conflict-avoidance rationale is sound for the sections it actually covers — I confirmed #31 does rewrite `Status` and `Next Step`, so leaving those alone was the right call — but it does not extend to `Future Steps`. 2. **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) - The `TODO.md` `Completed Steps` entry is accurate on every claim it makes, including the deferral reason, and is additive at the top of the section as described. - Backend Go files resolve to `indent_style = space` under the root `.editorconfig`, which fights `gofmt`. This is **zero delta** — `backend/.editorconfig` was 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.
clawbot added merge-ready and removed needs-review labels 2026-08-09 07:07:49 +02:00
clawbot removed their assignment 2026-08-09 07:07:54 +02:00
sneak was assigned by clawbot 2026-08-09 07:07:54 +02:00
Author
Collaborator

Manager note — merge-ready

Independent adversarial review returned PASS with no blocking findings. Relabelled needs-review -> merge-ready and assigned to @sneak for the merge, since main is protected.

Why I am accepting this

The two findings that could have made this dangerous were both tested rather than reasoned about:

  • "No tracked file became ignored" is the way a .gitignore change does real damage, and it was verified as a validated negative. The reviewer ran the sanity check first — confirming foo/.env, build/x.pem, and src/.vscode/settings.json are 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 .editorconfig relocation.
  • The "[Makefile] matches at any depth" claim was checked empirically against the reference editorconfig implementation, not just read off the spec. backend/Makefile resolves to indent_style = tab, and the frontend files resolve to space/4. Had that claim been wrong, backend/Makefile would have silently lost its tab rule — a subtle regression that would have surfaced later as a mangled Makefile. It is correct, and the root Makefile actually 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.json were each confirmed withheld and attributed to the expected rule.

The deferral is correct and I am endorsing it

.dockerignore is genuinely unmodified, and the reviewer independently confirmed both halves of the stated blocker: Dockerfile.backend:12 COPY .git /repo/.git feeding git describe, and vite.config.js:5-6 calling execSync("git rev-parse ...") at module scope behind Dockerfile's COPY . . + 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.0 and sequenced after #17. The vite.config.js half 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 .git incidentally. Good catch by the implementer.

Two minor findings I am not sending back for

  • TODO.md's first Future Steps bullet is now fully stale ("add .editorconfig and add the hooks target to the Makefile"). The .editorconfig half is done by this PR; the hooks half was already wrong on main, since the root Makefile has had hooks: @script/install-precommit for 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 to Future Steps, which #31 does not touch. Recorded on #24, which owns TODO.md accuracy.
  • The PR body promises to link the follow-up issue and never did. It is #36. Cosmetic; not worth a rework cycle.

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.

## Manager note — merge-ready Independent adversarial review returned **PASS** with no blocking findings. Relabelled `needs-review` -> `merge-ready` and assigned to @sneak for the merge, since `main` is protected. ### Why I am accepting this The two findings that could have made this dangerous were both tested rather than reasoned about: - **"No tracked file became ignored"** is the way a `.gitignore` change does real damage, and it was verified as a *validated* negative. The reviewer ran the sanity check first — confirming `foo/.env`, `build/x.pem`, and `src/.vscode/settings.json` are 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 `.editorconfig` relocation. - **The "`[Makefile]` matches at any depth" claim** was checked empirically against the reference editorconfig implementation, not just read off the spec. `backend/Makefile` resolves to `indent_style = tab`, and the frontend files resolve to space/4. Had that claim been wrong, `backend/Makefile` would have silently lost its tab rule — a subtle regression that would have surfaced later as a mangled Makefile. It is correct, and the root `Makefile` actually *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.json` were each confirmed withheld and attributed to the expected rule. ### The deferral is correct and I am endorsing it `.dockerignore` is genuinely unmodified, and the reviewer independently confirmed both halves of the stated blocker: `Dockerfile.backend:12` `COPY .git /repo/.git` feeding `git describe`, and `vite.config.js:5-6` calling `execSync("git rev-parse ...")` at module scope behind `Dockerfile`'s `COPY . .` + `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.0` and sequenced after #17. **The `vite.config.js` half 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 `.git` incidentally. Good catch by the implementer. ### Two minor findings I am not sending back for - **`TODO.md`'s first `Future Steps` bullet is now fully stale** ("add .editorconfig and add the hooks target to the Makefile"). The `.editorconfig` half is done by this PR; the `hooks` half was *already* wrong on `main`, since the root `Makefile` has had `hooks: @script/install-precommit` for 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 to `Future Steps`, which #31 does not touch. Recorded on #24, which owns `TODO.md` accuracy. - **The PR body promises to link the follow-up issue and never did.** It is #36. Cosmetic; not worth a rework cycle. ### 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.
Author
Collaborator

Checked against the cached-green risk — merge-ready stands

A fleet-wide warning landed after I labelled this: script/cibuild is a bare docker build ., so on an unchanged tree Docker can serve RUN make check from cache and exit 0 having run nothing. I reproduced that hole in this repo (514ms, 9 cached layers, make check cached) and filed it as #37.

It does not undermine this PR, for two independent reasons:

  1. This PR's gates were direct host runs, not Docker builds. Root make check and cd backend && make check were executed on the host by both the implementer and the reviewer. Those invoke script/check directly — there is no Docker layer cache in that path to serve a stale result from.

  2. This PR's changes necessarily bust the cache anyway. It adds .editorconfig at the root and rewrites .gitignore, both inside the build context. COPY . . therefore produces a different layer, which invalidates the RUN make check layer 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 .dockerignore is 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-cache build there and confirmed the checks genuinely execute (74s, real go test timings, 0 issues.). Both PRs remain merge-ready on earned evidence.

## Checked against the cached-green risk — `merge-ready` stands A fleet-wide warning landed after I labelled this: `script/cibuild` is a bare `docker build .`, so on an unchanged tree Docker can serve `RUN make check` from cache and exit 0 having run nothing. I reproduced that hole in this repo (514ms, 9 cached layers, `make check` cached) and filed it as **#37**. It does not undermine this PR, for two independent reasons: 1. **This PR's gates were direct host runs, not Docker builds.** Root `make check` and `cd backend && make check` were executed on the host by both the implementer and the reviewer. Those invoke `script/check` directly — there is no Docker layer cache in that path to serve a stale result from. 2. **This PR's changes necessarily bust the cache anyway.** It adds `.editorconfig` at the root and rewrites `.gitignore`, both inside the build context. `COPY . .` therefore produces a different layer, which invalidates the `RUN make check` layer 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 `.dockerignore` is 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-cache` build there and confirmed the checks genuinely execute (74s, real `go test` timings, `0 issues.`). Both PRs remain `merge-ready` on earned evidence.
All checks were successful
check / check (push) Successful in 26s
Required
Details
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin chore/dotfile-compliance:chore/dotfile-compliance
git checkout chore/dotfile-compliance
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/netwatch#35