Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .claude/agents/review-standards.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
name: review-standards
description: Reviews a file or a diff against this project's own standards. Use when asked to "check standards", "review against AGENTS.md", "does this follow our conventions", or before a commit on a focused change. The single-target counterpart to the diff-wide parallel-review workflow.
tools: Read, Grep, Glob, Bash
model: sonnet
permissionMode: auto
---

# Review Standards Agent

## Task

Review the target the user names — a file path, or the diff `git diff <base>...HEAD` — for
**violations of this project's stated standards only**. Not correctness bugs, not security
(those are `parallel-review`'s other two dimensions); not stylistic taste.

## Proceed without asking

You are a background subagent and cannot surface approval prompts (`AskUserQuestion` is
unavailable inside subagents). Do not hedge or ask permission to read or run read-only
commands — your frontmatter pre-approves the tools you need. Asking "May I proceed?" ends the
run without producing work. Execute the workflow end to end.

## Workflow

1. Read `AGENTS.md` and the relevant `docs/` files (`REACT_GUIDELINES.md` for components,
`ARCHITECTURE.md` for layout).
2. Read the target — the named file, or the changed lines from the diff.
3. Flag only **real** violations of the documented standards:
- **Barrel imports** — importing through an `index.ts` re-export instead of the direct path.
- **State synced via `useEffect`** that should be **derived during render**.
- **Genuine duplication or premature abstraction** — remembering this project holds DRY/KISS/SOLID *with judgment, not dogma*: a little duplication beats a wrong abstraction.
- **A module without a colocated test**, or a `utils`/`helpers`-style junk-drawer name.
4. For each violation, report: the file, the line, and **which standard** it breaks.

## Report

A short list — file, line, standard, one-line why — ranked by how much it matters, or
"No standards violations found." Do not pad the list to look thorough; an empty result is a
valid, useful answer.

## Do not

- Report correctness or security issues — out of scope; say so and point at `parallel-review`.
- Flag stylistic preferences the docs don't actually mandate.
- Edit code — you review; fixing is a separate, authorized step.
40 changes: 40 additions & 0 deletions .claude/commands/new-spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
description: Scaffold a new spec + implementation-plan pair from the house templates. Usage: /new-spec <kebab-case-slug>
---

# New Spec

Scaffold the two reviewable artifacts for a new feature — a design spec and its
implementation plan — pre-filled with this repo's section headings, so the spec-driven
workflow starts from a consistent shape instead of a blank page.

## Input

A short kebab-case feature slug (e.g. `retry-budget`, `flag-audit-log`). If the user didn't
provide one, ask for it — one word or two, no spaces.

## Steps

1. Compute today's date as `YYYY-MM-DD` (run `date +%F`).
2. Derive the two target paths:
- `specs/<date>-<slug>-design.md`
- `specs/implementation-plans/<date>-<slug>.md`
3. **If either file already exists, stop and report it** — never overwrite an existing spec
or plan.
4. Create the design spec with these headings (leave a one-line prompt under each):
`# Spec: <slug>` -> `## Problem` -> `## Inputs` -> `## Outputs` -> `## Errors / edge cases`
-> `## Acceptance` -> `## Testing`.
5. Create the implementation plan with these headings:
`# <slug> — Implementation Plan` -> a `**Goal:**` line -> `**Architecture:**` line ->
`**Spec:**` link back to the design file -> the agentic-workers note (use
`superpowers:executing-plans`, steps as `- [ ]`) -> `## File map` -> `## Backwards compatibility`
(a table) -> `## Steps` -> `## Acceptance (from spec)`.
6. Report the two paths created and remind the user the next step is to fill in the Problem
and Inputs, not to jump to code.

## Do not

- Overwrite an existing file.
- Fill in the actual design — scaffold the structure; the thinking is the user's (or the next
brainstorming pass).
- Skip the plan — a spec without a plan is half the artifact.
37 changes: 37 additions & 0 deletions .claude/skills/scaffold-module.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
name: scaffold-module
description: Use when adding a new self-contained module under src/ — "scaffold a module", "new module for X", "add a src/ folder for Y". Creates the types + implementation + colocated test trio following the repo's existing worked examples.
---

# Scaffold Module

## Overview

