OpenCode V2 plugin: bootstrap is injected on the 'context' hook only, so every compaction request misses the prompt cache

Author: tpischkeCreated Sep 26, 2026Updated Oct 1, 2026
  • I searched existing issues and this is not a duplicate (closest: #2306, the V2 port that added the context hook)

Environment (required)

Field Value
Superpowers version 6.4.2 (github:obra/superpowers, .opencode/plugins/superpowers.js)
Harness (Claude Code, Cursor, etc.) OpenCode 2 (V2 plugin API)
Harness version 2.0.18
Your model + version Qwen3.8-Flash-Next (local, mlx-serve 26.9.6, OpenAI-compatible provider). Report written by Claude Opus 5.5 in Claude Code from the server log and the OpenCode session database.
All plugins installed superpowers; one local plugin that only sets sampling / reasoning-effort fields on the request body
OS + shell macOS (host), OpenCode in a Debian container, sh

Is this a Superpowers issue or a platform issue?

  • I confirmed this issue does not occur without Superpowers installed

Not tested by uninstalling. Superpowers is what makes the two requests differ: the prompts match up to the end of system + tools, which is where the bootstrap goes, and registering the same handler on compaction makes them match up to the tail (numbers below).

What happened?

The OpenCode V2 plugin registers its bootstrap injection only on ctx.session.hook('context', ...). OpenCode 2 builds the compaction (summary) request through a separate hook, compaction. OpenCode's own built-in plugins register the same handler on context, compaction and generate.

So the compaction request's first user message lacks the bootstrap that every normal turn had. The prompt diverges right after system + tools, and on any backend with prefix caching the whole session is re-prefilled cold at every compaction. That usually means a 150-200k-token prompt, which is exactly when a cold prefill is most expensive. For hosted models this is a cache miss on the largest request of the session; for local models it is minutes.

Steps to reproduce

  1. OpenCode 2.0.18 + superpowers 6.4.2, any provider whose server reports prefix-cache hits (e.g. llama.cpp / mlx-serve logs).
  2. Work a session until auto-compaction fires.
  3. Compare the compaction request's cached-token count with the previous normal turn's.

Expected behavior

The compaction request reuses the cached prefix up to where it diverges near the tail.

Actual behavior

Same session, same server, 2026-09-26:

user-text bytes cached / prompt tokens prefill
last normal turn 64,784 B (warm)
compaction request, unpatched 63,314 B (~1.5 KB less: the bootstrap) 8,192 / 199,112 (= system + tools only) 510 s
compaction request, patched as below 176,687 / 177,402 13.7 s (SSD restore)

An earlier compaction the same day showed the same thing: 8,909 / 201,332 matched, 536 s.

Fix

Register the existing handler on both hooks, as OpenCode's built-ins do:

javascript
const bootstrapHook = async (event) => { /* existing body, unchanged */ };
await ctx.session.hook('context', bootstrapHook);
await ctx.session.hook('compaction', bootstrapHook);

The existing body already copes with a compaction-shaped event (it skips when the bootstrap is present and appends a user message when there is none). We have run this patch locally since 2026-09-26; the measurement above is with it.

Debug log or conversation transcript

Server log excerpt (mlx-serve), unpatched compaction:

POST /v1/chat/completions (247 msgs, ..., sys=22099b, user=63314b, tools=13164b, tool_msgs=115)
  > "You MUST summarize the conversation above into a structured summary that will be..."
  prompt=199112 tokens
  [hot-cache] reused 8192/199112 tokens (matched 8999; entry 4/4)
  <- 199112+5310 tokens streamed [prefill: 374.6 tok/s (8192 cached / 199112 total), decode: 66.1 tok/s] [stop]

Patched:

  > "Update the existing checkpoint in the conversation above into one consolidate..."
  prompt=177402 tokens
  [disk-cache] restored 176687/177402 tokens from SSD in 13709ms (ssm@176687)
  <- 177402+6901 tokens streamed [prefill: 38.3 tok/s (176687 cached / 177402 total), decode: 49.1 tok/s] [stop]