Files
prompts/prompts/NEW_REPO_CHECKLIST.md
sneak 51ee510ed8
All checks were successful
check / check (push) Successful in 23s
Gate the build on Docker lint and test phases (closes #40, closes #30)
Per the owner ruling on issue 40, linting and testing are phases of the
main Dockerfile rather than a separate lint file. script/lint and
script/test build one phase each by name with caching disabled, and the
final stage copies a harmless file from each so the image cannot be built
unless both passed. A stage that is not the last is built only when
something depends on it or --target names it, so the gates are invoked by
name and the edges kept. script/check runs the gates and builds no image
of its own; script/cibuild bootstraps first, because CI runs it alone and
fmt-check is native. fmt and fmt-check source nvm for the pinned node
before calling yarn, which bootstrap installs but leaves off its caller's
PATH. Every build in script/ is tagged and uncached. Issue 30 closes too:
a container has its own lint cache and lock.

Model: opus-5
2026-09-08 05:55:15 +00:00

8.1 KiB

title, last_modified
title last_modified
New Repo Checklist 2026-09-08

Use this checklist when creating a new repository from scratch. Follow the steps in order. Full policies are at https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md.

Template files can be fetched from: https://git.eeqj.de/sneak/prompts/raw/branch/main/<path>

1. Initialize

  • git init
  • Ask the user for the license (MIT, GPL, or WTFPL)

2. First Commit (README only)

  • Create README.md with all required sections:
    • Description: name, purpose, category, license, author
    • Getting Started: copy-pasteable code block
    • Rationale: why does this exist?
    • Design: how is it structured?
    • TODO: initial task list
    • License: matches chosen license
    • Author: @sneak
  • git add README.md && git commit

3. Scaffolding (feature branch)

  • git checkout -b initial-scaffolding

Fetch Template Files

  • .gitignore — fetch from https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitignore, extend for language-specific artifacts. Extensions are written to .gitignore's own semantics, where an unanchored pattern already matches at every depth: never add a **/ prefix here, which is a .dockerignore form. The canonical file already carries .claude/ so agent worktrees cannot be committed by accident.
  • .editorconfig — fetch from https://git.eeqj.de/sneak/prompts/raw/branch/main/.editorconfig
  • Makefile — fetch from https://git.eeqj.de/sneak/prompts/raw/branch/main/Makefile, adapt targets for the project's language and tools
  • For JS/docs repos: .prettierrc and .prettierignore — fetch from https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierrc and https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierignore

Create Project Files

  • LICENSE file matching the chosen license
  • REPO_POLICIES.md — fetch from https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md
  • Dockerfile and .dockerignore — fetch .dockerignore from https://git.eeqj.de/sneak/prompts/raw/branch/main/.dockerignore
    • Extend .dockerignore with the repo's own host-built artifacts, giving every depth-independent pattern a **/ prefix — but write a repo-root binary anchored, /myapp and never **/myapp, which would also match cmd/myapp/ and delete the package directory. Do not transplant .gitignore's patterns: .dockerignore anchors an unprefixed pattern at the context root, so the copied form leaves config/.env in the build context while reading as solved. The canonical file's .claude entry is anchored for the same reason as a repo-root binary; leave it that way, but note that it only covers agents running at the repo root — if this repo will run them in subdirectories, services/api/.claude/ needs its own anchored entry.
    • If the image embeds a version in a binary, the version is computed on the host and passed with --build-arg VERSION=..., and ARG VERSION=dev is declared in the stage that compiles. No stage calls git describe.dockerignore excludes .git, so it yields an empty version without failing the build.
    • The Dockerfile carries a lint phase and a test phase, each invoking its tool directly rather than through make or script/, and the final stage carries a COPY --from= of a harmless file from each so the image cannot be built unless both passed. Keep the final stage last: a stage nothing depends on is built only when --target names it.
    • Server: the final stage builds and runs the application
    • Non-server: the final stage brings up the dev environment
    • Image pinned by sha256 hash with version/date comment
  • Gitea Actions workflow at .gitea/workflows/check.yml that runs script/cibuild on push — reference https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitea/workflows/check.yml
  • Language-specific:
    • Go: go mod init sneak.berlin/go/<name>, .golangci.yml (fetch from https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml)
    • JS: yarn init, yarn add --dev prettier
    • Python: pyproject.toml

Configure script/ Entrypoints and Makefile

Implementations live in script/ (scripts-to-rule-them-all); Makefile targets are thin shims calling them. Model scripts: https://git.eeqj.de/sneak/prompts/raw/branch/main/script/<name>

  • scripts are POSIX sh (#!/bin/sh, set -eu, no bashisms) so they run on alpine images without bash
  • script/bootstrap / make bootstrap — installs all dependencies, idempotently, assuming nothing (pkg manager detection nix/apt/brew/apk; node used if present, else pinned version via nvm from a hash-verified archive; pinned yarn via corepack); Dockerfile runs it instead of inline installs
  • script/setup / make setup — readies a fresh clone: runs bootstrap, then install-precommit, plus repo-specific init
  • script/test / make testdocker build --no-cache --target test ., tagged; the phase runs real tests, not a no-op (90-second timeout, 60-second hard cap on wall time)
  • script/lint / make lintdocker build --no-cache --target lint ., tagged. No lint verdict may come from a host invocation of the linter.
  • script/fmt / make fmt — formats code (writes; native, never in a container)
  • script/fmt-check / make fmt-check — checks formatting (read-only; native)
  • script/check / make check — runs test, lint, fmt-check; must not modify files
  • script/projectname — outputs the project name (used by script/docker for the image tag)
  • script/docker / make docker — builds Docker image, tagged via script/projectname (byte-identical across repos); --no-cache, plus the version as a build arg
  • script/cibuild — cd to repo root, run script/bootstrap, run script/check, then docker build --no-cache --build-arg VERSION="$version" . (what CI runs). The bootstrap is required: CI checks out and runs this alone, and script/fmt-check runs the formatter on the host.
  • 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.
  • Every docker build in script/ is tagged, so no invocation leaves a dangling image behind
  • script/precommit — called by the pre-commit hook; runs script/check
  • script/install-precommit — installs the pre-commit hook that runs script/precommit
  • make hooks — shims to script/install-precommit
  • README Entrypoints section documents the scripts and links the standard

4. Verify

  • make check passes
  • make docker succeeds
  • 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
  • Plant a lint violation and confirm both make lint and a plain docker build . fail on it; revert. A plain build that passes proves the final stage is missing its COPY --from= edge to the gate phases.
  • No secrets in repo, and none in the build context: enumerate a probe image rather than reading .dockerignore
  • No mutable image/package references
  • No unnecessary files in repo root
  • All dates written as YYYY-MM-DD

5. Merge and Set Up

  • Commit, merge to main
  • make hooks to install pre-commit hook
  • Add remote and push
  • Verify main passes make check