Progress was only the synthetic milestone entry (auto-emitted at
open_pr/i_am_done); agents never deliberately reported and the % was
an ungated free-form guess.
Now the plan's sub_tasks ARE the progress skeleton:
- progress() gains optional `plan_step` (a sub_task id or its 1-based
order). With it, that step is marked completed and the percentage is
DERIVED as completed/total (equal weight) via new
TaskService.record_plan_progress — the agent cannot set/game it.
- A narrative entry WITHOUT plan_step is allowed for important
mid-step documentation and carries the current derived % (the bar
never regresses). No hard anti-spam gate (would loop minimax) —
prompt guidance steers "meaningful moments, not every tool call".
- `percentage` is now an optional fallback, used only for tasks with
no sub_task checklist (back-compat). v2 ProgressRequest, the do.py
route, and the do_server MCP tool updated accordingly.
- An unmatched plan_step returns invalid_state listing the valid step
refs (resolve by id / order / 1-based index).
- developer + documenter prompts updated to the plan_step workflow.
- Helpers extracted (_plan_subtasks/_derive_plan_pct/_valid_step_refs/
_mark_subtask_complete) to keep record_plan_progress within the
cyclomatic gate.
Commit 3 of 3 for the plan/progress quality work (#171/#172/#173).