A milestone that legitimately changes nothing has no way forward #81

Closed
opened 2026-09-30 13:02:04 +00:00 by dev-agent · 1 comment
Collaborator

Not every milestone produces code: an investigation, a verification or review pass, or a decision recorded outside the repo are legitimate milestones that change nothing. Today they're treated as failures, and there's no proper way to move on.

Today (control.py, publish.py)

  1. The executor finishes. MilestonePublisher.publish finds no changes and returns "no changes to publish".
  2. _publish_milestone moves to AWAIT_INPUT with a milestone_stopped question: "milestone N: no changes to publish. Reply to return to the approved plan."
  3. Any reply returns to AWAIT_MILESTONE_SIGNOFF, with the plan still approved: "say 'start milestone N' to run it again".
  4. From there only start milestone N (re-run the same milestone) and abandon work. next milestone and project complete exist only in PR_OPEN.

So a successful no-change milestone can only be re-run or abandoned. Abandoning closes the plan issue as "abandoned by …", the wrong record for work that succeeded. test_no_stranding didn't catch this: it checks that some exit exists, and abandon always does.

This path is currently mostly hidden. On the second live run, milestone 2 changed nothing but still got a PR (#166), because its branch carried milestone 1's commit (#62) and "no changes" is measured against origin/main. Fixing #62 will expose it.

Proposal

  • A milestone that completes with no changes counts as done, not stopped. The agent reports the executor's finish summary ("milestone 2 done, no code changes: …") and offers next milestone, project complete or abandon. That can be a "milestone done" waiting state, or PR_OPEN with no PR; this also retires the stub-only PR_OPEN-without-PR path.
  • The milestone plan declares whether it's expected to change code (e.g. a field in submit_plan, alongside the milestone declarations proposed in #79).
    • "No changes" on a milestone that should have changed code is still flagged as a problem (today's milestone_stopped).
    • On a review or investigation milestone, it's the normal outcome.
  • Model (specs/orchestrator.qnt): add a property that every exit from a completed milestone leads somewhere meaningful, i.e. a successful milestone never has abandon as its only way forward. "Some exit exists" isn't enough. Add the no-change outcome to the executor/publisher actions.

Tests

  • A review-type milestone with no changes ends in a state where next milestone and project complete work, and the plan issue is later closed as completed.
  • A code-type milestone with no changes is still flagged and can be re-run.

Related: #79 (ending projects, milestone declarations, PR state before moving on), #62 (branch at execution start), #80 (build artifacts).

🤖 Generated with Claude Code

https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE

Not every milestone produces code: an investigation, a verification or review pass, or a decision recorded outside the repo are legitimate milestones that change nothing. Today they're treated as failures, and there's no proper way to move on. **Today** (`control.py`, `publish.py`) 1. The executor finishes. `MilestonePublisher.publish` finds no changes and returns "no changes to publish". 2. `_publish_milestone` moves to `AWAIT_INPUT` with a `milestone_stopped` question: "milestone N: no changes to publish. Reply to return to the approved plan." 3. Any reply returns to `AWAIT_MILESTONE_SIGNOFF`, with the plan still approved: "say 'start milestone N' to run it again". 4. From there only `start milestone N` (re-run the same milestone) and `abandon` work. `next milestone` and `project complete` exist only in `PR_OPEN`. So a successful no-change milestone can only be re-run or abandoned. Abandoning closes the plan issue as "abandoned by …", the wrong record for work that succeeded. `test_no_stranding` didn't catch this: it checks that *some* exit exists, and `abandon` always does. This path is currently mostly hidden. On the second live run, milestone 2 changed nothing but still got a PR (#166), because its branch carried milestone 1's commit (#62) and "no changes" is measured against `origin/main`. Fixing #62 will expose it. **Proposal** - **A milestone that completes with no changes counts as done, not stopped.** The agent reports the executor's `finish` summary ("milestone 2 done, no code changes: …") and offers `next milestone`, `project complete` or `abandon`. That can be a "milestone done" waiting state, or `PR_OPEN` with no PR; this also retires the stub-only `PR_OPEN`-without-PR path. - **The milestone plan declares whether it's expected to change code** (e.g. a field in `submit_plan`, alongside the milestone declarations proposed in #79). - "No changes" on a milestone that should have changed code is still flagged as a problem (today's `milestone_stopped`). - On a review or investigation milestone, it's the normal outcome. - **Model** (`specs/orchestrator.qnt`): add a property that every exit from a completed milestone leads somewhere meaningful, i.e. a successful milestone never has abandon as its only way forward. "Some exit exists" isn't enough. Add the no-change outcome to the executor/publisher actions. **Tests** - A review-type milestone with no changes ends in a state where `next milestone` and `project complete` work, and the plan issue is later closed as completed. - A code-type milestone with no changes is still flagged and can be re-run. **Related:** #79 (ending projects, milestone declarations, PR state before moving on), #62 (branch at execution start), #80 (build artifacts). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
Author
Collaborator

Superseded by the Development Plan (#84, merged): a milestone that wasn't meant to change code is done when it changes nothing (D2, D4). 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): a milestone that wasn't meant to change code is done when it changes nothing (D2, D4). 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.