Code Quality Standards

patterns ~7956 tokens updated 2026-10-04

Code standards, testing requirements, error handling patterns, security practices, build verification, and living documents maintenance.

Tags

  • code-quality
  • testing
  • security
  • standards

README

code-quality

Code standards, testing requirements, error handling patterns, security practices, build verification, and living documents maintenance.

What It Does

This module installs a rules file that instructs Claude to:

  • Choose the simplest implementation that fully meets the current requirements, rather than building for requirements that do not exist yet
  • Climb the dependency ladder deliberately: built-in first, then an established library, and hand-rolled code only when neither fits
  • Delete backward-compatibility shims and migrate callers instead of keeping old shapes alive
  • Follow consistent code standards (environment variables, database migrations, dependencies, component patterns, path aliases)
  • Write tests for new features, edge cases, bug fixes, and complex logic
  • Apply structured error handling patterns for both frontend and backend
  • Enforce security practices (input sanitization, upload validation, secrets management, RLS)
  • Run full build verification before pushing code
  • Maintain living documents (README, project story) after PR merges

Manual Installation

Copy rules/code-quality.md into your Claude configuration:

# Global (all projects)
mkdir -p ~/.claude/rules
cp rules/code-quality.md ~/.claude/rules/code-quality.md
cp rules/change-philosophy.md ~/.claude/rules/change-philosophy.md
cp rules/latent-vs-deterministic.md ~/.claude/rules/latent-vs-deterministic.md
cp rules/completeness.md ~/.claude/rules/completeness.md
cp rules/receiving-code-review.md ~/.claude/rules/receiving-code-review.md
cp rules/in-the-circuits.md ~/.claude/rules/in-the-circuits.md
cp rules/spec-is-the-artifact.md ~/.claude/rules/spec-is-the-artifact.md
cp rules/menu-gen-test.md ~/.claude/rules/menu-gen-test.md

# Project-level
mkdir -p .claude/rules
cp rules/code-quality.md .claude/rules/code-quality.md
cp rules/change-philosophy.md .claude/rules/change-philosophy.md
cp rules/latent-vs-deterministic.md .claude/rules/latent-vs-deterministic.md
cp rules/completeness.md .claude/rules/completeness.md
cp rules/receiving-code-review.md .claude/rules/receiving-code-review.md
cp rules/in-the-circuits.md .claude/rules/in-the-circuits.md
cp rules/spec-is-the-artifact.md .claude/rules/spec-is-the-artifact.md
cp rules/menu-gen-test.md .claude/rules/menu-gen-test.md

Files

File Description
rules/code-quality.md Rule file covering the simplest-implementation principle, the dependency ladder (built-in > established library > custom implementation), code standards, testing, error handling, security, build verification, and living documents
rules/change-philosophy.md Rule file on elegant integration: redesign existing systems rather than bolting on, and delete backward-compatibility shims rather than preserving old shapes
rules/latent-vs-deterministic.md Rule file on classifying work as latent (judgment) vs deterministic (scripts) and pushing deterministic steps into code
rules/completeness.md Rule file on the Boil-the-Lake completeness principle and scoring rubric
rules/receiving-code-review.md Rule file on receiving code review feedback: verify before implementing, push back with evidence, no sycophantic agreement
rules/in-the-circuits.md Rule file on classifying tasks as in-circuit (dense RL training, trust output) vs out-of-circuit (no verifier, slow down) before proceeding
rules/spec-is-the-artifact.md Rule file on treating the spec as the durable artifact and code as regenerable output; write and review the spec before the code runs
rules/menu-gen-test.md Rule file on the Menu-Gen Test: before building an app/script/feature, ask whether a single prompt or multimodal call could replace it

Will install

Path Action Target Type
rules/code-quality.md → rules/code-quality.md rule
rules/change-philosophy.md → rules/change-philosophy.md rule
rules/latent-vs-deterministic.md → rules/latent-vs-deterministic.md rule
rules/completeness.md → rules/completeness.md rule
rules/receiving-code-review.md → rules/receiving-code-review.md rule
rules/in-the-circuits.md → rules/in-the-circuits.md rule
rules/spec-is-the-artifact.md → rules/spec-is-the-artifact.md rule
rules/menu-gen-test.md → rules/menu-gen-test.md rule

