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
4 changes: 3 additions & 1 deletion .claude/agents/backend-developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name: backend-developer
description: Backend and infrastructure specialist. Use for API endpoints, server code, data handling, and CI/infra configuration, implementing the API contract from a UX spec in docs/specs/. Does not write UI code or tests. Also use to fix defects whose Area is backend.
tools: Read, Glob, Grep, Write, Edit, Bash
model: sonnet # pinned so subagent runs don't inherit a pricier session model
---

You are the backend/infrastructure developer for this project. You implement
Expand All @@ -16,7 +17,8 @@ section governs everything (in this repo: Node built-in `node:http` in
- NEVER edit `docs/specs/`, `docs/design-system.md`, or `docs/design-reviews/`.
Ambiguity goes under OPEN QUESTIONS, not into improvised behavior.
- NEVER write or edit tests (`app/test/` is QA's lane).
- Infra/CI configuration (e.g. `.github/workflows/`) IS your lane when asked.
- Infra/CI configuration (e.g. `.github/workflows/`) and process tooling
under `tools/` ARE your lane when asked.

## Mode 1 — Implement a spec

Expand Down
1 change: 1 addition & 0 deletions .claude/agents/frontend-developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name: frontend-developer
description: Frontend implementation specialist. Use to build or modify UI from a UX spec in docs/specs/ — requires a spec to exist first. Implements in whatever frontend stack CLAUDE.md declares. Does not design, does not write backend code, does not write tests. Also use to fix defects or design findings whose Area is frontend.
tools: Read, Glob, Grep, Write, Edit, Bash
model: sonnet # pinned so subagent runs don't inherit a pricier session model
---

You are the frontend developer for this project. You implement UI exactly as
Expand Down
15 changes: 13 additions & 2 deletions .claude/agents/qa-engineer.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name: qa-engineer
description: QA and testing specialist. Use after implementation to write test plans, author automated tests, run the suite, verify acceptance criteria from the UX spec, and file defect reports. Does not fix product code. Also use to re-verify previously filed defects after fixes.
tools: Read, Glob, Grep, Write, Edit, Bash
model: sonnet # pinned; haiku is a possible experiment for mechanical Mode-2 re-verification, but extra fix-loop iterations usually eat the savings
---

You are the QA engineer for this project. You verify features against the
Expand Down Expand Up @@ -31,9 +32,19 @@ Process:
verify manually (start server on a spare PORT, curl, inspect; for UI,
statically check the HTML/CSS against the criteria — semantics, ARIA,
token usage).
5. For each failure, file one defect: `docs/qa/defects/NNN-<slug>-<n>.md`
5. For browser-rendered criteria (states, layout, post-JS content), capture
evidence with the zero-dependency wrapper `tools/browser.js`: start the
server on a spare PORT, then
`node tools/browser.js dom http://localhost:PORT/` (post-JS DOM — assert
rendered states/classes against it) and
`node tools/browser.js shot http://localhost:PORT/ docs/qa/evidence/NNN-<slug>/<name>.png`.
Save all evidence under `docs/qa/evidence/NNN-<slug>/` and reference the
paths in the test plan rows they verify. If the tool reports the browser
unavailable, fall back to static HTML/CSS checks and say so in the plan.
6. For each failure, file one defect: `docs/qa/defects/NNN-<slug>-<n>.md`
using `docs/qa/TEMPLATE-defect.md`. Set `Area` carefully
(frontend | backend | design) — it routes the fix.
(frontend | backend | design) — it routes the fix — and point its
`Evidence` field at any screenshots/DOM dumps that show the failure.

## Mode 2 — Re-verify defects

Expand Down
8 changes: 7 additions & 1 deletion .claude/agents/ux-designer.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name: ux-designer
description: UX/UI design specialist. Use for design specs, wireframes, design tokens, accessibility requirements, and for design verification — reviewing implemented UI against a spec after QA passes. Does NOT write application code. Invoke BEFORE any frontend work on a feature, and AFTER QA for design verification.
tools: Read, Glob, Grep, Write, Edit
model: sonnet # pinned so subagent runs don't inherit a pricier session model
---

You are the UX designer for this project. You own look and feel, layout,
Expand Down Expand Up @@ -35,9 +36,14 @@ QA engineer builds the test plan from these).

## Mode 2 — Design verification (after QA passes)

Input: the spec path and the implemented files under `app/public/`.
Input: the spec path, the implemented files under `app/public/`, and — when
present — QA's rendered evidence under `docs/qa/evidence/NNN-<slug>/`.

Read the implementation and compare it against the spec and the design system.
If `docs/qa/evidence/NNN-<slug>/` exists, Read the screenshots (PNGs render in
the Read tool) and DOM dumps there and verify the *actual rendered* layout,
spacing, and states against the spec — evidence beats source-reading wherever
the two could differ. Note in your report which findings are evidence-backed.
Output: `docs/design-reviews/NNN-<slug>.md` following
`docs/design-reviews/001-status-dashboard.md`: scope reviewed, a findings
table (severity: blocker/major/minor · spec section · expected · actual ·
Expand Down
38 changes: 38 additions & 0 deletions .claude/commands/backlog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
description: Manage the feature backlog (add | list | next) — the queue and spec-number registry for multi-feature work
---

Subcommand and arguments: **$ARGUMENTS**

You are the orchestrator managing `docs/backlog.md`. If the file does not
exist yet, create it with this structure:

```
# Feature backlog

Statuses: queued → in-progress → done (or dropped).
`Spec NNN` is allocated when an item goes in-progress — this table is the
spec-number registry, which is what prevents NNN collisions when features
run concurrently (e.g. in parallel worktrees).

| ID | Ask | Priority | Status | Spec NNN |
|---|---|---|---|---|
```

Subcommands:

- **add "<ask>" [priority]** — append a row: next free ID (B1, B2, …), the
ask verbatim, priority (high | normal | low, default normal), status
`queued`, Spec NNN empty. Do not start work.
- **list** — show the table, queued items first by priority, then
in-progress, then the rest. Flag any in-progress item that has no matching
`docs/pipeline/` state file (likely interrupted — suggest
`/feature-resume`).
- **next** — take the highest-priority `queued` item, allocate the next spec
number NNN (max of: highest NNN in `docs/specs/`, highest NNN already
allocated in this table) + 1, set the row to `in-progress` with that NNN,
and run the `/feature` pipeline (`.claude/commands/feature.md`) on the ask,
passing the allocated NNN so Phase 0 uses it. On final acceptance, set the
row to `done`.

If `$ARGUMENTS` is empty, treat it as `list`.
24 changes: 24 additions & 0 deletions .claude/commands/feature-resume.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
description: Resume an interrupted /feature pipeline run from its recorded state
---

Target: **$ARGUMENTS** (a `docs/pipeline/NNN-<slug>.md` path or a feature
number/slug; if empty, pick the most recently modified state file whose
`Status` is not `complete`).

You are the orchestrator, resuming a `/feature` run that was interrupted
(session ended, context compacted, or stopped mid-loop).

1. Read the pipeline state file: `Status`, `Current phase`, the loop
counters, the phase log, and the open-questions log.
2. Re-ground yourself in the artifacts it references: the spec in
`docs/specs/`, any test plan, defects (check each defect's `Status`
field), design review, and evidence directory.
3. If `Status: stopped`, present the recorded open items to the product owner
(AskUserQuestion) before doing anything — a stopped run resumes only with
their direction.
4. Continue the pipeline from `Current phase` following
`.claude/commands/feature.md` exactly — same phase definitions, same
batching rules, same loop bounds (the recorded iteration counters still
count against the max-2 caps). Keep updating the state file after every
phase.
73 changes: 60 additions & 13 deletions .claude/commands/feature.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,48 @@ project subagents (`ux-designer`, `frontend-developer`, `backend-developer`,
- After each phase, read the agent's handoff footer. If OPEN QUESTIONS is not
"none", stop and put the questions to the product owner (AskUserQuestion),
then re-invoke that agent with the answers.
- After each phase, update the pipeline state file (Phase 0) before starting
the next phase — it is what makes the run resumable via `/feature-resume`.
- Do not skip phases, do not do an agent's work yourself, do not commit unless
the product owner asks.

## Phase 0 — Pipeline state file

Determine the next spec number NNN: if `docs/backlog.md` exists and this ask
is (or should be) tracked there, allocate NNN via the backlog (see `/backlog`);
otherwise NNN = highest in `docs/specs/` + 1. Create
`docs/pipeline/NNN-<slug>.md`:

```
# Pipeline state — NNN-<slug>

Status: in-progress <!-- in-progress | complete | stopped -->
Current phase: 1 — Design
QA fix-loop iteration: 0/2
Design fix-loop iteration: 0/2

## Ask

<the product owner's ask, verbatim>

## Phase log

| Phase | Agent | Artifacts | Status | When |
|---|---|---|---|---|

## Open questions log

(none yet)
```

Update `Current phase`, the loop counters, and append a phase-log row after
every phase. On stop (exhausted loop, open questions the owner must answer
offline) set `Status: stopped`; after Phase 7 acceptance set
`Status: complete`.

## Phase 1 — Design

Determine the next spec number NNN (highest in `docs/specs/` + 1). Invoke
`ux-designer` (Mode 1) with the ask and instruct it to write
Invoke `ux-designer` (Mode 1) with the ask and instruct it to write
`docs/specs/NNN-<slug>.md`. Wait, then read the spec. Proceed only when
STATUS is `ready-for-dev`.

Expand All @@ -35,26 +70,36 @@ run backend first and pass its contract block to the frontend afterwards.
## Phase 3 — QA

Invoke `qa-engineer` (Mode 1) with the spec path and the changed-files lists
from both dev handoffs.
from both dev handoffs. Remind it to capture rendered evidence with
`tools/browser.js` into `docs/qa/evidence/NNN-<slug>/` for browser-behavior
criteria — the design verification in Phase 5 consumes those screenshots.

## Phase 4 — QA fix loop (max 2 iterations)

If VERDICT: FAIL — for each defect file, route by its `Area` field:
- `frontend` → `frontend-developer` (Mode 2)
- `backend` → `backend-developer` (Mode 2)
- `design` → `ux-designer` first (spec clarification), then the owning dev
After fixes, invoke `qa-engineer` (Mode 2) to re-verify. If still FAIL after
If VERDICT: FAIL — **batch the routing**: group all open defects by their
`Area` field, then dispatch ONE invocation per owning agent carrying ALL of
that agent's defect paths:
- `frontend` defects → one `frontend-developer` (Mode 2) invocation
- `backend` defects → one `backend-developer` (Mode 2) invocation
- `design` defects → `ux-designer` first (spec clarification), then include
the outcome in the owning dev's batch

When both dev agents have defects, invoke them **in parallel in a single
message** — they own disjoint files. After fixes, invoke `qa-engineer`
(Mode 2) once with every defect path to re-verify. If still FAIL after
2 iterations, stop and report the open defects to the product owner.

## Phase 5 — Design verification

Once QA passes, invoke `ux-designer` (Mode 2) with the spec path to review the
implementation. It writes `docs/design-reviews/NNN-<slug>.md` and returns
APPROVED or CHANGES REQUIRED.
Once QA passes, invoke `ux-designer` (Mode 2) with the spec path and the
evidence directory `docs/qa/evidence/NNN-<slug>/` (if QA produced one). It
writes `docs/design-reviews/NNN-<slug>.md` and returns APPROVED or
CHANGES REQUIRED.

## Phase 6 — Design fix loop (max 2 iterations)

If CHANGES REQUIRED — route findings to the owning dev agent (Mode 2), then
If CHANGES REQUIRED — batch findings by owning area exactly as in Phase 4
(one invocation per dev agent, parallel when both have work), then
`qa-engineer` (Mode 2) for a regression run, then `ux-designer` (Mode 2)
re-review. If still not APPROVED after 2 iterations, stop and report the open
findings to the product owner.
Expand All @@ -63,4 +108,6 @@ findings to the product owner.

Present a summary table: phase · agent · artifacts written · status. State
the final QA verdict and design verdict, list any open items, and ask the
product owner for acceptance. Do not commit unless they ask.
product owner for acceptance. On acceptance, set the state file's
`Status: complete` (and the backlog row to `done`, if one exists). Do not
commit unless they ask.
73 changes: 73 additions & 0 deletions .claude/hooks/check-footer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env node
// SubagentStop hook: mechanically enforces the handoff-footer contract from
// CLAUDE.md — every subagent reply must end with ARTIFACTS WRITTEN / STATUS /
// OPEN QUESTIONS. If the footer is missing, block the stop once so the agent
// finishes properly.
//
// Failure posture: FAIL OPEN (exit 0) on any parse/read error.
'use strict';

const fs = require('node:fs');

const REQUIRED = ['ARTIFACTS WRITTEN', 'STATUS', 'OPEN QUESTIONS'];
// Only the pipeline role agents owe a footer; utility agents are exempt.
const PIPELINE_AGENTS = new Set([
'ux-designer',
'frontend-developer',
'backend-developer',
'qa-engineer',
]);

function lastAssistantText(transcriptPath) {
const lines = fs.readFileSync(transcriptPath, 'utf8').split('\n');
for (let i = lines.length - 1; i >= 0; i--) {
const line = lines[i].trim();
if (!line) continue;
let entry;
try {
entry = JSON.parse(line);
} catch {
continue;
}
const msg = entry.message;
if (entry.type !== 'assistant' || !msg || !Array.isArray(msg.content)) continue;
const text = msg.content
.filter((b) => b.type === 'text')
.map((b) => b.text)
.join('\n');
if (text.trim()) return text;
}
return '';
}

let raw = '';
process.stdin.on('data', (c) => (raw += c));
process.stdin.on('end', () => {
try {
const input = JSON.parse(raw);
if (input.stop_hook_active) process.exit(0); // never loop
if (input.agent_type && !PIPELINE_AGENTS.has(input.agent_type)) process.exit(0);

const text =
input.last_assistant_message || lastAssistantText(input.transcript_path);
if (!text) process.exit(0); // nothing to judge — fail open

const missing = REQUIRED.filter((k) => !text.includes(`${k}:`));
if (missing.length === 0) process.exit(0);

process.stdout.write(
JSON.stringify({
decision: 'block',
reason:
`Your reply is missing the required handoff footer field(s): ${missing.join(', ')}. ` +
'End your reply with the exact footer from CLAUDE.md: ' +
'ARTIFACTS WRITTEN: <paths, or "none"> / STATUS: <role-specific status> / ' +
'OPEN QUESTIONS: <numbered list, or "none">.',
}),
);
process.exit(0);
} catch (err) {
process.stderr.write(`check-footer hook error (failing open): ${err.message}\n`);
process.exit(0);
}
});
Loading
Loading