Skip to content

fix(coding-agent): serialize post-compaction continuation on run settlement - #1844

Closed
snimu wants to merge 3 commits into
mainfrom
snimu/post-compaction-idle
Closed

fix(coding-agent): serialize post-compaction continuation on run settlement#1844
snimu wants to merge 3 commits into
mainfrom
snimu/post-compaction-idle

Conversation

@snimu

@snimu snimu commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

What was wrong

After an automatic compaction, the session decided whether it could continue by re-checking every 100ms on a recursive timer. That timer raced actually-starting runs: it could fire between "check" and "act", collide with a run that had just started, and it woke the process ten times a second during long work for no reason.

The fix

  • The 100ms timer (and its field and cleanup) is deleted.
  • The continuation now waits on the things that actually signal readiness: agent/retry/refine settlement, then the existing session-action commit fence, with a final idle re-check under the fence. Turn dispatch and direct prompts take the same fence, so the check-then-act race is structurally closed.
  • The settlement object doubles as the operation's identity: a cancelled or rescheduled continuation cannot be claimed by a stale runner (every post-await mutation is identity-guarded, including the busy-retry re-arm).
  • The busy fallback survives (one lower-layer retry path starts runs without the fence) but now waits for the active run's idle settlement instead of polling.
  • Timer-advancement tests replaced with settlement-driven ones.

How it's verified

Focused queue/compaction suites green; the goal-continuation-quiescence suite (PR #1610) passes 7/7 untouched — same semantics. Full CI-style suite compared against the stack base: identical failure names (pre-existing environment noise only). Reviewer independently traced the fence/settlement flow, verified run-ownership closure, and re-ran the noisy suite at base in a throwaway worktree to confirm zero delta. Two-model implement/review loop.

Stacked on #1708 (test the whole stack at the leaf; merge base-first).

Note: intentionally no Linear ticket for this cleanup stack, so that check stays red.


Note

Medium Risk
Changes session loop timing and concurrency around compaction and agent.continue(), which can affect autonomous runs and headless idle semantics; behavior is heavily tested but race surface is non-trivial.

Overview
Removes the 100ms timer that used to defer resuming the agent after compaction, so continuation now runs as soon as readiness checks pass instead of polling ten times per second during long work.

Post-compaction continuation is reworked around a settlement token (continueAfterSessionInput plus identity checks) so cancelled or rescheduled continuations cannot be driven by a stale runner. The runner loops until it can safely call agent.continue(), waiting on agent idle, retries, refine, queued-work pauses, and an active _compactionOperation (now tracked for auto-compaction as well as manual compact()). Session-input pump work can still run first; an empty resume can still continue when continueAfterSessionInput is set.

Tests drop fake-timer advancement and add settlement-driven cases (compaction in flight, refine apply, busy retry, cancellation/replacement).

Reviewed by Cursor Bugbot for commit 874a84e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Serialize post-compaction continuation on run settlement in AgentSession

  • Removes the 100ms setTimeout delay for post-compaction continuation; the runner now executes immediately once all real idle conditions are met (agent idle, retry completion, refine idle, queued-work pause release, and active compaction operation completion).
  • Adds continueAfterSessionInput to PostCompactionContinuationSettlement so the runner can decide whether to proceed after session input is processed or defer to the session input pump.
  • Introduces _compactionOperation promise fencing in both compact and _runAutoCompaction to hold off continuation until compaction fully completes, then schedules the session input pump afterward.
  • Rewrites _runScheduledPostCompactionContinue to loop on settlement identity, correctly retry after busy errors, and isolate cancellations so late completions from cancelled runs cannot forget replacement messages or reject newer waiters.
  • Risk: removing timer-based scheduling changes the timing of agent.continue() calls; tests in agent-session-compaction.test.ts and agent-session-queue.test.ts were rewritten to use deferred-based sequencing instead of timers.

Macroscope summarized 874a84e.

Linear: ENG-5651

Note: trivial textual overlap with #1859 (both add cleanup lines to the same finally block in _runAutoCompaction; both lines should survive). Whichever PR merges second will be refreshed against main.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7ec9bfa. Configure here.

Comment thread packages/coding-agent/src/core/agent-session.ts
Comment thread packages/coding-agent/src/core/agent-session.ts
…tion and stale runners

- Publish _compactionOperation from _runAutoCompaction so the
  post-compaction continuation runner (and other waiters) serialize
  against auto/threshold/overflow/requested compaction, not just
  manual compact().
- Identity-guard the success path of the continuation runner so a
  cancelled runner cannot forget continuation messages that a
  replacement settlement still owns (busy/error paths already did).
Comment thread packages/coding-agent/src/core/agent-session.ts
…ction continue

A refine() call could enter its apply phase (which disconnects the agent
event listener) between the runner's top-of-loop _waitForRefineIdle() and
agent.continue(), losing the continuation's messages. Recheck
_refineInFlight under the commit fence like pauses/_compactionOperation;
the path from that recheck to agent.continue() is synchronous, mirroring
the final refine barrier used by normal turn dispatch.
@snimu

snimu commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

Folded into #1859: that branch already contains this PR's commits (the two changes share code and were sequenced as a merge-after chain). Merging #1859 lands both; per snimu's preference the chain is collapsed into one merge unit. The review history here remains valid for the contained commits.

@snimu snimu closed this Aug 28, 2026
@kevinjosethomas
kevinjosethomas deleted the snimu/post-compaction-idle branch September 8, 2026 20:41
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