All checks were successful
check / check (push) Successful in 8s
The canonical .dockerignore was three lines -- .git, node_modules, .DS_Store -- while the canonical Dockerfile does `COPY . .`, so a developer's local .env, *.pem or *.key was shipped into the build context and could land in an image layer. Nothing surfaced it because .gitignore covers those patterns, so the files are invisible to every git-based check. The obvious repair, copying .gitignore's secret patterns across, is worse than the gap it closes. .dockerignore does not use .gitignore semantics: Docker matches with Go filepath.Match, `*` does not cross `/`, and a pattern without a leading `**/` is anchored at the build-context root. A file listing .env, *.pem and *.key therefore reads as solved, reviews as solved, and protects only the repository root, while config/.env and certs/server.key still ship. The three-line file at least invited scrutiny; the transplanted form manufactures confidence and stops anyone looking. So every depth-independent pattern here carries the `**/` prefix and only genuinely root-anchored entries stay unprefixed. `**/node_modules` fixes a defect the three-line file had today for any nested node_modules, independently of the secret exposure. The OS and editor patterns are included on their own merits rather than by mirroring .gitignore. None of them is ever a build input, and editor state in particular churns under a developer's hands, so each one is a source of `COPY . .` invalidation carrying no information about the source tree. Now that the checks are keyed on CHECK_EPOCH rather than on accidental context churn, there is no reason left to keep churn in the context. Language build artifacts are deliberately absent: they are per-repo, and the file's header comment tells consuming repos to add their own host-built binaries, which is the case that actually bites -- a host `make build` drops a multi-megabyte artifact into the context where .gitignore hides it from every git-based check. .gitignore is untouched. Its semantics are the inverse: an unanchored pattern already matches at any depth, so `**/`-prefixing it produces a file that is wrong in a way that looks careful. That asymmetry is why "derive one from the other" was the wrong instruction, and it is now written down in REPO_POLICIES.md in both directions, together with the requirement to verify by enumerating the image rather than by reading the patterns. Every consuming repo inherits .dockerignore by copy, so the trap has to live where the next person looks, not only be fixed once here. Both repo checklists gain the same requirement, since they are what an agent reads while extending the file. Verified by planting .env, server.key and ca.pem at the root plus config/.env, config/.env.production, certs/ca.pem, certs/server.key, deploy/secrets/id_rsa.key, web/node_modules/nested/index.js and a nested .swp below it, then building a standalone probe image doing `COPY . .` and listing what actually landed inside it. Before: all eleven planted files in the image. Against the naive unprefixed form: the three root-level files excluded and every nested one still present, which is what shows the enumeration can detect the failure mode at all. After: every planted file excluded at every depth, with web/src/app.js still present to prove the probe was copying nested files rather than copying nothing. Transferred-context size is recorded but load-bearing on nothing, and the runs show why: the naive build reported 2.18kB transferred while 43 files, five of them secrets, were in the image. BuildKit transfers only the delta from the previous build, so the number describes the transfer and not the contents. Planted files were removed and their absence confirmed against the filesystem rather than against `git status`, which could not have seen them. `make docker` re-run after the change: the check layer executed rather than being served from cache, so the CHECK_EPOCH verification still holds under the altered build context.
5.7 KiB
5.7 KiB
title, last_modified
| title | last_modified |
|---|---|
| Existing Repo Checklist | 2026-08-09 |
Use this checklist when beginning work in a repo that may not yet conform to our
repository policies
(https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md).
Work on a feature branch. Check each item and fix any gaps before proceeding with your task.
Formatting (do this first)
- If the repo has never been formatted to our standards, run
make fmtand commit the result as a standalone branch/commit/PR before any other changes. Formatting diffs can be large and should not be mixed with functional changes.
Required Files
README.mdexists with all required sections (Description, Getting Started, Rationale, Design, TODO, License, Author)LICENSEfile exists and matches the READMEREPO_POLICIES.mdexists and version date is current — fetch fromhttps://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md.gitignoreis comprehensive (OS, editor, language artifacts, secrets) — fetch fromhttps://git.eeqj.de/sneak/prompts/raw/branch/main/.gitignoreif missing.editorconfigexists — fetch fromhttps://git.eeqj.de/sneak/prompts/raw/branch/main/.editorconfigDockerfileand.dockerignoreexist (fetch.dockerignorefromhttps://git.eeqj.de/sneak/prompts/raw/branch/main/.dockerignore); Dockerfile runsmake checkas a build step, and every stage containing a check-runningRUNdeclaresARG CHECK_EPOCHwith theRUN [ -n "$CHECK_EPOCH" ] || exit 1guard immediately below it — see theCHECK_EPOCHrule inREPO_POLICIES.md. Without them the check layer is served from cache on an unchanged tree and the build reports a green it never ran.- Every depth-independent pattern in
.dockerignorecarries a**/prefix; only genuinely root-anchored entries such as.gitare unprefixed, and.gitignore's patterns have not been transplanted unmodified..dockerignoreanchors an unprefixed pattern at the context root, so the transplanted form leavesconfig/.envandcerts/server.keyin the build context while reading as solved — see the.dockerignorerule inREPO_POLICIES.md. - Gitea Actions workflow in
.gitea/workflows/runsscript/cibuildon push — referencehttps://git.eeqj.de/sneak/prompts/raw/branch/main/.gitea/workflows/check.yml - Language-specific config:
- Go:
go.mod,go.sum,.golangci.yml(fetch fromhttps://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml) - JS:
package.json,yarn.lock,.prettierrc,.prettierignore(fetch fromhttps://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierrcandhttps://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierignore) - Python:
pyproject.toml - Docs/writing:
.prettierrc,.prettierignore(same URLs as above)
- Go:
Makefile and script/ Entrypoints
Makefileexists in root — referencehttps://git.eeqj.de/sneak/prompts/raw/branch/main/Makefile- Has targets:
test,lint,fmt,fmt-check,check,docker,hooks - Target implementations live in
script/(scripts-to-rule-them-all); Makefile targets are thin shims calling them — model scripts athttps://git.eeqj.de/sneak/prompts/raw/branch/main/script/<name> script/precommitexists and the pre-commit hook (installed byscript/install-precommit, shimmed bymake hooks) runs it- README has an Entrypoints section documenting the
script/entrypoints and linking the standard make checkdoes not modify any files in the repomake testhas a 30-second timeoutmake testruns real tests, not a no-op (at minimum, import/compile check)make checkpasses on current branch
Formatting
- Platform-standard formatter is configured (
black,prettier,go fmt) - Default formatter config, only exception: four-space indents (except Go)
- All files pass
make fmt-check
Git Hygiene
- Pre-commit hook is installed (
make hooks) - No secrets in the repo (
.env, keys, credentials) - No mutable references in Dockerfiles or scripts (tags,
@latest) — all pinned by cryptographic hash with version/date comment - Using
yarn, notnpm(JS projects)
Directory Structure
- No unnecessary files in repo root
- Files organized into canonical subdirectories (
bin/,cmd/,docs/,internal/,static/, etc.) - Go migrations in
internal/db/migrations/and embedded in binary
HTTP Service Hardening (if targeting 1.0 and the repo is an HTTP/web service)
- Security headers set on all responses (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy)
- Request body size limits enforced on all endpoints
- Read/write/idle timeouts configured on the HTTP server (slowloris defense)
- Per-handler execution time limits in place
- Password-based auth endpoints are rate-limited
- CSRF tokens on all state-mutating HTML forms
- Passwords hashed with bcrypt, scrypt, or argon2
- Session cookies use HttpOnly, Secure, and SameSite attributes
- True client IP correctly detected behind reverse proxy (trusted proxy allowlist configured)
- CORS restricted to explicit origin allowlist for authenticated endpoints
- Error responses do not leak stack traces, SQL queries, or internal paths
Final
make checkpassesscript/cibuildsucceeds (a baredocker build .fails closed by design, on theCHECK_EPOCHguard)- Commit and merge fixes before starting your actual task