Skip to content

fix(compression): stop an aborted rotation from growing the parent it could not publish - #88411

Merged
kshitijk4poor merged 1 commit into
NousResearch:mainfrom
kshitijk4poor:salvage/88227-compression-preflight-ended-check
Aug 17, 2026
Merged

fix(compression): stop an aborted rotation from growing the parent it could not publish#88411
kshitijk4poor merged 1 commit into
NousResearch:mainfrom
kshitijk4poor:salvage/88227-compression-preflight-ended-check

Conversation

@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Summary

Stops an aborted compression rotation from growing the parent transcript it could not publish — when the parent session already carries ended_at, the pre-flush guard raises before the durable #47202 flush writes rows that survive the abort.

Root cause: the rotation path flushes un-persisted current-turn messages to the parent (#47202) and then calls publish_compression_child. The abort handler rolls back in-memory state but not the durable rows. On a sticky failure (parent already ended, e.g. by stale tui_shutdown), each auto-compaction appends another copy of the current turn — the session grows instead of shrinking until the provider rejects the request (#88197: 303 messages became 2,611 rows, HTTP 400).

Changes

  • agent/conversation_compression.py: hoist old_session_id above the flush (so the except handler rolls back correctly), add a pre-flush guard that reads the parent row and raises the publish's own RuntimeError if ended_at is set. Fails open on an unreadable row. Deliberately not extended to the compression lease (re-acquirable, transient).
  • tests/agent/test_compression_rotation_state.py: TestAbortedRotationDoesNotGrowParent — 3 tests covering ended-parent (the bug), live-parent (no regression), fail-open (unreadable row).

Validation

Before After
Aborted rotation on ended parent Appends duplicate rows to parent Raises before flush, parent unchanged
Aborted rotation on live parent Flushes + aborts Flushes + aborts (unchanged)
Unreadable parent row N/A Fails open, rotation proceeds
  • 3/3 new tests pass, 20/20 in rotation state file pass, 53/53 neighboring compression suites pass
  • Ruff clean
  • Mutation table from original PR verified: each mutation kills exactly the right test

Salvage of #88227 by @jackulau — cherry-picked with authorship preserved. Closes #88227.

… could not publish

The rotation path flushes its un-persisted transcript to the parent (NousResearch#47202)
and only then calls publish_compression_child. The abort handler rolls back
the in-memory transcript and keeps agent.session_id on the parent - its own
comment says "keep the parent live and discard the stale compacted snapshot" -
but the rows the flush just wrote are not part of what it discards. Every
failed rotation therefore leaves the parent transcript longer than it found
it, whatever the failure was.

That is survivable for a one-off failure and pathological for a sticky one.
A parent row carrying ended_at fails the publish on every attempt and nothing
in this path clears it, so each auto-compaction appends another copy of the
current turn to the transcript it was supposed to shrink. Worse, the growth
then satisfies conversation_compression's own len(durable_parent) >
len(messages) check, so the next attempt adopts the inflated snapshot as if it
were genuine concurrent activity and the in-memory transcript doubles too.

Check that one precondition before writing. It is a plain read of the row the
publish is about to read anyway, and it raises the publish's own message, so
split_status=aborted, failure_class=session_split_failed and the rollback path
are all unchanged; a live parent reaches the flush exactly as before.
Deliberately not extended to the compression lease, which is re-acquirable - a
transient miss there would abort a rotation that would otherwise have
committed. old_session_id moves above the flush so a failure raised from here
takes the same in-memory rollback as any other pre-publish failure.

Scope: this fixes the amplification for every abort cause. It does not fix
what marks a live session as ended in the first place (NousResearch#88197 Bug 1), which
needs a maintainer decision on end-reason taxonomy and is tracked on the
issue; an affected session still aborts every attempt, it just stops making
itself larger while it does.

Refs NousResearch#88197
@kshitijk4poor
kshitijk4poor enabled auto-merge (rebase) August 17, 2026 12:41
@kshitijk4poor
kshitijk4poor merged commit cf64ca2 into NousResearch:main Aug 17, 2026
43 checks passed
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint area/compression Context compression and continuation sessions sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/compression Context compression and continuation sessions comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P1 High — major feature broken, no workaround sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants