Skip to content

agent: empty <conversation-checkpoint> replies after compaction/revert are marked succeeded (no tools, no output) #57240

Open

Description

@GZZXWZA

Summary

After a compaction (and again after a revert to just-after that compaction), the model role-flips into OpenCode's compaction summarizer: it first emits the <conversation-checkpoint>…</checkpoint> serialization itself as a user-visible answer, then degenerates to answering every following turn with an empty <conversation-checkpoint><summary>(no content)</summary></checkpoint>. No tool parts, no substantive text — yet every step is stored finish=stop and closes idle outcome=succeeded, so nothing looks broken from the outside. Related: #50814 (flattened [Assistant tool call] transcripts after compaction). This is a distinct degenerate loop observed in the same session.

Environment

  • opencode version: 2.0.31 (install/channel: latest)
  • OS: Windows 11 Home 10.0.26200 (win32 x64)
  • Terminal: TERM=xterm-256color; desktop client (TERM_PROGRAM not set)
  • Shell: C:\Windows\system32\cmd.exe
  • Active plugins: none (no plugin key in ~/.config/opencode/opencode.json, no local plugin dirs)
  • Models: stepfun-ai/step-5-preview via a custom @ai-sdk/openai-compatible provider (NVIDIA NIM endpoint); mimo/mimo-v2.6-flash also affected earlier in the same session

Reproduction

  1. Run a long build session until auto-compaction fires (session ses_eca4fc28dffdfCI4tEPJ6I7UhI; compaction record at seq 37478, 2026-10-19 15:47 local, 148 KB).
  2. Revert the session to just after that compaction (the revert removed seq 37485–37608, i.e. the polluted turns — verified by diffing session rows before/after).
  3. Send a message that touches the compaction summary (e.g. asking how summary formatting is produced).
  4. Keep sending normal follow-up turns.

Observed (all verified against the session SQLite DB, not the TUI rendering):

seq time (local) model output stored result
37617 16:33:06 reasoning only finish=error, turn interrupted
37655 16:36:36 full <conversation-checkpoint><summary>…</summary><recent-context>…</recent-context></checkpoint> block (8,694 output tokens) finish=stop, 0 tool parts
37677 16:51:51 <conversation-checkpoint><summary>(no content)</summary></checkpoint> finish=stop, idle succeeded
37689 16:53:42 same, empty finish=stop, idle succeeded
37701 16:54:45 same, empty finish=stop, idle succeeded
37713 17:00:05 same, empty finish=stop, idle succeeded

After the revert the session produced 0 real tool calls. It was effectively dead while every turn reported success.

Expected Behavior

  • The assistant answers user turns normally and emits structured tool parts; it must not reproduce compaction serialization as chat output.
  • A turn with zero tool parts and no substantive text must not be recorded as a successful turn.

Actual Behavior

  • The model emits the compaction scaffold (<conversation-checkpoint> / <summary> / <recent-context> / </checkpoint>) as its answer, then collapses to empty checkpoint blocks for every subsequent turn.
  • <conversation-checkpoint> appears in none of the 80 stored compaction records, nor in instruction_blob, kv, or any config file on this machine (verified by search). The model reproduces the server-side serialization from context/memory, then imitates its own prior degenerate output.
  • The single full checkpoint it emitted (seq 37655) also re-asserted fabricated statistics from the compaction: model emits tool calls as literal text after compaction — tools never run, session returns no output #50814 failure mode ("23 files / 41,857 chars → 10–14K tokens per turn"; the real measurement is 9 files / 210,189 chars), promoting a hallucinated number into a checkpoint-shaped "authoritative" summary that later turns then inherit.

Additional Context

  • Evidence collected from a read-only copy of the session DB: session_message rows (type, content part types, finish, tokens, snapshot), type='compaction' rows, instruction_blob, kv.
  • Recovery dead-ends observed:
    • Reverting to just-after the compaction keeps the polluted compaction record as the context base; the model relapses.
    • /compact is not a user message, so revert/undo cannot target it.
    • POST /api/session/{id}/fork copies projected history before a message, so any fork point after the first polluted compaction carries the pollution into the child session — forking cannot produce a clean continuation of this session at all.
  • Suggested mitigations:
    1. Integrity check: an assistant message whose text/reasoning contains [Assistant tool call]:, [Tool result]:, or <conversation-checkpoint> while having zero tool parts should fail the step (or at least warn in the TUI + log), not close idle/succeeded.
    2. Make the compaction scaffold visibly non-imitable / out-of-band, since the model demonstrably role-flips into "summarizer" once the conversation touches compaction.
    3. Structural representation of prior tool calls in post-compaction prompts (tracked in compaction: model emits tool calls as literal text after compaction — tools never run, session returns no output #50814).

Happy to provide the full DB rows (seq 37655, 37677–37713) if useful.

Activity

  1. opencode-agent commented on Oct 20, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed write-up and the DB-level evidence.

    The <conversation-checkpoint> wrapper isn't stored anywhere. OpenCode builds it from the compaction record each time it assembles a prompt (packages/core/src/session/runner/to-llm-message.ts) and sends it as a user message labelled as historical context. That's why your searches of the compaction records, instruction_blob and kv didn't find it. What you saw is the model (step-5-preview / mimo) copying that injected block and then repeating its own empty output. Those turns contain text and finish with stop, so marking them as succeeded is how step completion currently works.

    I've filed this as a feature request for the safeguards you suggest: flagging or failing assistant turns whose text looks like compaction or transcript scaffolding but which have no tool parts, and making the checkpoint scaffold harder for models to imitate. Maintainers will consider it; I can't promise anything.

    Related issues:

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions