feat(plans): milestone-as-plan-container; retire kind=plan (T3)
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 22s
CI & Build / Python tests (push) Successful in 42s
CI & Build / Build & push image (push) Successful in 59s

The milestone becomes the plan container: a new nullable milestones.body
holds the design/intent (Goal/Approach/Verification) and individual steps
live as first-class child tasks (milestone_id) instead of checkboxes crammed
into one kind=plan task body. start_planning now creates a MILESTONE seeded
with the body template (not a kind=plan task) and returns it with applicable
rules; a new get_milestone MCP tool reads the plan back (body + steps + rules).

kind=plan is hard-retired going forward — start_planning never creates one.
The 'plan' task_kind enum value stays valid so the 11 historical plan-tasks
remain readable in place; no body-shredding backfill (corpus review showed
auto-splitting their checklists into tasks would be lossy: embedded code
blocks, a non-binary [~] state, tables, ID-encoded hierarchy).

- migration 0066: add milestones.body
- model/service/route/MCP: body passthrough on create+update; get_milestone
- server _INSTRUCTIONS: "plan" = milestone w/ body + child step-tasks
- UI: ProjectView shows/edits a milestone's plan body; start_planning expands
  the new milestone and opens its plan editor
- tests updated to the milestone contract + new body/get_milestone coverage

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-14 12:22:22 -04:00
parent c972af2690
commit 1f6c592226
13 changed files with 336 additions and 50 deletions
+23 -13
View File
@@ -17,14 +17,21 @@ Hierarchy: Project -> Milestone -> Task/Note.
What each part is for, and when to reach for it:
- Project: the top-level container for a body of work.
- Milestone: groups related tasks within a project toward a goal (status
active/done). Use one when a chunk of work needs its own arc.
active/done). A milestone is ALSO the home of a plan — its `body` holds the
design/intent (Goal/Approach/Verification) and its child tasks are the steps.
Use one when a chunk of work needs its own arc.
- Task: a unit of actionable work with a lifecycle (status
todo/in_progress/done/cancelled, optional priority). A task is a note with a
status — reach for one when there is something to DO. Record progress over
time with work-logs (add_task_log) rather than rewriting the body.
- Plan: a task with kind=plan — HOW you'll execute a chunk of work. The body
holds the design + step checklist; work-logs record progress. Start one with
start_planning when beginning non-trivial work, before you dive in.
- Plan: a MILESTONE acting as a plan container — HOW you'll execute a chunk of
work. The design/intent lives in the milestone `body`; each step is its own
child task (create_task(milestone_id=...)), tracked with status + work-logs —
NOT a checkbox buried in the body. Start one with start_planning when
beginning non-trivial work, before you dive in; read it back with
get_milestone (body + steps). (The old kind=plan task is retired — some
historical plan-tasks still exist and remain readable, but don't create new
ones.)
- Note: durable free-form knowledge — reference material, decisions, logs of
what happened.
No lifecycle, not actionable. Reach for one to CAPTURE something worth keeping.
@@ -147,14 +154,17 @@ adopting or creating — never do either silently, and never guess a project int
existence. Once a project is in scope, the enter_project handshake and the
host-memory pointer step above both apply.
Plans are tasks with kind=plan, and Scribe is the canonical home for them.
When you begin non-trivial work, call start_planning(project_id, title) FIRST —
before any brainstorming, design, or plan-writing skill runs. start_planning
seeds the plan body, returns the project's applicable_rules, and gives you the
task id you'll write into. If a habit tells you to save a plan or spec to a local
`.md` file, that's superseded here: put the spec/plan content in the kind=plan
task's body via update_task, and record progress with add_task_log. Local .md
files are not the record — the task is.
A plan is a MILESTONE, and Scribe is the canonical home for it. When you begin
non-trivial work, call start_planning(project_id, title) FIRST — before any
brainstorming, design, or plan-writing skill runs. start_planning creates the
milestone, seeds its `body` with the design template, returns the project's
applicable_rules, and gives you the milestone id you'll write into. Put the
design/intent in the milestone body via update_milestone(milestone_id, body=...);
create each step as a child task with create_task(milestone_id=...) and track it
with status + add_task_log — do NOT list steps as checkboxes in the body. Read
the plan back with get_milestone (body + steps). If a habit tells you to save a
plan or spec to a local `.md` file, that's superseded here: the milestone is the
record, not a local file.
Deletes are recoverable: every delete_* tool moves the entity (and its
descendants) to the trash and returns a deleted_batch_id. Use list_trash() to
@@ -180,7 +190,7 @@ operator. "Works for one user" is not done.
# until explicitly classified here.
_READ_ONLY_TOOLS = frozenset({
"get_event", "get_note", "get_project", "get_rule", "get_rulebook",
"get_task", "get_recent", "enter_project",
"get_task", "get_milestone", "get_recent", "enter_project",
"list_events", "list_lists", "list_milestones", "list_notes",
"list_persons", "list_places", "list_projects", "list_rulebooks",
"list_rules", "list_tags", "list_tasks", "list_topics", "list_trash",