Skip to content

fix(core): keep plan mode reminders out of the final user turn - #57065

Open
Veld101 wants to merge 1 commit into
anomalyco:devfrom
Veld101:plan-reminder-tail
Open

Veld101 wants to merge 1 commit into
anomalyco:devfrom
Veld101:plan-reminder-tail

Conversation

@Veld101

@Veld101 Veld101 commented Oct 19, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #57013

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

When an agent switches away from plan partway through a turn, the transcript ends on a tool result rather than a prompt. The context hook appended its NO LONGER in Plan mode reminder after it, so the request ended on a user turn holding only a <system-reminder> and no instruction. Models such as gpt-5.3-codex refused every request with that tail, and the reminder is also persisted as a synthetic message, so retries rebuilt the same tail and kept refusing until the user typed something.

Reminders now go ahead of the trailing assistant/tool run, so the request keeps ending on the turn the model continues from:

before: … assistant(tool_call) > tool(result) > user(<leave reminder>)
after:  … user(<leave reminder>) > assistant(tool_call) > tool(result)

Two further cases are the same invariant:

  • A reminder an earlier request already left as the final turn is re-placed the same way. That is what a retry rebuilds from, so fixing only the insert path leaves retries broken.
  • A reminder left directly after the user's prompt moves ahead of it, so the prompt ends the request.

Context hook reminders still land before the user's prompt when a prompt ends the request, unchanged from before.

The placement rule lives in reminderPoint, and reminderText is factored out of lastReminder so both the scan and the tail check agree on what counts as a reminder-only turn.

How did you verify your code works?

Three new cases in packages/core/test/plugin/plan.test.ts, each confirmed to fail without the change (reverting only plan.ts turns all three red):

  • keeps a switch reminder ahead of a trailing tool result — the mid-turn switch from the report; the request ends on the tool result instead of the reminder.
  • moves a reminder already persisted at the tail ahead of the trailing run — the retry path; nothing new is persisted.
  • moves a reminder persisted after the user's prompt before it — the prompt ends the request.
bun test test/plugin/plan.test.ts    # 15 pass, 0 fail
bun typecheck                        # packages/core, clean
bun run lint                         # exit 0, no new findings
bunx prettier --check                # clean

bun test test/plugin/ is 389 pass / 1 fail: applies organization integration changes to running external plugins fails with pending on an unmodified tree too (verified with the change stashed), so it is unrelated.

Note

The reporter mentioned having a patch ready and waiting on a maintainer. I did not see a PR on the issue, so this is offered as a starting point; happy to hand it over or fold in their approach if they prefer it.

Screenshots / recordings

Not applicable; no UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

When an agent switches away from `plan` partway through a turn, the transcript
ends on a tool result rather than a prompt, and the `context` hook appended its
`NO LONGER in Plan mode` reminder after it. The request then ended on a user
turn holding only a `<system-reminder>` with no instruction, and models such as
`gpt-5.3-codex` refused every request that had that tail.

Reminders now go ahead of the trailing assistant/tool run, so the request keeps
ending on the turn the model continues from. A reminder an earlier request left
as the final turn is re-placed the same way, which is what a retry rebuilds
from, and one left directly after the user's prompt moves ahead of it.

Adds three cases covering the mid-turn switch, a reminder already persisted at
the tail, and one persisted after the prompt. All three fail without the change.
@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix works and looks sound. I ran the #57013 reproduction (switch from plan to build mid-turn through the server API, simulated model makes a tool call) against both the base and this PR. On the base, the next request ends assistant > tool > user[leave reminder]. With this PR it ends user[leave reminder] > assistant > tool, so it finishes on the tool result. That run covers the case where the reminder was already saved at the end of the session, which is the one the issue needs. The three new tests fail on the base and pass here. plan.test.ts passes (15/15) and packages/core type-checks clean.

I also checked the edge cases. Tool results have their own tool role, so the insertion point really is just before the trailing assistant/tool messages. When a reminder is moved and a new one is added in the same request, the enter/leave order stays right. The saved reminder stays at the end of the stored session, but it is moved again on every request, so retries are covered.

Public API: unchanged (internal to the plan plugin).

No blocking concerns.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plan mode leave reminder becomes the final user turn after a tool-driven agent switch, models refuse

1 participant