Feature story spec
Context, user-visible behavior, a scope fence, machine-checkable acceptance criteria, and an approval line. Shown filled in with a password-reset example.
# Feature spec: Password reset via email
> **Status:** draft → approved (agent may not start while draft)
> **Story:** STORY-142 · **Branch:** `feature/password-reset`
> **Requested by:** Kylian · **Agent:** Claude Code
## Context — why this exists
Users who forget their password currently email support and wait up to a
business day for a manual reset. We already send transactional email through
Postmark, so a self-serve flow removes that queue entirely. This is the top
recurring support request this quarter.
## User-visible behavior
- On `/login`, a "Forgot password?" link opens `/forgot-password`.
- Submitting any email shows the same confirmation message — "If an account
exists for that address, we sent a reset link." No account enumeration.
- The email contains a single-use link valid for **30 minutes**.
- The link opens `/reset-password?token=…` with a new-password form
(minimum 12 characters, confirmation field, inline validation).
- On success: redirect to `/login` with a success banner, and **all existing
sessions for that user are revoked**.
## Scope
- `POST /api/auth/forgot-password` — issue token, send email.
- `POST /api/auth/reset-password` — validate token, set password, revoke sessions.
- New `password_reset_tokens` table: hashed token, user id, expires_at, used_at.
- Two pages: `/forgot-password` and `/reset-password`.
- One email template using the existing Postmark layout in `emails/`.
## Out of scope — do NOT touch
- The login form itself, beyond adding the one link.
- SSO / OAuth accounts (show "managed by your identity provider" instead).
- Rate-limiting infrastructure — reuse the existing `rateLimit()` helper.
- Password strength meter UI — that is STORY-155.
## Acceptance criteria (machine-checkable)
- [ ] Requesting a reset for an existing email creates exactly one token row and sends exactly one email.
- [ ] Requesting a reset for an unknown email returns 200 with a byte-identical response body.
- [ ] A token older than 30 minutes is rejected with 400 `TOKEN_EXPIRED`.
- [ ] A token is single-use: the second attempt returns 400 `TOKEN_USED`.
- [ ] A successful reset updates the password hash and deletes every session row for that user.
- [ ] A new password under 12 characters is rejected with a field-level error, not a toast.
- [ ] Lint and typecheck pass on the branch.
## Verification — run before opening the PR
```bash
npm test -- --grep "password reset"
npm run lint && npx tsc --noEmit
```
## Deliverable
One PR from `feature/password-reset` into `main`: code, tests, and the email
template. PR description generated from this spec (what/why, criteria
checklist with pass/fail, test evidence).
## Approval
- [ ] Spec approved by ____________ on ____-__-__ — the agent starts only after this box is checked.
Bugfix spec
Observed vs expected, reproduction steps, and a regression-test-first rule: the agent must write a failing test before touching the fix.
Refactor spec
Motivation, invariants that must not change, a safety net of characterization tests, a scope fence, and a mechanical step-by-step outline.
Epic breakdown
Goal, constraints, and a dependency-aware story table built for slicing an epic across parallel agents — contract stories first, lanes after.
Agent PR description
A PR body generated from the spec: what & why, the acceptance-criteria checklist with pass/fail, test evidence, and review guidance.
Why templates beat blank pages for agent work
A blank page produces a different document every time — different sections, different levels of detail, different things silently left out. That variance is annoying between humans; with AI agents it compounds, because the spec is the prompt. An agent given an under-specified story fills the gaps with plausible guesses, and you discover which guesses at diff-review time.
The quieter payoff is reviewability. When every bugfix spec has the same shape, your reviewer — human or LLM — learns one shape. They stop parsing structure and start checking substance: is the scope fence right, is the repro real, did the failing test come first. Ten differently-shaped documents get ten shallow reviews; ten identical shapes get ten fast, deep ones. Templates also make omissions visible: an empty “Out of scope” heading is a question, while a missing one is invisible.
What makes acceptance criteria machine-checkable
An acceptance criterion is machine-checkable when pass/fail can be decided without asking the author. That means naming a concrete input and an observable output — a status code, an error code, a test name — instead of a quality adjective. Compare:
# Vague — a human has to interpret it, an agent will rationalize it
- [ ] Password reset works properly and is secure
# Machine-checkable — a test can decide it, and probably already does
- [ ] A token older than 30 minutes is rejected with 400 TOKEN_EXPIRED
- [ ] A token is single-use: the second attempt returns 400 TOKEN_USEDThe vague version invites the agent to declare victory; the precise version converts almost line-for-line into test cases, which is exactly what you want the agent to write. A useful habit: if a criterion cannot be paired with a command in the spec’s verification block, rewrite it until it can. The feature and bugfix templates above are built around that rule.
How the epic template maps to parallel agents
The epic breakdown’s story table is not project-management decoration — the three middle columns are a scheduling algorithm. “Depends on” plus “parallel?” tells you which stories can become simultaneous agent lanes and which must serialize. The rule of thumb: shared-contract stories serialize, everything downstream parallelizes.
Contract stories run alone
Lane stories run together
The tell-tale smell
Spec status is the gate
The full playbook for running those lanes — branch naming, merge order, what to do when a lane stalls — is in our multi-agent coding workflow guide, and the git mechanics live in parallel agents with git worktrees.
Templates are the paper version of a workflow
Each template here is a stage of the same pipeline, frozen into a document: the story spec is the plan, the approval line is the gate, the acceptance criteria are the tests, and the PR description is the receipt. Used together, they give you the whole of spec-driven AI development with nothing but markdown files and discipline — no tooling required.
Paper has real limits, though: nothing stops an agent that skips the approval line, and nobody reconciles the PR checklist against the spec except you. If you want the spec generated interactively instead of edited by hand, try the agent spec builder; for the git-to-merge half of the pipeline, the AI PR automation guide covers how the PR description template gets filled and reviewed. Start on paper — it is the cheapest way to learn which sections your team actually needs.