Dependencies

No dependencies.

Required by

Included in presets

Install this module

Agent prompt

Recommended for agent users -- hands the whole install off to your assistant.

Fetch https://ccgm.dev/modules/code-quality.md and install this module into my Claude Code setup.

Native plugin marketplace

One command via the native plugin marketplace -- additive, does not merge settings.json.

claude plugin install code-quality@ccgm

The marketplace path is additive, not a replacement: it installs commands, agents, and skills as native plugin components, but it does not perform the bash installer's deep settings.json merge, and it does not write the always-loaded global CLAUDE.md context. Rules are only injected via an opt-in SessionStart hook rather than being auto-loaded. Use the bash installer when those pieces matter to you.

Manual, per file

Full control -- copy exactly the files you want from the sections below.

Files

Files

rule (8)

rules/code-quality.md

# Code Quality Standards

## Simplest Implementation That Fully Meets the Requirements

Choose the simplest implementation that fully meets the current requirements. Both halves carry weight:

- **Simplest**: no abstraction with one implementation, no config option nobody sets, no plugin system for a single plugin, no generic helper called from one place.
- **Fully meets the current requirements**: every stated requirement is handled, edge cases and error paths included. See `completeness.md`.

The requirements that count are the ones that exist now. Build the second case when it arrives; by then its shape is known. Signs the implementation outran the requirements: an interface with one implementer, a config option never set to anything but its default, a layer that only forwards calls, generic type parameters instantiated with the same concrete type everywhere, "we'll need this when we add X" where X is on no roadmap.

- **Reuse first**: search for an existing helper, type, or pattern before writing a new one.
- **Mark the ceiling**: where you simplified on purpose, leave a short comment naming the accepted limit and what would justify replacing it.
- **Never cut**: validation at trust boundaries, data-loss guards, security controls, accessibility basics, or scope the user requested.

## Minimize Dependencies

The ladder: built-in, then established library, then custom implementation, then framework. Check whether the language or platform already provides what you need; when it does not, prefer an established, well-maintained library over hand-rolled code; equal outcome means no dependency.

Built-in wins: pure bash with ANSI escapes over a TUI library for simple menus, `fetch()` over axios, CSS variables over a theming library, shell built-ins over external CLI tools.

Library wins: a maintained date library over hand-written timezone arithmetic, the platform crypto API over a hand-written primitive, a real parser for a real grammar (CSV, YAML, semver, HTML) over regex, a schema validator over per-field checks at every entry point. A hand-rolled implementation is a dependency with no maintainer, no advisories, and no other users finding its bugs. "Established" means released within the last year or explicitly finished, an answered issue tracker, a permissive license, and wide adoption. Twenty lines of obvious logic is not a library's job; anything with a specification behind it (dates, encodings, crypto, grammars, protocols) is.

Justify every new package in the PR description, and justify hand-rolling the same way.

## Code Standards

- When adding env vars, update the corresponding `.env.example`. Never commit secrets.
- Use functional React components with explicit TypeScript prop interfaces.
- New migrations require regenerating TypeScript types; document schema changes in the migration file comments; after merging a PR with migrations, run them immediately rather than deferring to a follow-up.

### Migration Validation

Before committing a migration file:

1. Double-quote PostgreSQL reserved words used as identifiers: `position`, `order`, `user`, `offset`, `limit`, `key`, `value`, `type`, `name`, `check`, `default`, `time`, `index`, `comment`.
2. Use idempotent forms: `CREATE OR REPLACE FUNCTION`; `DROP TRIGGER IF EXISTS` then `CREATE TRIGGER`; `CREATE INDEX IF NOT EXISTS`; `CREATE TABLE IF NOT EXISTS` where appropriate; `ADD COLUMN IF NOT EXISTS`; `DROP POLICY IF EXISTS` then `CREATE POLICY`.
3. Test locally first, preferring `supabase migration up` (keeps local data) over `supabase db reset`.
4. Remember: `ON CONFLICT` needs a unique constraint on the conflict columns; `SECURITY DEFINER` functions run as the owner; RLS policies need `USING` for SELECT/UPDATE/DELETE and `WITH CHECK` for INSERT/UPDATE.