A module here is a folder under `src/` with a single clear purpose, a typed surface, and a
colocated test. This skill scaffolds that trio consistently so a new module matches the two
existing worked examples ([`src/feature-flags/`](../../src/feature-flags),
[`src/http/`](../../src/http)) instead of inventing its own shape.

## Input

- A module **name** (kebab-case folder, camelCase export) and a **one-line purpose**.
- Whether it's **sync/pure** (like `feature-flags`) or **async/has an I/O boundary** (like
`http`) — this decides the test approach.

## Workflow

1. **Refuse generic names.** `utils`, `helpers`, `common`, `misc` name a junk drawer, not a
responsibility. Ask for a name that says what the module *does*.
2. **Check the target.** If `src/<name>/` already exists, stop and ask — don't merge into it blindly.
3. **Create the trio:**
- `src/<name>/types.ts` — the public types (options, results, typed errors). Make illegal states unrepresentable.
- `src/<name>/<name>.ts` — the implementation. Pure where possible; inject I/O (e.g. an injected `fetch`) so it's testable without globals.
- `src/<name>/<name>.test.ts` — Vitest. For sync/pure: assert inputs -> outputs directly. For async/I-O: inject a fake and use `vi.useFakeTimers()` instead of real waiting.
4. **No barrel file.** Callers import from the direct path; don't add an `index.ts` re-export.
5. **Prove it green:** `pnpm lint && pnpm typecheck && pnpm test` before calling it done.

## Do not

- Create a module without a test — the test is the guardrail, not an afterthought.
- Add a dependency to scaffold a module; the toolchain stays minimal.
- Reach for a class when a function and plain data will do (KISS).
79 changes: 79 additions & 0 deletions .claude/workflows/generate-tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
export const meta = {
name: "generate-tests",
description:
"Find src/ modules without a colocated test and write one per file in parallel, each verified green before it's reported",
whenToUse:
"After scaffolding or landing new modules — backfills the test guardrail across many files at once instead of one at a time.",
phases: [
{ title: "Discover", detail: "one agent lists untested modules" },
{ title: "Generate", detail: "one write-test agent per file, in parallel" },
],
};

// Default to the whole source tree; callers can narrow with `--glob`.
const glob = (args && args.glob) || "src/**/*.ts";

const DISCOVER_SCHEMA = {
type: "object",
properties: {
files: {
type: "array",
items: { type: "string" },
},
},
required: ["files"],
};

const RESULT_SCHEMA = {
type: "object",
properties: {
file: { type: "string" },
testFile: { type: "string" },
passing: { type: "boolean" },
summary: { type: "string" },
},
required: ["file", "passing", "summary"],
};

const discovery = await agent(
`List every implementation module matching \`${glob}\` that has NO colocated ` +
`\`*.test.ts\` sibling. Exclude files that are themselves tests (\`*.test.ts\`), ` +
`type-only files (\`types.ts\`), and barrel files (\`index.ts\`). Return the source ` +
`file paths only.`,
{
label: "discover:untested",
phase: "Discover",
schema: DISCOVER_SCHEMA,
},
);

const targets = discovery.files || [];
log(`${targets.length} untested module(s) found`);

const results = await parallel(
targets.map(
(file) => () =>
agent(
`Write a Vitest test for \`${file}\` following the write-test subagent's ` +
`workflow: match the nearest existing \`*.test.ts\` patterns, cover the real ` +
`branches and edge cases, and run \`pnpm test -- --run\` on the new file to ` +
`confirm it passes. Report the test path, whether it passes, and what it covers.`,
{
label: `generate:${file}`,
phase: "Generate",
schema: RESULT_SCHEMA,
},
),
),
);

const written = results.filter(Boolean);
const passing = written.filter((r) => r.passing);
log(`${passing.length} of ${written.length} generated test file(s) passing`);

return {
glob,
untested: targets.length,
written: written.length,
results: written,
};
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Load the right doc for the task instead of reading everything:

