Skip to content

feat: a turn-scoped note stops Claude repeating a long reply after a housekeeping call - #418

Merged
ualtinok merged 3 commits into
mainfrom
fix/turn-end-note
Oct 21, 2026
Merged

ualtinok merged 3 commits into
mainfrom
fix/turn-end-note

Conversation

@antauth-alfonso

@antauth-alfonso antauth-alfonso Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

When Claude writes a long final reply and then calls a housekeeping tool in the same response, the response ends tool_use, the host sends the small result back, and the model often writes the reply again. Replays of a real OpenCode request: unchanged, the model rewrote its reply 3 of 3 times; the same "don't repeat" wording inside the tool result stopped it 1 of 3; a turn-scoped system message after the result stopped it 3 of 3. On a mid-work step (short narration, then the tool, then real work) the model kept working 3 of 3 with the note.

  • After each assistant message with at least 350 characters of visible text whose tool calls are all in turnEndNote.tools (default board, ctx_reduce; [] turns it off), the request carries {"role":"system","clear_at":"next_user_message","content":"Your reply above has already reached the user. …"} after that message's tool results.
  • It is derived only from that message and its results, so every request re-sends earlier notes byte for byte and the prompt cache holds. The one exception: a note that would sit directly before another user message (Anthropic rejects that) is left out at that position only.
  • OAuth routes only, on Fable 5.1, Opus 5.5, Sonnet 5.5 and Haiku 5.5; no beta header changes. OpenCode 1, OpenCode 2 and Pi through one Core function, after effort-marker handling and before signing. Settings field only, no menu change.

Tests: per-host sequence tests (requests N, N+1, N+2 share an identical prefix through every earlier note), the 349/350 boundary, mixed tools, API-key routes and unsupported models unchanged, no cache_control on the note. Mutation rows: inject only at the latest point, drop the all-tools check, drop the length minimum; all CAUGHT.


Summary by cubic

Prevents Claude from repeating a long final reply after a housekeeping tool call in the same response; the model rewrote its reply 3 of 3 times on replayed OpenCode requests without the note.

  • Qualifying turns — at least 350 visible characters of reply text, and every tool call in turnEndNote.tools — get a turn-scoped system note (clear_at: "next_user_message") after the tool results, on OAuth routes for Fable 5.1, Opus 5.5, Sonnet 5.5, and Haiku 5.5.
  • Earlier notes are replayed byte-for-byte on every request so the prompt cache holds; the only exception omits a note that would sit directly before another user message, which Anthropic rejects.
  • API-key routes, unsupported models, and the beta/header shape are unchanged; the note carries no cache or effort fields.

Configuration

  • New native turnEndNote.tools setting, default ["board", "ctx_reduce"]; an empty array disables the note, a custom list uses the host's tool spellings.
  • The 350-character minimum is a safety margin for short mid-work steps; thinking content does not count as visible text.

Written for commit 026fe85. Summary will update on new commits.

View guided diff Turn on auto-fix

Add native turnEndNote.tools policy and a shared deterministic history transform for OAuth OpenCode 1, OpenCode 2 and Pi. Require at least 350 outgoing visible text characters and all-housekeeping tool calls. Replay every earlier qualifying note; omit only a note directly before another user message to avoid Anthropic's invalid placement.

Cover per-host N/N+1/N+2 serialized history prefixes, settings projection, OAuth/API exclusions, tool-name mapping, length boundaries, cache lookback, effort controls and signing. Add three mutation controls: exact dispatch catches all three; direct marked mutations each fail only their named test (nine companions green). Broad replay found unrelated custody flakes on the latest-point control; all-tools and length controls had no collateral.

Verified pinned Bun 1.3.14 remote typecheck, build, focused Core/OpenCode/Pi tests, format/lint and Pi host mapping. Full unit gates encountered the offline packed OpenCode registry refusal and unrelated Pi native-relay timeouts; no unrelated tests or deadlines were changed.
packages/opencode/CHANGELOG.md holds inherited upstream history; release notes come from the root CHANGELOG.md.
@ualtinok
ualtinok merged commit 026fe85 into main Oct 21, 2026
11 checks passed
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.

1 participant