Files
prompts/prompts/EXISTING_REPO_CHECKLIST.md
T
clawbot 61a9afbb4f
check / check (push) Waiting to run
Keep a submodule's own .git/config out of the build context (closes #88)
A submodule that keeps its own `.git` directory, instead of one under `.git/modules/`, still shipped `sub/.git/config` into the build context, credential included. Both git patterns in the canonical `.dockerignore` now start with `**/`: `**/.git/config` and `**/.git/modules/**/config`.

A submodule whose name has a `config` segment (`config`, `deploy/config`, `config/lib`) still loses its whole git directory, because the pattern also matches that directory, and Go's version stamping then fails the build loudly. The file records this as a known gap with the way around it, `git submodule add --name`; closing it needs a wildcard re-include that makes every build walk excluded directories. `prompts/REPO_POLICIES.md` and both checklists say the same.

Model: opus-5-5
2026-10-04 12:48:55 +02:00

11 KiB

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

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
  • Guidance for coding agents, if the repo has any, is one AGENTS.md at the root — never a file or directory named after one agent tool, such as CLAUDE.md or .claude/, and never separate memory files. Move what any such committed file says into AGENTS.md and delete it.
  • .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 every git config at any depth (**/.git/config, **/.git/modules/**/config): the repository's own, each submodule's under .git/modules/, and that of a submodule keeping its own .git directory. git describe does not need them, and each can hold a credential: a password in a remote URL, or the token the CI checkout step stores there. A submodule whose name has a config segment (config, deploy/config, config/lib) loses its whole git directory to **/.git/modules/**/config, and Go's version stamping then fails the build: give it a name without that segment (git submodule add --name). 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. The stage that compiles also marks its working directory safe for git (git config --system --add safe.directory /src): a context sent as a tar stream keeps the sender's file owners, and git refuses a checkout owned by another user, so the version would come out empty. 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 and, in the same commit, set the lint phase digest to the one named in the .golangci.yml paragraph of REPO_POLICIES.md)
    • 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