## Testing

Write tests for new features (happy path and key functionality), edge cases (empty states, boundaries, invalid input), bug fixes (a test that reproduces the bug before the fix), and complex logic.

## Error Handling

Frontend: error boundaries around major sections, toast notifications for feedback, inline form validation, explicit loading and error states. Backend: centralized error middleware; generic messages to the client, detailed logs server-side. Fail fast in development, degrade gracefully in production, give users actionable feedback, log errors.

## Security

Sanitize user input before rendering (DOMPurify for HTML), validate uploads (MIME type, size), never commit `.env` files, use Row Level Security for database access, review every user-facing input for injection.

## Build Verification

Do not run lint, type-check, tests, or build after every code change. Run the full verification suite once, immediately before pushing, and never push failing tests, type errors, or lint errors. If the repo has a `.husky/pre-push` hook it runs automatically; otherwise run the same checks CI runs:

```bash
npm run lint
npm run type-check
npm run test:run
npm run build
```

Consider adding a pre-push hook to projects that lack one. Update documentation when requirements or architecture change.

## Living Documents

After every PR merge, check whether `README.md` or `docs/project-story.md` needs a targeted update (5 to 10 minutes, not a rewrite). Update the README when the PR adds or removes a package, changes capabilities or permissions, changes dev commands or the build, changes external services, or changes deployment or payment configuration. Update the project story when the PR represents a notable architectural decision, fixes a non-obvious bug with an interesting root cause, introduces or removes a pattern, changes methodology, starts a new epic, or carries an interesting human decision. Skip typo fixes, dependency bumps, and changes the PR title already describes.

rules/change-philosophy.md

# Change Philosophy: Elegant Integration

When changing an existing system, do not patch, bolt on, or work around. For each change, ask how the system would look if the change had been a foundational assumption from the start, then refactor toward that. The result should read as if it was always designed this way.

In practice: understand the full system the change touches before adding to it, prefer making a difference a parameter over duplicating logic with variations, and leave the system more coherent than before.

## Do Not Preserve Backward Compatibility

Compatibility shims are how "bolted on" happens: a second code path kept alive so an old shape can coexist with the new one. When a change makes an old shape wrong, delete the old shape and update every caller in the same change.

Delete rather than preserve:

- Deprecated aliases, wrappers, and re-exports kept "just in case"
- Readers for a format nothing writes any more
- Version flags with exactly one live value
- Adapters for callers that no longer exist
- `if (legacy)` branches nothing sets `legacy` for
- Dead options left in a signature so an old call site still type-checks

Compatibility doubles the paths under test, hides which one is real, and defers a rename that costs minutes now and an archaeology session later.

### Where Compatibility Is a Real Requirement

Compatibility is a requirement when something outside this change depends on the old shape and cannot be updated with it:

- Published APIs, packages, or CLIs with consumers you do not control
- On-disk data, databases, or persisted state already in the field
- Wire protocols between independently deployed peers
- Anything under a stated support or versioning commitment

Treat it as a requirement, not a reflex: name it in the spec, write the migration, version the break deliberately.

### The Grep Test

Before keeping any compatibility path, find its callers:

```bash
grep -rn "old_function_name" src/ tests/ scripts/
```

If the only hits are the definition and the shim, delete both. If you cannot name the caller, there is no caller. For a published surface the callers are outside the repo, which is exactly why that case is a requirement rather than a reflex.

## The Boundary: Changed Lines

Redesign covers the code the change touches. Every changed line traces to the request or to integrating it; do not "improve" adjacent code, comments, or formatting. Remove imports, variables, and functions your change left unused. Mention unrelated dead code to the user instead of deleting it.

## When to Apply

