* docs(PBI-58): add developer manual chapters under docs/manual/ Adds a 7-file English-language manual targeted at new human contributors: index, overview, statuses & transitions (with mermaid state diagrams), git workflow, MCP integration, docker, and troubleshooting. The manual is the *map* — it cross-references existing runbooks/ADRs/architecture docs rather than duplicating their content. Regenerates docs/INDEX.md and validates with check-doc-links.mjs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(PBI-58): add markdown rendering deps + manual:build script Adds mermaid, rehype-slug, rehype-autolink-headings for the in-app /manual page. Wires manual:build into prebuild so production builds always regenerate the chapter TOC. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(PBI-58): codegen script for in-app manual TOC scripts/build-manual.mjs walks docs/manual/, parses YAML front-matter, strips it from the body, and emits lib/manual.generated.ts with a typed ManualEntry[] containing slug, title, description, filePath, and the embedded markdown body. Pure Node 20, mirrors generate-docs-index.mjs. Inlining the markdown at build time keeps runtime serverless functions free of filesystem reads, which avoids whole-project NFT tracing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(PBI-58): /manual route renders developer manual chapters in-app Catch-all route at app/(app)/manual/[[...slug]]/page.tsx with generateStaticParams covering every TOC entry. Server-side MarkdownView uses react-markdown with remark-gfm, rehype-slug, and rehype-autolink-headings; mermaid code blocks are routed to a client-only MermaidBlock that dynamic-imports mermaid on mount. ManualSidebar (client) reads the typed TOC and highlights the active chapter via usePathname. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(PBI-58): add Manual link to main nav bar Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
10 KiB
| title | status | audience | language | last_updated | when_to_read | |
|---|---|---|---|---|---|---|
| Statuses & Transitions | active |
|
en | 2026-05-07 | Whenever an entity's status changes unexpectedly or you need to know what status comes next. |
02 — Statuses & Transitions
Every persistent entity in Scrum4Me has an explicit status enum. This chapter documents them all, with state-machine diagrams showing allowed transitions, the trigger for each transition (user action vs system / job-driven), and the side effects.
Hardstop: the database stores enums in
UPPER_SNAKE; the REST API exposes them inlowercase. Conversion happens only throughlib/task-status.ts— never call.toLowerCase()or.toUpperCase()directly. See the DB vs API mapping section at the end.
Quick reference
| Entity | Source enum | Statuses |
|---|---|---|
| PBI | PbiStatus |
READY, BLOCKED, DONE, FAILED |
| Story | StoryStatus |
OPEN, IN_SPRINT, DONE, FAILED |
| Task | TaskStatus |
TO_DO, IN_PROGRESS, REVIEW, DONE, FAILED |
| Sprint | SprintStatus |
ACTIVE, COMPLETED, FAILED |
| SprintRun | SprintRunStatus |
QUEUED, RUNNING, PAUSED, DONE, FAILED, CANCELLED |
| ClaudeJob | ClaudeJobStatus |
QUEUED, CLAIMED, RUNNING, DONE, FAILED, CANCELLED, SKIPPED |
| Idea | IdeaStatus |
DRAFT, GRILLING, GRILL_FAILED, GRILLED, PLANNING, PLAN_FAILED, PLAN_READY, PLANNED |
PBI
A Product Backlog Item holds one or more stories. Its status reflects whether the PBI as a whole is ready to be picked up, blocked on something external, finished, or written off.
stateDiagram-v2
[*] --> READY: create_pbi
READY --> BLOCKED: user marks blocked
BLOCKED --> READY: user unblocks
READY --> DONE: all stories DONE
READY --> FAILED: user gives up
BLOCKED --> FAILED: user gives up
DONE --> [*]
FAILED --> [*]
| Transition | Trigger | Side effect |
|---|---|---|
* → READY |
create_pbi MCP tool or PBI dialog |
New PBI lands in priority group, sort_order = last + 1 |
READY ↔ BLOCKED |
User toggles via PBI dialog | None besides log entry |
READY → DONE |
All child stories reach DONE |
Auto-promotion (see ST-1109 plan) |
* → FAILED |
User gives up on the PBI | Stories may remain OPEN; PBI is filtered out of active boards |
Story
A Story sits under a PBI. It moves out of the backlog when added to a Sprint, and reaches DONE when its tasks are complete and the implementation is verified.
stateDiagram-v2
[*] --> OPEN: create_story
OPEN --> IN_SPRINT: added to sprint
IN_SPRINT --> OPEN: removed from sprint
IN_SPRINT --> DONE: all tasks DONE + verify passes
IN_SPRINT --> FAILED: verify fails / abandoned
DONE --> [*]
FAILED --> [*]
| Transition | Trigger | Side effect |
|---|---|---|
* → OPEN |
create_story MCP tool or Story dialog |
Lives in product backlog |
OPEN ↔ IN_SPRINT |
Drag onto Sprint board, or sprint-removal | Tasks denormalise sprint_id |
IN_SPRINT → DONE |
Story completion via MCP / UI; auto-PR flow may trigger | Auto-PR flow (runbooks/auto-pr-flow.md) may run; PBI is re-evaluated for READY → DONE |
IN_SPRINT → FAILED |
Verification failure or manual abandon | Logged in story log |
Task
A Task is the smallest unit. The Claude worker mainly reads implementation_plan and writes status transitions through MCP tools.
stateDiagram-v2
[*] --> TO_DO: create_task
TO_DO --> IN_PROGRESS: agent claims / user starts
IN_PROGRESS --> REVIEW: implementation done, awaiting verify
REVIEW --> DONE: verify passes
REVIEW --> IN_PROGRESS: verify fails, retry
IN_PROGRESS --> FAILED: unrecoverable error
REVIEW --> FAILED: gives up after retries
DONE --> [*]
FAILED --> [*]
| Transition | Trigger | Side effect |
|---|---|---|
* → TO_DO |
create_task MCP tool / Task dialog |
Inherits sprint_id from parent story |
TO_DO → IN_PROGRESS |
Worker claim or user starts | Story may auto-promote to IN_SPRINT |
IN_PROGRESS → REVIEW |
Implementation logged | Optional verify_task_against_plan runs |
REVIEW → DONE |
Verify passes / human accepts | When all sibling tasks are DONE, the parent story is eligible for DONE |
* → FAILED |
Unrecoverable error or human marks failed | Story may auto-promote to FAILED |
The MCP tool is update_task_status({ task_id, status }) accepting lowercase API values: todo | in_progress | review | done | failed.
Sprint
A Sprint is the cross-cutting time-box. Its status tracks the overall sprint container, not the agent execution.
stateDiagram-v2
[*] --> ACTIVE: create sprint
ACTIVE --> COMPLETED: user closes sprint
ACTIVE --> FAILED: user abandons sprint
COMPLETED --> [*]
FAILED --> [*]
For execution semantics (PER_TASK vs SPRINT_BATCH) see docs/architecture/sprint-execution-modes.md.
SprintRun
A SprintRun is one execution attempt of a sprint by the agent worker. Multiple runs may exist over a sprint's lifetime (if a run is cancelled or paused and restarted).
stateDiagram-v2
[*] --> QUEUED: trigger sprint run
QUEUED --> RUNNING: worker claims
RUNNING --> PAUSED: pause requested
PAUSED --> RUNNING: resume
RUNNING --> DONE: all tasks done
RUNNING --> FAILED: unrecoverable
QUEUED --> CANCELLED: user cancels
RUNNING --> CANCELLED: user cancels
PAUSED --> CANCELLED: user cancels
DONE --> [*]
FAILED --> [*]
CANCELLED --> [*]
The cascade rules (which task transitions automatically promote the SprintRun) are described in docs/plans/sprint-pr-worktree-state-machines.md. When calling update_task_status from inside a sprint run, pass the optional sprint_run_id so the server can validate ownership and propagate cascades.
ClaudeJob
The agent job queue (M13). Each enqueued unit of work is a ClaudeJob with a kind (TASK_IMPLEMENTATION, IDEA_GRILL, IDEA_MAKE_PLAN, PLAN_CHAT, SPRINT_IMPLEMENTATION).
stateDiagram-v2
[*] --> QUEUED: enqueue
QUEUED --> CLAIMED: wait_for_job (FOR UPDATE SKIP LOCKED)
CLAIMED --> RUNNING: worker starts
RUNNING --> DONE: update_job_status('done')
RUNNING --> FAILED: update_job_status('failed')
QUEUED --> CANCELLED: user cancels
CLAIMED --> QUEUED: stale (>30min)
QUEUED --> SKIPPED: superseded
DONE --> [*]
FAILED --> [*]
CANCELLED --> [*]
SKIPPED --> [*]
| Transition | Trigger | Side effect |
|---|---|---|
QUEUED → CLAIMED |
wait_for_job atomically claims |
Bearer token is bound to the job (claimed_by_token_id) |
CLAIMED → QUEUED |
Stale claim (>30 min) | Auto-requeue on next wait_for_job |
RUNNING → DONE |
update_job_status('done') |
Optional token-cost telemetry stored on the row |
RUNNING → FAILED |
update_job_status('failed') |
For IDEA_GRILL/IDEA_MAKE_PLAN, idea status auto-rolls to GRILL_FAILED / PLAN_FAILED |
For idempotency rules and recovery procedures see docs/runbooks/worker-idempotency.md.
Idea
The Idea entity (M12) is a pre-PBI staging area. It goes through two AI-driven phases: a grill (Q&A loop with the user to clarify the idea) and a plan (single-pass output of a structured PBI tree). Failures are explicit terminal-ish states that allow retry.
stateDiagram-v2
[*] --> DRAFT: create idea
DRAFT --> GRILLING: enqueue IDEA_GRILL
GRILLING --> GRILLED: update_idea_grill_md
GRILLING --> GRILL_FAILED: job failed
GRILL_FAILED --> GRILLING: retry
GRILLED --> PLANNING: enqueue IDEA_MAKE_PLAN
PLANNING --> PLAN_READY: update_idea_plan_md (parse ok)
PLANNING --> PLAN_FAILED: parsePlanMd rejected
PLAN_FAILED --> PLANNING: retry
PLAN_READY --> PLANNED: PBI tree created
PLANNED --> [*]
| Transition | Trigger | Side effect |
|---|---|---|
DRAFT → GRILLING |
User clicks "Grill" | Enqueues IDEA_GRILL job; worker reads prompt_text + idea.grill_md |
GRILLING → GRILLED |
update_idea_grill_md |
Logs IdeaLog{GRILL_RESULT} |
* → GRILL_FAILED |
update_job_status('failed') for IDEA_GRILL |
Idea remains usable; user can retry |
GRILLED → PLANNING |
User clicks "Make plan" | Enqueues IDEA_MAKE_PLAN; worker outputs strict YAML-frontmatter |
PLANNING → PLAN_READY |
update_idea_plan_md parse ok |
Logs IdeaLog{PLAN_RESULT} |
PLANNING → PLAN_FAILED |
parsePlanMd rejected |
Logs IdeaLog{JOB_EVENT, errors} |
PLAN_READY → PLANNED |
PBI tree generated from plan | Idea is archived; PBI/Story/Task tree appears in the backlog |
For the full Idea workflow, prompts, and prompt_text contents, see docs/plans/M12-ideas.md.
DB vs API mapping
Hardstop: never bypass
lib/task-status.ts.
The database stores enums in UPPER_SNAKE (TO_DO, IN_PROGRESS, IN_SPRINT, …) because Prisma + PostgreSQL prefer that convention. The REST API exposes them in lowercase (todo, in_progress, in_sprint, …) because that's the convention HTTP consumers expect.
The two are mapped only through the helpers in lib/task-status.ts:
taskStatusToApi(status) // DB → API
taskStatusFromApi(input) // API → DB (returns null on bad input)
storyStatusToApi(status)
storyStatusFromApi(input)
pbiStatusToApi(status)
pbiStatusFromApi(input)
sprintStatusToApi(status)
sprintStatusFromApi(input)
sprintRunStatusToApi(status)
sprintRunStatusFromApi(input)
Bad input on the inbound side (*FromApi) returns null — the route handler converts that to a 422 Zod-style error. See docs/adr/0004-status-enum-mapping.md for the rationale.
What's next
→ 03 — Git Workflow covers branching, commits, and the cost-driven PR rules.