PRD Format Reference
Complete format documentation for prd.md.
Story Heading Format
Each user story is defined by a level-3 (or level-4) markdown heading with an ID and title:
### ID: TitleThe ID must match the pattern LETTERS-NUMBERS (one or more letters, a hyphen, then one or more digits). Headings whose ID doesn't fit this pattern are treated as regular prose, not stories.
Examples:
### US-001: User Registration
### AUTH-003: Password Reset Flow
### BUG-012: Fix Login Redirect
#### US-042: Also works as a level-4 headingRecommended: give each PRD its own prefix. IDs only need to be unique within a PRD, and numbering restarts per PRD — so two PRDs that both use the generic US- prefix will each have a US-001. Prefer a short prefix derived from the PRD's feature/domain (AUTH-, BILL-, PRIO-) so stories read unambiguously across PRDs. This is only a readability convention: Chief already namespaces each story's commit as feat: <prd-name>/<ID> - <title> (where <prd-name> is the PRD's directory under .chief/prds/), so it never confuses same-numbered stories from different PRDs even if the prefixes collide.
Story Fields
Below each story heading, Chief recognizes these bold-label fields:
| Field | Format | Required | Default | Description |
|---|---|---|---|---|
| Status | **Status:** value | No | todo | Current state: done, in-progress, todo, needs-review, or blocked |
| Blocked reason | **Blockiert (Ben):** text | No | — | Written by Chief under a blocked status: what blocks the story and what to do about it. Removed when the status changes |
| Priority | **Priority:** N | No | Document order | Execution order (lower = higher priority) |
| Blocked by | **Blocked by:** ID, ID | No | none | Story IDs that must be done before this story may start |
| Braucht | **Braucht:** OS (why) | No | any system | The operating system the story can only be done on, e.g. macOS (Xcode, signing) or Linux (systemd). See needs |
| Description | **Description:** text | No | — | Story description (or use freeform prose) |
Acceptance Criteria
Acceptance criteria use markdown checkboxes:
- [ ] Criterion not yet met
- [x] Criterion completedChief reads checkbox state to track progress. The agent checks boxes as it completes each criterion.
Status Values
| Value | Aliases (also accepted) | Meaning |
|---|---|---|
done | complete, completed, passed | Story is complete — Chief skips it |
in-progress | in progress, started | Agent is actively working on this story |
todo | (anything unrecognized falls back to this) | Story is pending (also the default if Status is absent) |
needs-review | needs review | Chief parked the story (e.g. after repeated failed attempts). Skipped by the loop and flagged with ◆ in the TUI until a human resets it. |
blocked | — | The agent stopped on something only a person can resolve and said why with <chief-blocked>. Chief writes the reason under the status as **Blockiert (Ben):** …. Skipped by the loop, and so is every story that depends on it, until a person sets it back to todo — chief prep asks about each one. Flagged with ⊘ in the TUI. |
Status matching is case-insensitive. Any value Chief doesn't recognize is treated as todo rather than raising an error.
Full Example
# User Authentication
## Overview
Complete auth system with login, registration, and password reset.
## Technical Context
- Backend: Express.js with TypeScript
- Database: PostgreSQL with Prisma ORM
- Auth: JWT tokens in httpOnly cookies
## User Stories
### US-001: User Registration
**Status:** done
**Priority:** 1
**Description:** As a new user, I want to register an account so that I can access the application.
- [x] Registration form with email and password fields
- [x] Email format validation
- [x] Password minimum 8 characters
- [x] Confirmation email sent on registration
- [x] User redirected to login after registration
### US-002: User Login
**Status:** todo
**Priority:** 2
**Description:** As a registered user, I want to log in so that I can access my account.
- [ ] Login form with email and password fields
- [ ] Error message for invalid credentials
- [ ] Remember me checkbox
- [ ] Redirect to dashboard on successField Details
id (from heading)
Parsed from the story heading: ### US-001: Title → id is US-001.
Format: LETTERS-NUMBERS (one or more letters, a hyphen, one or more digits). Headings that don't match aren't parsed as stories.
Example: US-001, US-042, AUTH-001
title (from heading)
Parsed from the story heading: ### US-001: User Registration → title is User Registration.
Length: Keep under 50 characters for clean commit messages.
description
The text after **Description:**, or freeform prose between the heading and the first checkbox list.
Format: "As a [user], I want [feature] so that [benefit]." recommended but not required.
acceptanceCriteria (checkboxes)
The - [ ] / - [x] items under each story heading. The agent uses these to know when the story is complete.
Guidelines:
- Specific and testable
- One requirement per item
- 3-7 items per story
priority
Lower numbers = higher priority. Chief always picks the incomplete story with the lowest priority number first. If omitted, stories are selected in document order.
Range: Any positive number (integers or decimals). A value that isn't a positive number is ignored, and the story falls back to document order.
needs
The operating system a story can only be done on, and why: **Braucht:** macOS (Xcode, signing), **Braucht:** Linux (systemd), **Braucht:** Windows (installer). **Needs:** and **Requires:** are read as well. Internally the parsed field is needs.
Only the system is compared, deterministically, with the one Chief runs on. It is found as a word anywhere in the value, in any case — macOS 14+, macOS/Xcode, Xcode (macOS) all name macOS (macOS, Mac, Mac OS X, OS X, OSX, darwin → macOS; Linux, Ubuntu, Debian → Linux; Windows); the rest is for the reader. A story for another system is skipped like a parked one — on a box, which runs Linux, every macOS story is — and so is every story that depends on it. Locally on a Mac the same stories run normally. A value naming no system Chief knows, or more than one, is ignored, so a typo never keeps a story from running at all — but chief start, chief box up and the box's run log warn about it, since the story then runs everywhere.
chief prep writes these lines for you and offers to split a story whose code runs anywhere from the part that needs one system, so the stories after it are not held back.
blockedBy
A comma-separated list of story IDs that must be done before this story may start. Omit the line entirely for stories with no dependencies. Internally the parsed field is blockedBy.
Format: **Blocked by:** US-001, US-002 — one or more story IDs separated by commas.
Effect on selection: Chief selects the next story from the frontier — the stories that are not done, not needs-review, and whose every Blocked by ID is already done. A blocked story stays off the frontier (and is never picked) until all of its blockers complete, regardless of its priority. Priority only orders stories that are already on the frontier. See Story Selection Logic for the full algorithm.
Robustness rules: parsing is lenient so an authoring mistake can't deadlock the loop.
- An unknown or misspelled blocker ID (matching no story in the PRD) is treated as satisfied and ignored.
- A self-reference (a story listing its own ID) is ignored.
- Duplicate IDs are harmless.
- If a dependency cycle (or a chain of stories all blocked by a parked
needs-reviewstory) leaves the frontier empty while unfinished, non-parked work still remains, Chief falls back to the lowest-priority unfinished, non-parked story so the loop always makes progress. - A story that depends on a
blockedstory — directly or through others — is never picked, not even by that fallback: it waits until the blocked story is released and done.
status
Tracked by Chief. Set to in-progress when work begins, done when the agent outputs <chief-done/> and a matching commit lands, needs-review when a story is parked after repeated failed attempts, and blocked at once when the agent outputs <chief-blocked>reason</chief-blocked>.
Values: done, in-progress, todo (default if absent), needs-review, blocked — plus the case-insensitive aliases listed under Status Values
Parsing Behavior
Chief parses prd.md leniently rather than validating it against a strict schema. It reads the markdown structure and extracts whatever it recognizes; it does not reject a PRD for missing or malformed fields. Concretely:
- Only file-read errors fail. If
prd.mdcan be read, parsing succeeds. There's no separate validation step that exits with an error for structural problems. - Unrecognized headings are skipped. A heading whose ID doesn't match
LETTERS-NUMBERSis treated as prose, not a story — so a typo silently drops the story rather than raising an error. - Unknown status values fall back to
todoinstead of erroring. - Non-positive or non-numeric priorities are ignored, leaving the story in document order.
- Duplicate IDs are not detected. Chief keeps every parsed story as-is; it does not deduplicate or warn.
Blocked byparsing is lenient. Unknown or misspelled blocker IDs are ignored rather than raising an error, and a story can't block itself — so a typo in aBlocked byline can't deadlock the loop.
Because of this, the most common "why isn't my story being picked up?" cause is a heading whose ID doesn't fit the LETTERS-NUMBERS pattern. See Common Issues → Invalid PRD Format for how to spot it.