Write CLAUDE.md as a short, checkable contract from the repo's scripts and CI

Claude Code reads the repo's scripts, CI and docs and writes a short CLAUDE.md in which every rule cites a source and can be checked in review.

Claude Code beginner Tested Opus 5.5 · Nov 2026May be dated

The prompt

Run it in a Claude Code session at the root of the repository. /init describes the repo; this writes rules with a source for each. It writes the files and leaves the commit to you.

Fill in the blanks3 placeholders · empty ones stay as {{…}}

Example

Input: run at the root of a small static-site repo: Markdown entries under content/, a validator and a shared loader in scripts/, a build script, site.config.json, CONTRIBUTING.md and one CI workflow. The run was read-only, so the file was written outside the repo, the commit-history scan was skipped and npm run build was not run. Values:

  • repo_purpose: A static site that builds a public library of tested prompts from Markdown files.
  • extra_rules: Ask before adding a category or a frontmatter field. Never edit LICENSE or LICENSE-CONTENT.
  • ai_tools: Claude Code only

Output (trimmed to two of the five Source of truth rows, two Hard rules, and parts of Never and When something fails from the 74-line file; then lines from the reply):

## Source of truth
| Topic | File | Rule |
|---|---|---|
| Entry rules | `scripts/content.mjs`; categories, labels: `site.config.json` | Win; update `CONTRIBUTING.md`, `TEMPLATE.md`, `README.md` in the same commit. |
| Copies | `staleAfterMonths` (`site.config.json`), `MAX_OPEN_URL` (`scripts/build.mjs`) | Keep `STALE_MONTHS` and `MAX_OPEN_URL` in `site/app.js` equal. |
…

## Hard rules
- Never write or edit an `## Example` output yourself: it is real, trimmed output of the prompt on `model_tested`.
…
- marked is v15: renderer overrides take a token object, `link({ href, title, tokens })`, not `(href, title, text)`.
…

## Never
- Edit `LICENSE`, `LICENSE-CONTENT`, `dist/`, `.wrangler/` or `node_modules/`; commit `.env*` or `.dev.vars*`.
…
- Act only when the owner asks for that exact action and names the target:
  - Push, merge or force-push to `main` (deploys production). Proof: Validate passes on `main`; the live page changed. Blocked: give the owner the PR link.
  - `npm run deploy` (`wrangler deploy` to production, skipping CI). Proof: the change is live. Blocked: hand the owner that command.
  - Delete or rename an entry or skill (removes `/p/<slug>/`, `/skills/<slug>.zip`). Proof: validate passes; no file names the old slug.

## When something fails
- Status: no status script; read the PR's Validate check and the Preview URL that Workers Builds posts on it.
- Done: after a push, report that Preview URL or the failing check (CI times out at 10 minutes), not "pushed".
- If `N problems. See CONTRIBUTING.md for the entry format.` (exit 1): fix each `error` line in the file it names; re-run.
…
- If `"date_tested" <d> is in the future.`: the check uses today's UTC date; set the UTC date of the real run.
…

Sources

  • …
  • Hard rules:
    • …
    • The marked rule comes from package.json (^15.0.12), node_modules/marked/lib/marked.d.ts line 194 and scripts/build.mjs lines 46–47. …

Proposed rules (left out of the file until you agree)

  • Status command. Use gh pr checks <n> --watch with a 10-minute timeout to match the CI job, and … The evidence is that the README only says the Preview URL is posted on the PR; there is no script to check it.
  • …
  • Node 22.2 or later. scripts/build.mjs imports crc32 from node:zlib, which first shipped in Node 22.2. package.json engines only says >=22.
  • …
  • Enforcing the hard-to-undo actions in .claude/settings.json. Use ask instead of deny if you want to approve each one at the prompt:
    • Push, merge or force-push to main: deny Bash(git push origin main:*), Bash(git push origin HEAD:main:*), Bash(git push --force:*), Bash(git push -f:*), Bash(gh pr merge:*) and mcp__github__merge_pull_request.
      • Prefix rules miss forms like git push origin main --force, so also add a PreToolUse hook on Bash.
    • …

Questions

  • …
  • Local or UTC date for date_tested? CONTRIBUTING.md says "the day you ran it", but the validator compares against today's UTC date. A run in a timezone ahead of UTC can fail as "in the future".
  • …
  • Which MAX_OPEN_URL wins? No file says whether the copy in scripts/build.mjs or the one in site/app.js is authoritative.

Each hard-to-undo action in the file carries a proof check, and the reply turns each one into a deny rule or hook. The marked rule has a source and went into the file, while the Node 22.2 floor, which no file states, went to Proposed rules, as did a status command because the repo has none.

Why it works

  • Inspect first, cite every line. From habit, Claude writes a plausible CLAUDE.md: npm test where the repo runs make check, rules nobody agreed to. A source for each line, and a separate "Proposed rules" list for everything else, keeps the file true and leaves anything new for the owner to approve. CLAUDE.md guides but doesn't enforce, so the reply also proposes a deny rule or hook for each hard-to-undo action.
  • Checkable rules in a small budget. A rule that names a path, a command or a visible result can be followed by an agent and checked in review. "Be careful" can't, and it still costs context in every session. A word and line-length cap stops rules being packed into 300-character lines that a line count misses. Procedures sit behind plain links (an @ import would load them anyway) and folder rules in .claude/rules/, so the contract stays short enough to read in full.
  • The sections hold what code can't show. Code shows how things work, not which folder is generated, which invariant a test guards, which library version broke the usual example, or which action can't be undone. Exact error text lets Claude match a failure to its fix. An exact command with its proof check, a "done" that means the deployed URL, and one block of commands for owner-only steps let Claude finish and check the work itself. "Ask first" plus a list of what may change freely stops both silent redesigns and needless questions.
  • Gaps become questions, and the file changes with the code. An unclear point logged in the roadmap stays a visible question, not an invented rule that later passes for a decision; once answered, it moves to Answered and into Settled decisions in one commit. Updating CLAUDE.md in the same commit as the command or path it names keeps it from going stale.

Variations

  • Several AI tools. Set ai_tools to the list ("Claude Code, Cursor and an image generator"). The contract moves to AGENTS.md, and non-coding tools get their own short section, such as "Adding images": where files go, how to name them, the check to run. CLAUDE.md becomes @AGENTS.md alone on its first line plus any Claude-only rules: an @ path followed by punctuation is not imported, so a sentence such as "Agents: see @AGENTS.md." loads nothing.
  • Audit instead of rewrite. Replace step 5 with: "Don't edit. For each existing rule say keep, fix or cut, with the reason, then list the missing sections."
  • Contract plus skill. For a long multi-step workflow (set up, build, release, confirm it is live), put the steps in a skill and start CLAUDE.md with "Step-by-step work: use the <skill> skill. These rules always apply:", then list only the rules.
  • Generalised from a deployment repo and a game prototype.