The planner can't say 'no more milestones', and 'next milestone' forces one #79

Closed
opened 2026-09-30 12:58:06 +00:00 by dev-agent · 2 comments
Collaborator

Found on the second live run.

The architecture plan had one milestone. After its PR, the operator said next milestone, which unconditionally starts a milestone-2 planning turn (_handle_pr_open). The planner correctly asked what milestone 2 should be, and the operator chose "no milestone 2". But the planner's only tools are submit_plan and ask_human, so it had to submit a plan anyway: a "closeout" (it even set type: \"closeout\").

  • That plan went through the milestone-2 gate.
  • The executor then ran a milestone of read-only review checks.
  • The publisher opened PR #166 for it, which is noise at best. Because of the branch it was cut from (#62), it also repeated milestone 1's commit.

Expected

  • The planner can end the project: e.g. a finish_project(summary) tool, or a submit_plan type that means "no further milestones". The orchestrator answers it by proposing project complete to the operator, not by opening a gate.
  • The architecture plan declares its milestones (count and titles), stored in _data. Then next milestone after the last one says so ("the architecture plan had 1 milestone; say 'project complete', or 're-plan' to extend it") instead of planning a milestone nobody defined. This ties in with #61 (single-milestone projects).
  • A milestone that changes nothing isn't published as a PR. With the executor's work already on another branch, "no changes" must mean no PR, which depends on #62 (branch at execution start) and the publisher counting commits relative to the milestone's starting point, not origin/main.

Model: add "a PR is only opened for a milestone that changed the tree" and "planning never happens beyond the declared milestones without an explicit operator decision" to specs/orchestrator.qnt.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE

Found on the second live run. The architecture plan had one milestone. After its PR, the operator said `next milestone`, which unconditionally starts a milestone-2 planning turn (`_handle_pr_open`). The planner correctly asked what milestone 2 should be, and the operator chose "no milestone 2". But the planner's only tools are `submit_plan` and `ask_human`, so it had to submit a plan anyway: a "closeout" (it even set `type: \"closeout\"`). - That plan went through the **milestone-2 gate**. - The executor then ran a **milestone of read-only review checks**. - The publisher opened **PR #166** for it, which is noise at best. Because of the branch it was cut from (#62), it also repeated milestone 1's commit. **Expected** - **The planner can end the project:** e.g. a `finish_project(summary)` tool, or a `submit_plan` type that means "no further milestones". The orchestrator answers it by proposing `project complete` to the operator, not by opening a gate. - **The architecture plan declares its milestones** (count and titles), stored in `_data`. Then `next milestone` after the last one says so ("the architecture plan had 1 milestone; say 'project complete', or 're-plan' to extend it") instead of planning a milestone nobody defined. This ties in with #61 (single-milestone projects). - **A milestone that changes nothing isn't published as a PR.** With the executor's work already on another branch, "no changes" must mean no PR, which depends on #62 (branch at execution start) and the publisher counting commits relative to the milestone's starting point, not `origin/main`. **Model:** add "a PR is only opened for a milestone that changed the tree" and "planning never happens beyond the declared milestones without an explicit operator decision" to `specs/orchestrator.qnt`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
Author
Collaborator

Also: moving on ignores the PR's state. On the same run, PR #166 opened at 12:54:14, and project complete arrived nine seconds later and was accepted. The operator had never reviewed #166, and the task ended with it open and orphaned. Neither project complete nor next milestone checks the PR, although B1 says the flow moves on "on acceptance".

Expected: before leaving PR_OPEN, the orchestrator checks the PR in Gitea (merged / open / closed).

  • Merged: proceed as now.
  • Still open: say so and ask, e.g. "PR #166 is open and unmerged; 'project complete' again to leave it open, or 'abandon' to close it", rather than silently leaving it.

With #65 (the agent merges approved PRs), acceptance becomes something the agent observes, and this check is where that lands. Model property: a task never ends with a PR the operator hasn't decided on.

**Also: moving on ignores the PR's state.** On the same run, PR #166 opened at 12:54:14, and `project complete` arrived nine seconds later and was accepted. The operator had never reviewed #166, and the task ended with it open and orphaned. Neither `project complete` nor `next milestone` checks the PR, although B1 says the flow moves on "on acceptance". **Expected:** before leaving `PR_OPEN`, the orchestrator checks the PR in Gitea (merged / open / closed). - **Merged:** proceed as now. - **Still open:** say so and ask, e.g. "PR #166 is open and unmerged; 'project complete' again to leave it open, or 'abandon' to close it", rather than silently leaving it. With #65 (the agent merges approved PRs), acceptance becomes something the agent observes, and this check is where that lands. **Model property:** a task never ends with a PR the operator hasn't decided on.
Author
Collaborator

Superseded by the Development Plan (#84, merged): build plans declare their milestones, and moving on checks the PR's state (D2, D4, D5). It is covered by M2.5 in docs/Development-Plan.md, which designs the new conversational loop that replaces control.py. Fixing it in control.py would be thrown away when that loop retires.

Superseded by the Development Plan (#84, merged): build plans declare their milestones, and moving on checks the PR's state (D2, D4, D5). It is covered by **M2.5** in `docs/Development-Plan.md`, which designs the new conversational loop that replaces `control.py`. Fixing it in `control.py` would be thrown away when that loop retires.
Sign in to join this conversation.