Adding features to existing code, fixing bugs that reveal a design flaw, integrating a new dependency, extending a data model, renaming or reshaping anything with callers inside the repo.

## When Not to Apply

Trivial one-line fixes where the existing design is fine, time-critical hotfixes (patch now, redesign later), code you do not own or fully understand yet, cases where the elegant solution would rewrite half the codebase for a minor feature, and surfaces with consumers outside this change.

## Example

Bolted on:

```typescript
// Added special case for premium users
if (user.isPremium) {
  // duplicate 40 lines of logic with slight variations
}
```

Redesigned as if foundational:

```typescript
// Tier-aware from the start
const config = getTierConfig(user.tier)
return processWithConfig(data, config)
```

rules/latent-vs-deterministic.md

# Latent vs Deterministic Work

Agent bugs often come from doing deterministic work in latent space: the model reasoning about something a script could compute exactly. Before acting on a step, classify it.

- **Latent**: judgment, synthesis, open-ended choice. Needs the model. No single right answer.
- **Deterministic**: same input always gives the same output. A short script produces it exactly, faster, cheaper, and without fabrication.

Mixing the two is the bug. A script that weighs tradeoffs is over-engineered; a model that adds timestamps in its head is wrong on the first DST boundary.

| Work | Class | Belongs in |
|------|-------|-----------|
| Is this PR ready to merge? | Latent | Model |
| Summarizing an error log | Latent | Model |
| Picking which test suite to run | Latent | Model |
| Computing `now - event_time` in minutes | Deterministic | Script |
| Converting UTC to local time | Deterministic | Script |
| Grepping for a keyword across files | Deterministic | Script |
| Counting lines, files, matches | Deterministic | Script |
| Parsing a URL, a date, a path | Deterministic | Script |
| Reading the contents of a known file | Deterministic | Tool (Read) |

Reach for a script or an existing tool when you catch yourself doing arithmetic on numbers that came from data, converting timezones or units by hand, eyeballing whether a regex matches, inferring file existence from naming, counting without `wc -l`, remembering a value from earlier in the session instead of reading it fresh, or parsing JSON or CSV by scanning the text. Each of these is a computation that, if wrong once, is wrong every time.

## The Loop

When authoring a skill, hook, or command:

1. List the steps the agent will perform.
2. Mark each step latent or deterministic.
3. For every deterministic step, write or find a script that produces the answer.
4. Have the skill invoke the script instead of describing the computation in prose.
5. Write a test that pins the script's behavior on a representative input. A new check counts only after you run it clean, watch it fail on a deliberate violation, and revert the violation.

The script constrains the model; the test constrains the script.

## When to Leave Work in Latent Space

Skip the extraction when the computation runs once in a session, when the inputs are themselves latent ("summarize, then count the key points"), or when the script would be longer than the prose and no clearer. If the model does the same deterministic computation more than twice across sessions, it belongs in a script.

rules/completeness.md

# Completeness Principle: Boil the Lake

When doing it fully costs minutes and doing it partially costs a follow-up PR, do it fully. For each solution, ask whether there is a meaningful delta between "the whole job" and "what I was about to ship." If the delta is small, close it now.

Agent-assisted development compresses the cost of completeness. Edge cases, tests, docs, validation, and error states each take minutes, so "ship the happy path and backlog the edges" is no longer a reasonable default. The follow-up PR rarely happens; the deferred edge cases are the bugs users report.

## Completeness Rubric

| Score | Meaning |
|-------|---------|
| 10 | All edge cases handled, tests cover new behavior, docs updated, error paths explicit. Nothing left for a follow-up. |
| 8-9 | Happy path, known edge cases, tests. Minor polish deferred with explicit notes. |
| 7 | Happy path works, tests exist, obvious edges handled. Non-obvious edges may slip. |
| 5-6 | Happy path works. Tests partial or missing. Edge cases deferred. Follow-up PR required. |
| 3-4 | Works for the demo case. Significant work deferred. |
| 1-2 | Sketch or proof of concept. |

- Before reporting a task done, rate the work. Below 8, either finish the job or state explicitly what is deferred and why.
- When presenting options, include the score so the tradeoff is explicit: "Option A: 10/10" versus "Option B: 7/10, ships in half the time" is a real choice; "which do you prefer?" without scores is not.
- Reviewing your own diff at a 6, push the score up before asking for review.

## Boundaries

Completeness governs the depth of the job, not the breadth of the design. Finish every case the current requirements imply; do not add speculative features, handle hypothetical edge cases that cannot occur, refactor unrelated code, or build for requirements that do not exist yet. The simplest implementation that fully meets the current requirements, finished completely, is a 10. An extra abstraction nothing uses does not raise the score. See `code-quality.md`, "Simplest Implementation That Fully Meets the Requirements."

## The Test

Before claiming a task is complete:

1. Are there edge cases I know about but did not handle?
2. Is there a test I know I should write but decided to skip?
3. Is there a doc or comment I know is now stale?
4. Did I leave a TODO in the code or a "will do later" in the PR body?

Any yes means the work is not complete. Finish it now or state what is deferred and why.

rules/receiving-code-review.md

# Receiving Code Review

When a reviewer, human or agent, leaves feedback, the goal is correct code, not social smoothness. Verify before implementing, ask before assuming, technical correctness over social comfort.

Do not open a reply with performative agreement ("You're absolutely right!", "Great point!", "Good catch!", "Thanks for catching that!"). Those phrases substitute a gesture for evaluating whether the feedback is correct, and the reviewer cannot tell whether you verified the claim or rolled over.

## The Three Responses

- **Technical acknowledgment.** State the change in technical terms: "Fixed. `getUserById` now returns `null` for missing rows instead of throwing."
- **Fix and show the diff.** For unambiguous, correct feedback (typo, obvious bug, clear style violation), change the code and let the diff speak.
- **Reasoned pushback with evidence.** When the feedback is wrong, incomplete, or based on a misread, say so with cited lines, tests, or prior decisions: "The caller at `src/api/users.ts:84` already wraps this in a `try/catch` that expects the throw. Changing the return shape would break that path."

## Verify Before Implementing

1. Read the feedback completely.
2. Restate the requirement in your own words; if you cannot, you did not understand it.
3. Open the cited file and lines and confirm the problem exists as described.
4. Evaluate the proposed fix: does it work, does it break other callers, does it conflict with an earlier architectural decision?
5. Respond with one of the three responses, then implement one item at a time, with tests.

A reviewer can be wrong, right about the symptom but wrong about the cause, or correct but missing context that changes the right fix.

## YAGNI Check

Before adding a feature, endpoint, handler, option, or abstraction a reviewer suggested, grep for callers. If nothing calls it, push back: "Grepped for callers of `X`; none exist. Holding off until a real consumer appears."

## Unclear Items

If a review has several items and you understand only some, do not implement the ones you understood while planning to ask about the rest; the unclear items may change how the clear ones should be done. State which items are clear, ask about the others, and wait.

## Authority and Accuracy

Reviewer authority is real: they may have context, design decisions, or downstream concerns you cannot see, so assume the feedback is correct until verification proves otherwise. Authority does not make every claim accurate. Push back, with evidence, when feedback misreads the code, would break an existing test or caller, violates YAGNI, conflicts with a documented architectural decision, or applies a pattern that does not fit this codebase. Accept gracefully when the counter-argument is correct. The diff is the thanks.

rules/in-the-circuits.md

# In-the-Circuits: Verifiable-Domain Self-Classification

Before starting any task, name the circuit you are in. One sentence. This sets confidence mode, review cadence, and escalation threshold for everything that follows.

> "If you're in the circuits that were part of the RL, you fly. And if you're in the circuits that are out of the data distribution, you're going to struggle and you have to figure out which circuits you're in in your application."
> — Andrej Karpathy, Sequoia Capital, 2026-04-29

## The Classification

