Skip to content

compaction: buffer of 180,904 reduces 200k window ceiling to just 15k tokens #58774

Open

Description

@thtesche

Summary

Automatic context compression triggers far too early (after ~15k tokens) despite a 200k token context window, because the configured compaction.buffer is subtracted directly from the model's input limit, leaving only 195,904 − 180,904 = 15,000 tokens of headroom before compaction fires.

Environment

  • opencode version: 2.1.3
  • OS: Darwin 27.0.0 (darwin arm64)
  • Terminal: Unavailable (terminal environment variables not set)
  • Shell: /bin/zsh
  • Install/channel: latest
  • Active plugins: -opencode.browser, /Users/thtesche/.config/openchamber/run/instances/52345/agent-tool/openchamber-agent-tool, @openchamber/opencode-claude@^1
  • Active model: strata/qwen3.8-flash-next-q2_0 (context: 200,000 / input: 195,904 / output: 4,096)
  • User config: compaction: { auto: true, buffer: 180904 }

Reproduction

  1. Set compaction.buffer to a large value close to the model's input limit (e.g. 180904 of 195904).
  2. Start a session and exchange enough messages that the measured context passes ~15k tokens.
  3. Observe automatic compaction firing on nearly every turn, well before the 200k window is approached.

Expected Behavior

With a 200k context window and buffer unset, automatic compaction should not fire until roughly 85% of the window is used (~166k tokens). A large buffer should not collapse the ceiling to ~15k tokens.

Actual Behavior

The ceiling is calculated as window - buffer = 15,000 tokens, so due() returns true as soon as estimateContext(context) >= 15,000. Compaction fires after only a few exchanges, repeatedly rewriting history and discarding prompt-cache benefits.

Root Cause

calculateCeiling in packages/core/src/session/compaction.ts (introduced 2026-10-23 in commit 33726e8, PR #58313):

const calculateCeiling = (limit, buffer) => {
  const window = limit.input || limit.context   // 195,904
  if (buffer !== undefined && buffer < window)  // 180,904 < 195,904 → true
    return window - buffer                       // 195,904 − 180,904 = 15,000
  return window - Math.max(Math.floor(window * RESERVE_SHARE), RESERVE_MIN)
}

When buffer is set but still smaller than window, it is subtracted in full. The fallback to RESERVE_SHARE (15%) only applies when buffer >= window or buffer is unset.

The same day's commit added a guard ("ignore a compaction buffer that leaves the model no room"), but it only ignores buffers >= window. A buffer of 180,904 against an input limit of 195,904 still passes the buffer < window check, so the 15k ceiling stands.

Additional Context

  • Evidence from opencode.db: two local sessions exist, both measured well under the current 15k ceiling; the active strata/qwen3.8 session shows input 707 / output 8847 / reasoning 3165 / cache read 245141 / cache write 127909 — cache-heavy, so estimateContext climbs quickly.
  • The event table in opencode.db is empty; compaction events are not persisted there, so the DB alone cannot confirm trigger counts. The code path is sufficient evidence.
  • This is not issue compaction: auto-compaction loops when provider reports input tokens above the model's context limit #50474 (auto-compaction loop on provider-reported overflow). This is a config/ceiling interaction: a user-set buffer collapses the trigger threshold.
  • Suggested fix: validate compaction.buffer so it cannot reduce the ceiling below a sane floor (e.g. RESERVE_SHARE of the window), or document that buffer values close to input will cause near-immediate compaction.

Activity

  1. opencode-agent commented on Oct 24, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed write-up, @thtesche!

    What you're seeing is how compaction.buffer is meant to work. Per the compaction docs, buffer is the number of tokens to keep free below the model's limit, and larger values start automatic compaction earlier. So buffer: 180904 on a model with a 195,904-token input limit asks OpenCode to keep 180,904 tokens free. That leaves 15,000 tokens before compaction, which is what you're getting. Only a buffer at least as large as the whole window is ignored (#58313).

    To get the behaviour you expected (compaction at about 85% of the window), remove buffer from your config so the default reserve applies (15% of the limit, at least 16k). Or set it to a small value like 30000:

    {
      "compaction": { "auto": true }
    }

    I've filed this as a feature request: OpenCode could validate or cap compaction.buffer so that a value close to the window can't push the threshold down to a few thousand tokens, or at least warn about it. Maintainers will consider it.

    Related:

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