Skip to content

fix: honest session_state source label — "compaction" only after real compaction - #996

Open
donwellsav wants to merge 1 commit into
mksglu:mainfrom
donwellsav:fix/false-compaction-label
Open

fix: honest session_state source label — "compaction" only after real compaction#996
donwellsav wants to merge 1 commit into
mksglu:mainfrom
donwellsav:fix/false-compaction-label

Conversation

@donwellsav

Copy link
Copy Markdown

Problem

hooks/auto-injection.mjs hardcodes the wrapper label:

return `<session_state source="compaction">\n\n${parts.join("\n\n")}\n\n</session_state>`;

but the pi adapter injects that block on every before_agent_start — not
just after a real compaction (the code comment says so itself: "Pi-3 + Pi-4:
Always build active_memory (not just post-compact)"
). The file header of
auto-injection.mjs still documents the original invariant ("Only fires on
source === 'compact'"
), which the pi adapter silently broke.

Observed impact (2026-07-24): a pi session with context-mode active had
zero compaction events in its transcript, yet the standing
source="compaction" marker led two different models to declare their
context destroyed and ask the user to re-provide decisions that were fully
visible in context — a false "context loss" cascade.

Fix

The label now tells the truth, on every path:

  • buildAutoInjection(events, source)source is a required
    parameter (fail-safe: no default, so no present or future caller can
    silently emit the wrong label).
  • Fidelity line only on genuine compaction"Context was compacted; full history persists in the session transcript." tells the agent what
    happened and where its history lives (the two facts missing in the
    incident). Routine per-turn injection carries no extra line, so the
    ~500-token content budget and per-turn token cost are unaffected.
  • pi adapter tracks session_compact via _pendingCompactLabel: the
    first before_agent_start after a real compaction is labeled
    "compaction"; all routine turns are labeled "active_memory". The flag
    is read-and-cleared at the top of the handler (can never strand true and
    mislabel a later unrelated turn) and reset in session_shutdown (can never
    leak across sessions in the same process).
  • Genuine compaction paths pass "compaction" explicitly:
    hooks/sessionstart.mjs (source === "compact") and the opencode plugin
    (experimental.session.compacting) — the latter would otherwise have been
    relabeled active_memory, inverting the bug on that platform.

Tests

New tests/auto-injection-label.test.ts (5 cases): both labels, fidelity
line presence/absence, empty-events path, required-parameter loudness, and an
honest budget assertion (content ≤ 500 tokens + unbudgeted wrapper/fidelity
overhead; P2-overflow fixtures excluded by design).

  • New tests: 5/5 pass
  • tests/pi-extension.test.ts + tests/opencode-plugin.test.ts: 125/125 pass
  • tsc --noEmit: clean
  • Full suite: 4721 passed / 1 failed — the failure
    (tests/core/server.test.tsCONTEXT_MODE_PROJECT_DIR env cascade) is
    pre-existing and environment-related: it fails identically on a clean
    checkout of main in the same environment and does not touch this code
    path.

Notes for review

  • build/pi-extension.js in the published tarball contains a stale duplicate
    of the per-turn injection call site. Nothing in the package references it
    (package.jsonpi.extensions points at build/adapters/pi/extension.js);
    it may be worth deleting in a separate cleanup.
  • Behavior change is limited to label text + one added sentence after real
    compactions; no event-selection, priority, or budget logic was modified.

buildAutoInjection hardcoded source="compaction" while the pi adapter
injects the block on every agent start, causing models to falsely believe
their context was destroyed (false-loss cascade).

- source is now a REQUIRED parameter (fail-safe, no silent default)
- fidelity line ("full history persists in the session transcript") only
  on genuine compaction; routine injection carries no extra line
- pi adapter tracks session_compact via _pendingCompactLabel: the first
  post-compact turn is labeled "compaction", routine turns "active_memory";
  flag read-and-cleared at handler top (never strands) and reset on
  session_shutdown (never leaks across sessions)
- genuine compact paths pass "compaction" explicitly:
  hooks/sessionstart.mjs, opencode plugin (experimental.session.compacting)
- tests: tests/auto-injection-label.test.ts (5 cases)

Full suite: 4721 passed, 1 pre-existing env failure (tests/core/server.test.ts
CONTEXT_MODE_PROJECT_DIR cascade — fails on clean base too).
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