| Topic | File | When to read |
|-------|------|-------------|
| System architecture & layout | [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | Understanding where things live; adding a module or agent tool |
| Git hooks & branch naming | [docs/GIT_HOOKS.md](docs/GIT_HOOKS.md) | Before committing or pushing |
| Development setup & tooling | [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) | First-time setup, tooling questions |
| React & component standards | [docs/REACT_GUIDELINES.md](docs/REACT_GUIDELINES.md) | When creating or structuring components |
Expand Down
14 changes: 9 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ stays thin to avoid duplicating that source of truth.
A `SessionStart` hook ([.claude/hooks/session-start.sh](.claude/hooks/session-start.sh))
primes branch and workflow context at the start of every session.

## Skills & subagents
## Commands, skills, subagents & workflows

- **Skills** ([.claude/skills/](.claude/skills)): `commit`, `pr-description`, `safe-rollout`.
- **Subagents** ([.claude/agents/](.claude/agents)): `write-test` — dispatch with the
`Agent` tool; run independent work in parallel.
- **Commands** ([.claude/commands/](.claude/commands)): `new-spec` — scaffold a spec + plan pair.
- **Skills** ([.claude/skills/](.claude/skills)): `commit`, `pr-description`, `safe-rollout`,
`scaffold-module` (scaffold a new `src/` module).
- **Subagents** ([.claude/agents/](.claude/agents)): `write-test`, `review-standards` — dispatch
with the `Agent` tool; run independent work in parallel.
- **Workflows** ([.claude/workflows/](.claude/workflows)): `parallel-review` (multi-agent review
+ skeptic), `generate-tests` (parallel test backfill).

## Workflow

Brainstorm → spec → implementation plan → implement, on the
[Superpowers](https://github.com/obra/superpowers) workflow. Specs and plans live
in [specs/](specs).
in [specs/](specs). Layout: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,17 @@ The biggest lever for good AI output is the context you give it, not the prompt.

- **[`AGENTS.md`](AGENTS.md)** — a single source of truth for project rules, indexed so the AI (and humans) can load the right doc at the right time.
- **[`CLAUDE.md`](CLAUDE.md)** — the Claude Code entry point. Deliberately thin: it redirects to `AGENTS.md` so the rules live in one place instead of being duplicated per tool. A `SessionStart` hook in [`.claude/`](.claude) primes branch and workflow context automatically.
- **[`docs/`](docs)** — the standards `AGENTS.md` points to (git hooks, development setup, React guidelines).
- **[`.claude/skills/`](.claude/skills)** — reusable, plain-English skills for recurring tasks (commit, PR description, safe rollout) with the safety rules baked in.
- **[`.claude/agents/`](.claude/agents)** — focused subagents (e.g. a test writer) that run independently and in parallel.
- **[`docs/`](docs)** — the standards `AGENTS.md` points to (architecture, git hooks, development setup, React guidelines).
- **[`.claude/commands/`](.claude/commands)** — slash commands for repeatable ops; `new-spec` scaffolds a spec + implementation-plan pair from the house templates.
- **[`.claude/skills/`](.claude/skills)** — reusable, plain-English skills for recurring tasks (commit, PR description, safe rollout, scaffold a module) with the safety rules baked in.
- **[`.claude/agents/`](.claude/agents)** — focused subagents (a test writer, a standards reviewer) that run independently and in parallel.
- **[`.claude/workflows/`](.claude/workflows)** — `parallel-review`: a runnable,
multi-agent orchestration script — three reviewers (correctness, security,
this project's own standards) fan out in parallel, then every finding goes
through an independent skeptic before it's reported. This is the literal
code behind the "parallel subagents" claim above, not just a description of
it.
it. `generate-tests` reuses the same orchestration to backfill tests across
many untested modules at once.
- **[`.claude/settings.json`](.claude/settings.json)** — a scoped permission allowlist and a session-start hook that primes context automatically.
- **[`.husky/`](.husky)** — the git hooks `docs/GIT_HOOKS.md` documents, actually
wired up: `pre-commit` runs lint-staged, `commit-msg` blocks commits whose
Expand Down Expand Up @@ -89,13 +91,16 @@ measured by what users feel, backed by tests, not by how clever the code looks.
| [`AGENTS.md`](AGENTS.md) | The single-source-of-truth context pattern |
| [`CLAUDE.md`](CLAUDE.md) | The thin Claude Code entry that redirects to `AGENTS.md` |
| [`docs/`](docs) | Standards referenced by `AGENTS.md` |
| [`.claude/skills/`](.claude/skills) | Reusable task skills with safety rails |
| [`.claude/agents/`](.claude/agents) | Independent, parallelizable subagents |
| [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | How the repo is laid out — worked examples, docs, and agent tooling |
| [`.claude/commands/`](.claude/commands) | Slash commands — `new-spec` scaffolds a spec + plan pair |
| [`.claude/skills/`](.claude/skills) | Reusable task skills with safety rails (incl. `scaffold-module`) |
| [`.claude/agents/`](.claude/agents) | Independent, parallelizable subagents (`write-test`, `review-standards`) |
| [`.claude/`](.claude) | Permissions + session-start hook |
| [`.husky/`](.husky) | Real git hooks matching `docs/GIT_HOOKS.md` — not just documentation |
| [`.github/`](.github) | CI workflow + PR template the skills/docs reference |
| [`.editorconfig`](.editorconfig) | Editor-level formatting baseline matching `docs/DEVELOPMENT.md` |
| [`.claude/workflows/parallel-review.js`](.claude/workflows/parallel-review.js) | A runnable multi-agent workflow: parallel reviewers + adversarial verification |
| [`.claude/workflows/generate-tests.js`](.claude/workflows/generate-tests.js) | A parallel workflow that backfills tests for untested modules |
| [`docs/CASE_STUDY.md`](docs/CASE_STUDY.md) | The workflow's first real PR, including the bugs it caught before merge |

## Credits
Expand Down
47 changes: 47 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Architecture

This repo is a **teaching artifact**, not an application. Its "architecture" is the layout
that makes AI-assisted work reproducible: where the rules live, where the worked examples
live, and where the agent tooling lives. Everything here is generic on purpose.

## Two worked examples (`src/`)

Each example takes the same spec -> plan -> implement path to a deliberately different kind
of problem, so the workflow is shown on more than one shape of code:

| Module | Shape | What it proves |
|--------|-------|----------------|
| [`src/feature-flags/`](../src/feature-flags) | sync, pure (no I/O) | a typed `useFeatureFlag` hook tested without timers or mocks |
| [`src/http/`](../src/http) | async, real I/O boundary | a `fetchWithRetry` wrapper tested with an injected `fetch` + fake timers |

A module is a folder with `types.ts`, the implementation, and a colocated `*.test.ts`. New
modules follow the same shape — see the [`scaffold-module`](../.claude/skills/scaffold-module.md) skill.

## Context, in version control

The rules an agent needs are files, not tribal knowledge:

- **[`AGENTS.md`](../AGENTS.md)** — the single source of truth, with a documentation index that tells an agent which `docs/` file to load for a task.
- **[`CLAUDE.md`](../CLAUDE.md)** — a thin Claude Code entry point that redirects to `AGENTS.md`.
- **[`docs/`](.)** — the standards `AGENTS.md` points to (this file, git hooks, development setup, React guidelines).

## Agent tooling (`.claude/`)

| Path | Kind | Runs | Example |
|------|------|------|---------|
| [`commands/`](../.claude/commands) | slash command | on demand, in your session | `new-spec` scaffolds a spec + plan pair |
| [`skills/`](../.claude/skills) | reusable skill | when its trigger matches | `commit`, `pr-description`, `safe-rollout`, `scaffold-module` |
| [`agents/`](../.claude/agents) | subagent | dispatched via the `Agent` tool, in parallel | `write-test`, `review-standards` |
| [`workflows/`](../.claude/workflows) | orchestration script | as a multi-agent run | `parallel-review`, `generate-tests` |
| `settings.json` | config | always | scoped permission allowlist + session-start hook |
| `hooks/session-start.sh` | hook | every session start | primes branch + workflow context |

**Skills vs subagents vs workflows.** A *skill* is guidance loaded into the current agent. A
*subagent* is a fresh, focused agent you dispatch for one job (it can run in parallel and has
its own tool permissions). A *workflow* is a script that orchestrates several subagents across
phases — `parallel-review` and `generate-tests` are the two worked examples.

## Guardrails

`.husky/` git hooks and `.github/workflows/ci.yml` enforce the same four checks — `pnpm lint`,
`format:check`, `typecheck`, `test` — locally and in CI. See [GIT_HOOKS.md](GIT_HOOKS.md).
Loading
Loading