PBI-58: Developer manual + in-app /manual page (#148)

* 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>
This commit is contained in:
Janpeter Visser 2026-05-07 18:00:10 +02:00 committed by GitHub
parent d750676f5e
commit bd7478861b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 2239 additions and 105 deletions

View file

@ -0,0 +1,222 @@
---
title: "Statuses & Transitions"
status: active
audience: [contributor]
language: en
last_updated: 2026-05-07
when_to_read: "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 in `lowercase`. Conversion happens **only** through [`lib/task-status.ts`](../../lib/task-status.ts) — never call `.toLowerCase()` or `.toUpperCase()` directly. See the [DB vs API mapping](#db-vs-api-mapping) section at the end.
## Quick reference
| Entity | Source enum | Statuses |
|---|---|---|
| [PBI](#pbi) | `PbiStatus` | `READY`, `BLOCKED`, `DONE`, `FAILED` |
| [Story](#story) | `StoryStatus` | `OPEN`, `IN_SPRINT`, `DONE`, `FAILED` |
| [Task](#task) | `TaskStatus` | `TO_DO`, `IN_PROGRESS`, `REVIEW`, `DONE`, `FAILED` |
| [Sprint](#sprint) | `SprintStatus` | `ACTIVE`, `COMPLETED`, `FAILED` |
| [SprintRun](#sprintrun) | `SprintRunStatus` | `QUEUED`, `RUNNING`, `PAUSED`, `DONE`, `FAILED`, `CANCELLED` |
| [ClaudeJob](#claudejob) | `ClaudeJobStatus` | `QUEUED`, `CLAIMED`, `RUNNING`, `DONE`, `FAILED`, `CANCELLED`, `SKIPPED` |
| [Idea](#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.
```mermaid
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](../plans/ST-1109-pbi-status.md)) |
| `* → 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.
```mermaid
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`](../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.
```mermaid
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.
```mermaid
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`](../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).
```mermaid
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`](../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`).
```mermaid
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`](../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.
```mermaid
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`](../plans/M12-ideas.md).
## DB vs API mapping
> **Hardstop:** never bypass [`lib/task-status.ts`](../../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`](../../lib/task-status.ts):
```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`](../adr/0004-status-enum-mapping.md) for the rationale.
## What's next
→ [03 — Git Workflow](./03-git-workflow.md) covers branching, commits, and the cost-driven PR rules.