feat(mcp): enter_project names active milestones with no steps as open work (#4076)
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / Build & push image (push) Canceled after 0s
CI & Build / integration (push) Canceled after 42s
CI & Build / TypeScript typecheck (push) Canceled after 42s
CI & Build / Python tests (push) Canceled after 47s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / Build & push image (push) Canceled after 0s
CI & Build / integration (push) Canceled after 42s
CI & Build / TypeScript typecheck (push) Canceled after 42s
CI & Build / Python tests (push) Canceled after 47s
A plan written as a milestone with a description and no steps was invisible to the session handshake: it lists the 5 most recently touched milestones (#4045), and touching is a step changing, so a step-less milestone can never qualify. FabledLibrarian's roadmap (nine such milestones) sat unseen while later plans were opened as new milestones beside the ones that already described them. enter_project adds `unplanned_milestones`: active milestones with no steps, in roadmap order, id/title/description, up to 10 with an omitted count, none repeated from the recent list, and absent when there are none. The docstring says what they are for: check them before starting a new milestone, and add steps to a match with create_records(milestone_id=...). Milestone 415 step 1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
This commit is contained in:
@@ -48,6 +48,7 @@ async def list_projects() -> dict:
|
||||
# characters, past what a client accepts as a tool result (#4045).
|
||||
_HANDSHAKE_MILESTONES = 5
|
||||
_HANDSHAKE_OPEN_TASKS = 10
|
||||
_HANDSHAKE_UNPLANNED = 10
|
||||
|
||||
|
||||
async def enter_project(project_id: int) -> dict:
|
||||
@@ -67,7 +68,8 @@ async def enter_project(project_id: int) -> dict:
|
||||
|
||||
Returns a dict with keys: project, milestone_summary, open_tasks, systems,
|
||||
design_system, project_rules, pattern_coverage —
|
||||
plus milestone_summary_omitted, inception and systems_bootstrap, each
|
||||
plus unplanned_milestones, milestone_summary_omitted,
|
||||
unplanned_milestones_omitted, inception and systems_bootstrap, each
|
||||
present only when it applies (see below).
|
||||
|
||||
`project` is id, title, status and the full goal. get_project has the
|
||||
@@ -79,6 +81,15 @@ async def enter_project(project_id: int) -> dict:
|
||||
get_milestone(id) reads a plan and its steps. `milestone_summary_omitted`
|
||||
says how many others exist; list_milestones lists them all.
|
||||
|
||||
`unplanned_milestones` is the active milestones that have NO steps yet,
|
||||
in roadmap order (up to 10; `unplanned_milestones_omitted` counts the
|
||||
rest) — id, title and description. They are open work: a plan somebody
|
||||
wrote down and nobody has broken into steps. A milestone with no steps is
|
||||
never touched, so the recent list above can never show one. Before
|
||||
starting a new milestone for work, check whether one of these already
|
||||
describes it; if so, add steps to it with create_records(milestone_id=…)
|
||||
rather than opening a second plan for the same thing.
|
||||
|
||||
`open_tasks` is the 10 most recently touched todo / in-progress tasks,
|
||||
with or without a milestone. A work-log counts as touching its task. Each
|
||||
names its milestone. list_tasks has the rest.
|
||||
@@ -156,6 +167,11 @@ async def enter_project(project_id: int) -> dict:
|
||||
milestone_rows, limit=_HANDSHAKE_MILESTONES,
|
||||
)
|
||||
milestone_titles = {m["id"]: m.get("title") for m in milestone_rows}
|
||||
unplanned, unplanned_omitted = milestones_svc.unplanned_milestones(
|
||||
milestone_rows,
|
||||
exclude_ids={m["id"] for m in milestone_summary},
|
||||
limit=_HANDSHAKE_UNPLANNED,
|
||||
)
|
||||
open_tasks, _ = await notes_svc.list_notes(
|
||||
uid, is_task=True, project_id=project_id,
|
||||
status=["todo", "in_progress"], sort="touched", limit=_HANDSHAKE_OPEN_TASKS,
|
||||
@@ -256,6 +272,13 @@ async def enter_project(project_id: int) -> dict:
|
||||
f"{omitted} other milestone(s) not listed. "
|
||||
f"list_milestones({project_id}) lists every milestone."
|
||||
)
|
||||
if unplanned:
|
||||
out["unplanned_milestones"] = unplanned
|
||||
if unplanned_omitted:
|
||||
out["unplanned_milestones_omitted"] = (
|
||||
f"{unplanned_omitted} more active milestone(s) with no steps. "
|
||||
f"list_milestones({project_id}) lists every milestone."
|
||||
)
|
||||
if systems_bootstrap:
|
||||
out["systems_bootstrap"] = systems_bootstrap
|
||||
if inception_ask:
|
||||
|
||||
Reference in New Issue
Block a user