Skip to content

Compaction summarizes the current turn and then appends the same turn again #810

Description

@leseb

Clawpatch finding

  • Severity: medium
  • Category: bug
  • Confidence: high
  • Triage: confirmed-bug
  • Feature: feat_custom_apis_openai_responses_retrieval_proxy
  • Finding ID: fnd_sig-feat-custom-apis-openai-resp_b177a277d5
  • Run ID: 20260824T070943-dd5f81
  • Run report: .clawpatch/reports/20260824T070943-dd5f81.md
  • Prior-run overlap: No exact prior-run overlap found; related issue Compaction restores stale file references after resolution, bypassing the protected rewrite #661 covers a different compaction state-rewrite hazard.

Evidence

  • apis/src/openai/responses/compact/mod.rs:291-322 (should_compact) — let conversation_text = build_conversation_text(&state.messages);
  • apis/src/openai/responses/compact/mod.rs:505-515 (replace_messages) — new_messages.extend(state.input.iter().cloned());
  • apis/src/openai/responses/compact/tests.rs:239-249 (conversation_text_array_content) — let text = build_conversation_text(&messages);

Description

After rehydration, state.messages contains prior history followed by the current request's normalized state.input. should_compact sends all of state.messages to the summarizer, so the generated summary already includes the current user turn. replace_messages then constructs the outbound context as the summary followed by state.input, duplicating the current turn semantically. This can cause the model to answer or weigh the latest request twice and wastes the context compaction was intended to reclaim.

Reproduction

Construct state with one historical exchange and current input "CURRENT UNIQUE TURN", set history_rehydrated and a zero compaction threshold, and capture the summarization request. Its user message contains CURRENT UNIQUE TURN. After applying the returned summary, inspect state.messages: the summary represents that turn and the original current-input item is also present.

Recommendation

Use the full context for threshold accounting if desired, but build the summarization text only from the history prefix state.messages[..state.messages.len() - state.input.len()]. Preserve state.input exactly once after the compaction item.

Minimum fix scope

Split history from the current-input tail when constructing conversation_text, retaining existing full-context token accounting where appropriate, and add one focused compaction lifecycle test.

Human review gate

This issue requires human review before any work can begin. Do not self-assign and Do not submit a PR before the issue has been validated.

Activity

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions