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. Coverage is not limited to the three patterns the issue names, because the enumeration found more shapes reaching the image. `**/*.env` covers the prod.env / local.env convention, which the .env and .env.* spellings miss entirely; `**/.envrc` is a secrets file by direnv convention; `**/*.p12` and `**/*.pfx` are bundles carrying private keys; and `**/id_rsa`, `**/id_dsa`, `**/id_ecdsa`, `**/id_ed25519` are the extensionless SSH keys that were covered before only when someone happened to append a .key suffix. Matching is case-sensitive, so `**/*.key` does not match certs/SERVER.KEY, which is reachable on the case-insensitive filesystems most laptops use. Doubling each pattern with an ALL-CAPS twin is not the fix: measured against the planted set it still ships certs/Server.Key and certs/Ca.Pem while reading as though case were handled, which is this issue's failure mode restated in a new place. filepath.Match supports character ranges, so the secret-material extensions use `**/*.[kK][eE][yY]`, `**/*.[pP][eE][mM]`, `**/*.[pP]12` and `**/*.[pP][fF][xX]`, covering every capitalisation in one line each. Names that exist in exactly one spelling because a tool writes them -- .env, .envrc, id_rsa -- stay literal. Deliberately not covered: bare `key` and `pem` filenames, which no tool produces and which collide with legitimate paths (a `**/key` pattern would delete an internal/key/ package directory from the context); `**/id_*`, which would match ordinary source such as id_generator.go; and *.crt and *.cer, which are public certificates rather than secrets and are sometimes a legitimate build input. Each of those is planted as a positive control and verified present in the image after the change. 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. That comment gives the anchored form explicitly, `/myapp` rather than `**/myapp`, because the prefixed spelling also matches cmd/myapp/ and deletes the package directory. The header comment is the only part of this guidance a consuming repo actually receives, since it is vendored with the file. .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 case-sensitivity rule and 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 requirements, since they are what an agent reads while extending the file. Verified by planting twenty-eight secret shapes at the root and up to three directories below it -- including CA.PEM, SERVER.KEY, Server.Key, Ca.Pem, config/prod.env, config/local.env, deploy/secrets/.envrc, extensionless SSH keys and PKCS#12 bundles -- alongside seven positive controls, then building a standalone probe image doing `COPY . .` and listing what actually landed inside it. Before: nineteen of the twenty-eight in the image. Against three naive forms: unprefixed leaks twenty-one, `**/`-prefixed lowercase-only leaks six, ALL-CAPS-doubled still leaks two. After: zero, with all seven controls still present, including internal/key/key.go, pkg/pem/decode.go, internal/id_generator.go and certs/ca.crt. Transferred-context size is recorded but load-bearing on nothing, and the runs show why: a 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.
prompts
prompts is an MIT-licensed collection of LLM prompts by @sneak, including development policy prompts and other useful prompts for working with large language models.
Quick Start
Existing Repo
Run from within the repo you want to bring up to standards. Clone the prompts repo once, then run both commands in order.
export TD="$(mktemp -d)"
git clone --depth 1 https://git.eeqj.de/sneak/prompts.git "$TD"
Repository structure and policies:
claude "Read $TD/prompts/REPO_POLICIES.md and
$TD/prompts/EXISTING_REPO_CHECKLIST.md, then bring this repo up to those
standards. Your scope is repo scaffolding and policy compliance:
Makefile, Dockerfile, .dockerignore, .gitignore, .editorconfig, CI
workflow, README sections, LICENSE, REPO_POLICIES.md, and any
language-specific config files (.golangci.yml, .prettierrc, etc.).
You must also run the formatter (make fmt) and fix any linter errors
(make lint) so that make check passes — this will touch source code,
but do not restructure, refactor, or rewrite any application logic.
Follow the policies yourself: work on a feature branch, never git add -A,
and make each logical change a separate commit (e.g. one commit for
formatting, one for linter fixes, one for README updates, one for each
new repo file added, etc.)."
Code style and conventions:
claude "Read $TD/prompts/CODE_STYLEGUIDE.md and whichever
language-specific styleguides in $TD/prompts/ apply to this repo
(CODE_STYLEGUIDE_GO.md, CODE_STYLEGUIDE_JS.md, CODE_STYLEGUIDE_PYTHON.md,
GO_HTTP_SERVER_CONVENTIONS.md). Then review the application code in this
repo and bring it into compliance with those coding standards. Your scope
is application code structure and style: naming, patterns, error
handling, project layout, and conventions described in the styleguides.
Do not modify repo scaffolding (Makefile, Dockerfile, CI workflow,
.gitignore, .editorconfig, etc.) — only application code. Work on a
feature branch, never git add -A, and make each logical change a
separate commit."
New Repo
Run from inside the directory where you want to create a new repo. Clone the prompts repo once, then run both commands in order.
export TD="$(mktemp -d)"
git clone --depth 1 https://git.eeqj.de/sneak/prompts.git "$TD"
Repository scaffolding:
claude "Read $TD/prompts/REPO_POLICIES.md and
$TD/prompts/NEW_REPO_CHECKLIST.md, then set up this new repo according
to those standards. Your scope is repo structure and required files:
README.md, LICENSE, REPO_POLICIES.md, Makefile, Dockerfile, .dockerignore,
.gitignore, .editorconfig, CI workflow, and language-specific config.
Run the formatter (make fmt) and fix any linter errors (make lint) so
that make check passes — this will touch source code, but do not
restructure, refactor, or rewrite any application logic. Follow the
policies yourself: work on a feature branch, never git add -A, and make
each logical change a separate commit (e.g. one commit for formatting,
one for linter fixes, one for README, one for each new repo file, etc.)."
Code style and conventions:
claude "Read $TD/prompts/CODE_STYLEGUIDE.md and whichever
language-specific styleguides in $TD/prompts/ apply to this repo
(CODE_STYLEGUIDE_GO.md, CODE_STYLEGUIDE_JS.md, CODE_STYLEGUIDE_PYTHON.md,
GO_HTTP_SERVER_CONVENTIONS.md). Then review the application code in this
repo and bring it into compliance with those coding standards. Your scope
is application code structure and style: naming, patterns, error
handling, project layout, and conventions described in the styleguides.
Do not modify repo scaffolding (Makefile, Dockerfile, CI workflow,
.gitignore, .editorconfig, etc.) — only application code. Work on a
feature branch, never git add -A, and make each logical change a
separate commit."
Getting Started
git clone https://git.eeqj.de/sneak/prompts.git
cd prompts
Prompts are stored as Markdown files in prompts/. Copy or reference them as
needed in your projects.
Entrypoints
This repository adheres to the
Scripts to Rule Them All
standard: normalized scripts in script/ are the entrypoints for the
development workflow, and the Makefile targets are thin shims that call them.
The scripts are POSIX sh (not bash) so they run in minimal containers such as
alpine. We provide:
script/bootstrap— install all dependencies (yarn install)script/setup— set up the repo for development after a fresh clone: runsscript/bootstrap, thenscript/install-precommitscript/projectname— output the project name (our own extension); used byscript/dockerfor the image tagscript/test— run the test suite (no tests defined here)script/lint— lint the markdown files with prettierscript/fmt— format all markdown files with prettier (writes)script/fmt-check— check formatting (read-only)script/check— run all checks:test,lint,fmt-check(our own extension)script/docker— build the Docker image, tagged viascript/projectname(byte-identical across repos); passes the sameCHECK_EPOCHnonce asscript/cibuildscript/cibuild— cd to the repo root, assignepoch="$(date +%s%N)$$", thendocker build --build-arg CHECK_EPOCH="$epoch" .(what CI runs; the image build runsscript/check, and the per-invocationCHECK_EPOCHnonce is what stops Docker serving that check from cache on an unchanged tree — a baredocker build .fails closed on purpose)script/precommit— run by the git pre-commit hook (our own extension); callsscript/checkscript/install-precommit— installs the git pre-commit hook (our own extension);make hooksshims to it
make hooks installs the pre-commit hook that runs script/precommit.
Rationale
LLM prompts, especially development policies, benefit from version control and a single authoritative source. This repo provides a central place to maintain, share, and evolve prompts across projects.
Design
The repository is a collection of Markdown files organized in the prompts/
subdirectory. Each file contains one or more related prompts or policy
documents. There is no build step or runtime component; the prompts are consumed
by copying them into other projects or referencing them directly.
Template Repos
These template repositories implement the policies defined in this repo and serve as starting points for new projects. They must be kept in sync when policies change.
- template-app-go — Go HTTP server template (Uber fx, chi, SQLite, session auth, Prometheus metrics)
- template-app-js — JavaScript SPA template (Vite, Tailwind CSS v4, nginx Docker deployment)
- template-app-python — Python web application template (FastAPI, uvicorn, pytest, black, ruff)
When updating policies in this repo, also update the template repos to match (Makefile targets, Dockerfile conventions, CI workflows, required files, etc.).
See Also
- clawpub — Real-world examples, rationale, and operational lessons from applying these policies with an OpenClaw AI agent. Includes detailed documentation on how the interlocking check system (CI → Docker → Makefile → tests/lint/fmt) works in practice, why checklists complement prose policies, and failure stories from production use.
TODO
- Add more prompt templates for common development tasks
License
MIT. See LICENSE.