Files
prompts/prompts/EXISTING_REPO_CHECKLIST.md
T
clawbot 4004bbc897
check / check (push) Successful in 36s
Derive the image version from git; send .git without its config (closes #69, closes #71)
The canonical documents told every repo to exclude .git from the build
context, default ARG VERSION to dev and never run git describe in a
build stage, so an image built from a clone with no build argument
reported dev. .dockerignore now sends .git but keeps out .git/config,
which can hold a credential. The Dockerfile example installs git, takes
the VERSION build argument when one is given and otherwise
git describe --tags --always, and fails when .git exists but the version
is empty, dev or unknown. The policy and both checklists state the rule
in the same words, including that a plain docker build . with no build
arguments must succeed.

Model: opus-5-5
2026-10-02 01:26:47 +00:00

10 KiB

title, last_modified
title last_modified
Existing Repo Checklist 2026-10-02

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 fmt and 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.md exists with all required sections (Description, Getting Started, Rationale, Design, TODO, License, Author)
  • LICENSE file exists and matches the README
  • REPO_POLICIES.md exists and version date is current — fetch from https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md
  • .gitignore is comprehensive (OS, editor, agent scratch, language artifacts, secrets) — fetch from https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitignore if missing. An existing repo usually has a hand-written one that is never re-fetched, so check the entries rather than the file's presence.
  • .editorconfig exists — fetch from https://git.eeqj.de/sneak/prompts/raw/branch/main/.editorconfig
  • Dockerfile and .dockerignore exist; the Dockerfile carries a lint phase and a test phase, and the final stage carries a COPY --from= of a harmless file from each — fetch .dockerignore from https://git.eeqj.de/sneak/prompts/raw/branch/main/.dockerignore
  • Nothing has been appended after the final stage, and the gate phases are reachable from it. A stage nothing depends on is built only when --target names it, so a lost COPY --from= edge leaves docker build . passing while the gate never runs. Confirm by planting a violation, not by reading the file.
  • The gate phases invoke their tools directly, never through make lint or script/test — those are themselves a docker build and would recurse inside a build step
  • Every depth-independent pattern in .dockerignore carries a **/ prefix, only genuinely root-anchored entries such as .claude are unprefixed, and .gitignore's patterns have not been transplanted unmodified — the transplanted form leaves config/.env and certs/server.key in the build context while reading as solved
  • .dockerignore excludes the repo's own host-built artifacts (compiled binaries, test binaries, coverage output), written root-anchored — /myapp, never **/myapp. An existing repo is where such a binary is likeliest to already be sitting in the build context, invisible to git.
  • .claude/ is in .gitignore (unanchored) and .claude in .dockerignore (anchored, no **/ prefix). Agent worktrees are entire checkouts of the repo, so they inflate the context by a multiple of it and can copy another session's unreviewed work into an image layer. If agents here run anywhere other than the repo root, the anchored entry misses services/api/.claude/: add anchored entries for those directories.
  • If the repo embeds a version in a binary: .dockerignore lets .git into the build context. It keeps out .git/config, which git describe does not need and which can hold a credential: a password in a remote URL, or the token the CI checkout step stores there. The stage that compiles has git (the Debian Go image has it; an alpine one needs apk add --no-cache git) and takes the version from the VERSION build argument when one is given, otherwise from git describe --tags --always. That gives the tag on a tagged commit; on a later commit, the tag, the number of commits since it and the short commit (v1.2.3-4-gabc1234); and the short commit when no tag is reachable. ARG VERSION has no default, and the build fails if the context carries .git and the version still comes out empty, dev or unknown. A plain docker build . with no build arguments must succeed; a Dockerfile that refuses an empty build argument drops that refusal and keeps the argument. script/docker and script/cibuild pass the version they compute on the host; it takes precedence. A tag-derived version additionally needs fetch-depth: 0 on the CI checkout step, which clones shallow and fetches no tags by default.
  • Gitea Actions workflow in .gitea/workflows/ runs script/cibuild on push — reference https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitea/workflows/check.yml
  • Language-specific config:
    • Go: go.mod, go.sum, .golangci.yml (fetch from https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml)
    • JS: package.json, yarn.lock, .prettierrc, .prettierignore (fetch from https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierrc and https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierignore)
    • Python: pyproject.toml
    • Docs/writing: .prettierrc, .prettierignore (same URLs as above)

Makefile and script/ Entrypoints

  • Makefile exists in root — reference https://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 at https://git.eeqj.de/sneak/prompts/raw/branch/main/script/<name>
  • script/precommit exists and the pre-commit hook (installed by script/install-precommit, shimmed by make hooks) runs it
  • README has an Entrypoints section documenting the script/ entrypoints and linking the standard
  • script/lint and script/test build their phase by name (docker build --no-cache --target <phase> -t <name>-<phase> .), and no host invocation anywhere in the repo can produce a lint verdict — grep for the linter's own name across script/, the Makefile and CI config, not just script/lint. A second path is likeliest here: a make lint-fast, an older host-versus-container branch, or a CI step calling the binary directly. script/fmt and script/fmt-check are expected hits and stay on the host.
  • Every docker build in script/ is tagged — an untagged one leaves a dangling image behind on every run, on every host and CI runner
  • script/cibuild runs script/bootstrap before script/check, and builds the image with --no-cache. Without the bootstrap the CI run dies in script/fmt-check, which runs the formatter on the host and finds nothing installed.
  • script/fmt and script/fmt-check source nvm for the pinned node version before invoking yarn, as script/bootstrap's own install step does. script/bootstrap leaves the node and yarn it installs off the PATH of the shell that called it, so a bare yarn exits 127 on a runner carrying nothing but docker and git.
  • script/bootstrap installs no linter of its own — delete the block, its version variables and its call site. A JS repo's yarn install stays; it brings a linter along with every other dependency, and no verdict is taken from it.
  • make check does not modify any files in the repo
  • make test has a 90-second timeout and completes within the 60-second hard cap (over 20 seconds is green but must be filed as an improvement bug)
  • make test runs real tests, not a no-op (at minimum, import/compile check)
  • make check passes 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, not npm (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 check passes
  • script/cibuild succeeds in a fresh clone on a host carrying nothing but docker and git, with no node or yarn on PATH, which is what CI has, and demonstrably executed the checks — a sub-second build, or CACHED on a gate layer, means nothing ran
  • A planted lint violation fails both make lint and a plain docker build .; revert it afterwards
  • Commit and merge fixes before starting your actual task