feature_list.json
The ledger of every feature the product will eventually have. Fresh-context sessions read it to pick work; they flip exactly one boolean when done. JSON, not Markdown — the syntactic strictness is load-bearing. Prose gets edited freely; data gets edited carefully.
Format per entry:
{
"category": "functional",
"description": "New chat button creates a fresh conversation",
"steps": [
"Navigate to main interface",
"Click the 'New Chat' button",
"Verify a new conversation is created",
"Check that chat area shows welcome state",
"Verify conversation appears in sidebar"
],
"passes": false
}
Categories: functional, ux, data, infra. Order the array in implementation order — the top is the next thing to build.
When to apply
- Initializer agent at scaffold time: produce the full list, every entry
passes: false. Aim for breadth — 200 well-scoped entries beats 30 vague ones.
- Coding agent at end of session: after end-to-end verification, flip exactly one
passes from false to true.
- Any session at start:
jq '[.[] | select(.passes==false)] | length' to see remaining work; pick the topmost unblocked entry.
Procedure — initializer
- Enumerate every user-observable behavior the spec implies. Login, list rendering, empty states, error states, keyboard shortcuts, mobile layout — all of it.
- Write each as one entry.
description is one sentence, steps is a user's actual action sequence (not implementation notes).
- Order the array so an agent walking top-to-bottom builds prerequisites before dependents.
- Every
passes starts false. No exceptions, not even for the smoke test.
- Validate:
jq '. | length' returns your count; jq '[.[] | select(.passes==true)] | length' returns 0.
Procedure — coding agent
- Read the file. Do not edit yet.
- Pick the topmost entry with
passes: false and satisfied prerequisites. If the top is blocked, drop to the deepest unblocked entry.
- Implement. Test. Verify end-to-end via the runtime path (browser automation, HTTP, CLI) — not unit tests alone. See [[broken-window-check]] for what E2E means.
- Only after E2E green: flip that single entry's
passes to true.
- Diff the file. The diff must be exactly one
false → true. Nothing else.
Anti-patterns
- Editing
description, steps, or category — it is unacceptable to remove or edit these fields because it lets missing functionality slip past future sessions. The ledger is append-only in every field except passes.
- Flipping
passes: true on unit-test evidence — unit tests pass while routes are misrouted, CORS is broken, or the button is unwired. Only end-to-end evidence flips the bit. See [[verification-before-completion]].
- Flipping multiple entries in one session — the single-feature-per-session rule (see [[one-feature-per-session]]) exists because packed sessions ship everything half-done. One flip per session.
- Adding new entries mid-project without spec change — if scope grew, note it in [[shift-notes]] and flag for a re-scope, don't quietly extend the ledger.
- Deleting "obsolete" entries — if a feature is no longer needed, leave it and mark it
passes: true with a note, or negotiate removal explicitly. Silent deletion breaks priority counting.
- Markdown or YAML instead of JSON — measured in shift-work agent runs, JSON cuts spurious field edits ~7x vs. Markdown and premature-pass marking ~2x. The strictness does the work.
Audit check
At session end, before committing:
git diff feature_list.json | grep -E '^[-+]' | grep -v 'passes'
If this prints anything other than the file header, you edited a forbidden field. Revert those hunks. Only "passes": false ↔ "passes": true lines are allowed to change.
When NOT to apply
- Projects under ~20 features — the overhead of writing the list exceeds the benefit; a flat TODO in [[shift-notes]] is enough.
- Solo one-shot sessions with no handoff — the ledger's whole purpose is cross-session discipline.
Related
- [[shift-notes]] — the prose companion; feature_list.json holds state, shift-notes holds context.
- [[broken-window-check]] — what "end-to-end verified" means before you flip a bit.
- [[one-feature-per-session]] — the rule that limits you to one flip per session.
- [[verification-before-completion]] — the general form of "no bit-flip without runtime evidence".
1---2name: feature-list-json3description: Enumerate every end-to-end feature as strict JSON entries with passes:false, editable-passes-only discipline, and priority order. The ledger fresh-context sessions read to know what's done, what's next, and what they're forbidden to touch.4---56# feature_list.json78The ledger of every feature the product will eventually have. Fresh-context sessions read it to pick work; they flip exactly one boolean when done. JSON, not Markdown — the syntactic strictness is load-bearing. Prose gets edited freely; data gets edited carefully.910Format per entry:1112```json13{14 "category": "functional",15 "description": "New chat button creates a fresh conversation",16 "steps": [17 "Navigate to main interface",18 "Click the 'New Chat' button",19 "Verify a new conversation is created",20 "Check that chat area shows welcome state",21 "Verify conversation appears in sidebar"22 ],23 "passes": false24}25```2627Categories: `functional`, `ux`, `data`, `infra`. Order the array in implementation order — the top is the next thing to build.2829## When to apply3031- **Initializer agent** at scaffold time: produce the full list, every entry `passes: false`. Aim for breadth — 200 well-scoped entries beats 30 vague ones.32- **Coding agent** at end of session: after end-to-end verification, flip exactly one `passes` from `false` to `true`.33- **Any session** at start: `jq '[.[] | select(.passes==false)] | length'` to see remaining work; pick the topmost unblocked entry.3435## Procedure — initializer36371. Enumerate every user-observable behavior the spec implies. Login, list rendering, empty states, error states, keyboard shortcuts, mobile layout — all of it.382. Write each as one entry. `description` is one sentence, `steps` is a user's actual action sequence (not implementation notes).393. Order the array so an agent walking top-to-bottom builds prerequisites before dependents.404. Every `passes` starts `false`. No exceptions, not even for the smoke test.415. Validate: `jq '. | length'` returns your count; `jq '[.[] | select(.passes==true)] | length'` returns 0.4243## Procedure — coding agent44451. Read the file. Do not edit yet.462. Pick the topmost entry with `passes: false` and satisfied prerequisites. If the top is blocked, drop to the deepest unblocked entry.473. Implement. Test. Verify end-to-end via the runtime path (browser automation, HTTP, CLI) — not unit tests alone. See [[broken-window-check]] for what E2E means.484. Only after E2E green: flip that single entry's `passes` to `true`.495. Diff the file. The diff must be exactly one `false` → `true`. Nothing else.5051## Anti-patterns5253- **Editing `description`, `steps`, or `category`** — it is unacceptable to remove or edit these fields because it lets missing functionality slip past future sessions. The ledger is append-only in every field except `passes`.54- **Flipping `passes: true` on unit-test evidence** — unit tests pass while routes are misrouted, CORS is broken, or the button is unwired. Only end-to-end evidence flips the bit. See [[verification-before-completion]].55- **Flipping multiple entries in one session** — the single-feature-per-session rule (see [[one-feature-per-session]]) exists because packed sessions ship everything half-done. One flip per session.56- **Adding new entries mid-project without spec change** — if scope grew, note it in [[shift-notes]] and flag for a re-scope, don't quietly extend the ledger.57- **Deleting "obsolete" entries** — if a feature is no longer needed, leave it and mark it `passes: true` with a note, or negotiate removal explicitly. Silent deletion breaks priority counting.58- **Markdown or YAML instead of JSON** — measured in shift-work agent runs, JSON cuts spurious field edits ~7x vs. Markdown and premature-pass marking ~2x. The strictness does the work.5960## Audit check6162At session end, before committing:6364```bash65git diff feature_list.json | grep -E '^[-+]' | grep -v 'passes'66```6768If this prints anything other than the file header, you edited a forbidden field. Revert those hunks. Only `"passes": false` ↔ `"passes": true` lines are allowed to change.6970## When NOT to apply7172- Projects under ~20 features — the overhead of writing the list exceeds the benefit; a flat TODO in [[shift-notes]] is enough.73- Solo one-shot sessions with no handoff — the ledger's whole purpose is cross-session discipline.7475## Related7677- [[shift-notes]] — the prose companion; feature_list.json holds state, shift-notes holds context.78- [[broken-window-check]] — what "end-to-end verified" means before you flip a bit.79- [[one-feature-per-session]] — the rule that limits you to one flip per session.80- [[verification-before-completion]] — the general form of "no bit-flip without runtime evidence".