Theme
Lesson 04 — Small state machines and invariants
Outcome
Implement and test the canonical Task lifecycle without agent code.
Why this comes now
If state transitions are left as arbitrary string updates, every future screen and service can create impossible combinations.
Understand
The Task lifecycle is deliberately small: BACKLOG → READY → WORKING ⇄ NEEDS_YOU, WORKING → REVIEW ⇄ WORKING, REVIEW → DONE. Planning, verification, review progress, agent startup, and worktree preparation are separate lifecycles.
Methods should express intent: MakeReady, StartWork, NeedHuman, ResumeWork, SubmitForReview, RequestChanges, Approve rather than SetStatus.
Build the real project
- Encode allowed transitions in domain methods.
- Return typed/actionable errors for illegal transitions.
- Write exhaustive transition tests.
- Add placeholder policy input where approval will later need current Review/Verification evidence, but do not fake those subsystems yet.
- Keep a clear test showing that “agent finished” is not a Task transition because there is no agent yet.
Completion gate
Every valid transition is tested; every invalid edge is rejected. There is no public unrestricted status setter used by application code.
Pitfalls to avoid
Avoid one giant transition table if named methods remain clearer. Avoid adding statuses to represent loading spinners.
References
STATE_MACHINES.md, Making illegal states unrepresentable (concept).
Checkpoint
Update the current status. You should be able to explain why Review is a Task state but “human_review_in_progress” is not.