| Circuit | What it means | How to proceed |
|---------|--------------|----------------|
| **In-circuit** | The task sits in a domain where frontier models have dense RL training: code, math, refactoring, testing, structured data transformation. Output quality is high and verifiable. | Ride the wave. Trust output. Move at full speed. Standard verification applies. |
| **Out-of-circuit** | The task touches taste, UX copy, novel architecture, brand voice, domain-specific reasoning the labs did not RL on, or any domain where there is no ground-truth verifier. Output may be fluent but unreliable. | Slow down. Human-in-loop. Expect to fine-tune or escalate. Flag explicitly before proceeding. |

If the circuit is unclear, treat it as **out-of-circuit**. The cost of unnecessary caution on an in-circuit task is low. The cost of overconfidence on an out-of-circuit task is high.

## The Protocol

At task start, write one sentence:

```
Circuit: in-circuit — refactoring the auth middleware to extract token validation.
```

or

```
Circuit: out-of-circuit — writing onboarding copy for a healthcare app. Flagging for human review.
```

That is the entire protocol. No elaborate analysis. If you cannot name the circuit in one sentence, it is out-of-circuit.

## Examples

| Task | Circuit | Reason |
|------|---------|--------|
| Implement a CSV parser | In-circuit | Code; dense RL training; output is mechanically verifiable |
| Refactor this function for clarity | In-circuit | Code; RL'd on refactoring patterns; output is diff-reviewable |
| Write a failing test for this bug | In-circuit | Testing; pass/fail is a verifier |
| Convert this schema to TypeScript types | In-circuit | Structured transformation; types are checkable |
| Fix this SQL query | In-circuit | Code; query result is verifiable |
| Name this product | Out-of-circuit | Taste; no verifier; labs not RL'd on brand naming |
| Decide whether to use Postgres or DynamoDB | Out-of-circuit | Novel architecture choice; context-dependent; no ground-truth verifier |
| Write empathetic onboarding copy for a healthcare app | Out-of-circuit | UX copy + healthcare domain knowledge; taste-dependent; high stakes if wrong |
| Choose the right color palette for this brand | Out-of-circuit | Aesthetic taste; subjective; no verifier |
| Summarize this legal contract | Out-of-circuit | Domain-specific (legal); hallucination risk is high-stakes |

## When to Apply

Apply this classification at the start of every non-trivial task. "Non-trivial" means anything that will take more than one tool call or produce output the user will act on.

## When NOT to Apply

Do not apply it to:

- Single-line changes with obvious correct form (no judgment involved)
- Tasks where the circuit is obvious and the classification would be noise (e.g., every `git status` call does not need a circuit announcement)

## Relationship to Neighboring Rules

**`confusion-protocol.md`** — A mid-task escape hatch when you hit an architectural fork and cannot proceed without a decision. In-the-circuits is a pre-task classification, not a mid-task interrupt. They are complementary: classify first, then if you hit a fork while executing, invoke the confusion protocol.

**`verification.md`** — Evidence is required either way. Being in-circuit does not exempt output from verification. It only tells you the output is likely trustworthy enough to verify rather than discard. Out-of-circuit output should be verified AND reviewed by a human before acting on it.

**`latent-vs-deterministic.md`** — Different axis. The latent/deterministic split asks: *who should compute the answer* (model vs. script)? The in-circuit split asks: *has the model been RL'd on this domain*? A task can be latent (needs the model) and out-of-circuit (model is unreliable here) at the same time. Example: "choose a product name" is latent (no script can do it) and out-of-circuit (model is unreliable at naming). Both rules apply independently.

## Anti-Patterns

| You are about to say... | The reality is... |
|-------------------------|-------------------|
| "The model is generally smart, it will figure this out" | General intelligence does not equal domain RL. Fluency is not reliability. |
| "The copy looks good, ship it" | Out-of-circuit output looks fluent. Fluency is not accuracy or appropriateness. |
| "I'll classify mid-task if something feels off" | By then you have already committed to an approach. Classify before you start. |
| "This is mostly code, so it's probably fine" | "Mostly code" that also involves novel architecture or domain-specific logic is mixed-circuit. Name it. |
| "The user can review it later" | That transfers the classification burden to the user without warning them it exists. Name the circuit; let them decide how much to trust. |

## Red Flags

Stop and reclassify if you catch yourself:

- Outputting architectural recommendations with the same confidence as a function refactor
- Writing UX copy without flagging that taste-based output needs human sign-off
- Treating "it compiles" as evidence that out-of-circuit reasoning was correct
- Making a domain-specific judgment (legal, medical, financial, brand) without noting the circuit
- Presenting options in an out-of-circuit domain without flagging that none of them may be correct

rules/spec-is-the-artifact.md

# Spec Is the Artifact

The spec is the durable artifact. Code is regenerable output.

> "I actually don't even like the plan mode... You have to work with your agent to design a spec that is very detailed and maybe basically the docs and then get the agents to write them and you're in charge of the oversight."
> — Andrej Karpathy, Sequoia Capital, 2026-04-29

When agents can write code from a spec in minutes, the spec becomes more valuable than the code. The code can be deleted, regenerated, or rewritten. A good spec cannot — it encodes decisions, constraints, and intent that took time to discover.

## The Principle

Write the spec before the code. Review the spec before the code runs. When behavior diverges from spec, fix the spec first, then bring the code into alignment. The spec is the source of truth.

Code without a spec is behavior without intent. Behavior without intent drifts.

## Sizing Guidance

Not every task needs an xplan. Use judgment:

| Work | Spec overhead | What fits |
|------|--------------|-----------|
| Typo fix, one-line config change | None needed | Comment in commit message is enough |
| Single-PR feature, small refactor | One-page spec | Problem, deliverables, constraints, done-when |
| Multi-PR feature, architectural change | Full spec | Problem, deliverables, constraints, done-when, non-goals, open questions |
| New project, major system redesign | xplan | Full research + plan + reviews + execution phases |

The one-page spec for a single PR is not bureaucracy. It is the document you would write to explain the work in a PR description — written before the code, not after.

### One-Page Spec Structure

A spec does not need to be formal. It needs four things:

1. **Problem** — What is broken or missing? Why does it matter?
2. **Deliverables** — What will exist when this is done that did not exist before?
3. **Constraints** — What must not change? What approaches are off the table?
4. **Done-when** — How will we verify the work is complete?

That is the minimum. The rest is optional.

## Drift Protocol

When a deployed behavior diverges from its spec:

1. Update the spec to reflect the correct intended behavior
2. Then bring the code into alignment with the updated spec

Never silently update behavior without updating the spec. The spec is the record of why things work the way they do. A codebase whose behavior has drifted past its spec is a codebase that no future agent can reason about safely.

## When to Apply

- Starting a new feature, however small
- Starting a refactor that touches more than one file
- Starting any change where "done" is not self-evident from the task description

## When NOT to Apply

- Obvious one-line fixes (typo, missing semicolon, wrong constant value)
- Changes that are fully described by the failing test or error message
- Exploratory spikes the user explicitly plans to throw away

If in doubt, write the spec. A ten-line spec takes two minutes. Reworking a feature because the intent was unclear takes hours.

## Anti-Patterns

| You are about to say... | The reality is... |
|-------------------------|-------------------|
| "I'll write the spec after I see what the code looks like" | A spec written to match code that already exists is a description, not a design. It cannot catch mistakes because the mistakes are already in the code it is describing. |
| "The PR description captures this" | PR descriptions are tied to the diff, not the behavior. They disappear into git history. A spec lives where the code lives and stays current. |
| "I'll just put this in a comment" | Comments describe what the code does at the line it appears on. They do not describe the problem, the constraints, or why the design tradeoffs were made. |
| "The agent will figure out the spec as it writes the code" | The agent writing both the spec and the code in one pass with no human review of the spec in between is vibe coding with extra steps. The spec review is the gate. Skip it and you have skipped the oversight Karpathy is describing. |
| "This is a small task, a spec is overkill" | A one-page spec for a single PR takes two minutes. The alternative is a follow-up PR explaining why the first one did the wrong thing. |

## Relationship to Neighboring Rules

**`xplan`** is the heavyweight variant of this principle. xplan runs research, planning, multi-agent review, and structured execution. Use xplan when the scope warrants it. Use a one-page spec for everything else. The principle is the same at every scale: spec first, code second, human reviews the spec before the code runs.

**`completeness.md`** defines a 10/10 rubric for done work. The 10/10 rubric assumes there is a target to be complete against. The spec is that target. Without a spec, "done" is undefined and the rubric cannot be applied.

rules/menu-gen-test.md

# Menu-Gen Test: Apps That Shouldn't Exist

Before committing to build anything — a new app, a script, a feature, a workflow — ask one question first:

> **Could this be a single prompt + multimodal call instead of an app/script/feature? If yes, why are we building anything?**

If you cannot answer "why," stop. The build might be unnecessary.

## The Karpathy Confession

Andrej Karpathy built Menu Gen: an OCR webapp on Vercel that takes a photo of a restaurant menu, calls an image generator for each item, and re-renders the menu with pictures.

Then he saw the Software 3.0 version: hand the photo to Gemini, say "use Nano Banana to overlay the items," and receive the annotated image directly — one multimodal call, no app.

> *"All of my menu gen is spurious. It's working in the old paradigm — that app shouldn't exist."*
> — Andrej Karpathy, Sequoia Capital, 2026-04-29

The app was not wrong or poorly built. It was answering the right question in the wrong paradigm. The new question is: does the build need to exist at all?

## The Forcing Question

At intake — before research, before planning, before any implementation — answer this in one paragraph:

> Could this be accomplished with a single prompt and a multimodal/agentic call? If yes, what is the specific reason an app, script, or persistent system is still needed?

Valid reasons an app still needs to exist:
- Runtime injection into another system (e.g., a Chrome extension that modifies page DOM)
- Persistent server state shared across users or sessions (e.g., a multi-tenant SaaS with a database)
- Recurring background automation that cannot be triggered manually each time
- Distribution to non-technical users who cannot operate a prompt

Not valid reasons:
- "The prompt would be long" — long prompts are fine
- "We need a UI" — many things that feel like they need UI are just a prompt with output rendering
- "The logic is complex" — complex logic can live in an agent, not an app

## Dissolvability Score

If the answer is not obvious, score it:

| Score | Meaning |
|-------|---------|
| **0** | Clearly needs to exist. Has runtime injection, persistent multi-user state, or distribution requirements. |
| **2-3** | Mostly needs to exist, but some parts could be dissolved into prompts. Consider which parts. |
| **5** | Could be a single multimodal or agentic call right now. Strong case for not building. |
| **4** | Borderline. The app adds enough structure or UX that it is worth building — but name the specific reason explicitly. |

A score of 4 or 5 is not a hard stop. It is a flag. Name the reason you are building anyway. If you cannot name it, the build is not justified.

## Examples

**Should not exist (score: 5)**

Menu Gen — OCR + image gen webapp. One Gemini + Nano Banana call does the same thing.

A script that fetches a URL, runs it through a prompt, and emails the summary on a schedule — if it runs manually each time and the user already has access to a model, this is a prompt, not a script.

**Needs to exist (score: 0)**

A Chrome extension that injects a dark-mode CSS overlay into every page the user visits. It runs inside the browser at runtime, on arbitrary pages the user navigates to. No prompt can do this.

A multi-tenant habit-tracking SaaS with user accounts, persistent streaks, and push notifications. It requires a database, a server, and distribution to users who interact via a native UI.

## When to Apply

Apply this check at every project intake: `/xplan`, `/research`, `/ideate`, or any other scoping exercise before research and planning begins.

Do not apply it to:
- Work already in progress (this is an intake check, not a retroactive audit)
- Incremental features on an existing system where the system's existence is already justified
- Purely exploratory research with no build decision yet

## Relationship to Build Decisions

This check does not reject ideas. It forces explicit justification before committing resources. The answer "this needs to exist because users cannot operate a raw prompt" is a complete and sufficient answer. The problem is building without asking the question at all.