fix(orchestrator): serialize auto-compact self-dispatch via GroupQueue.enqueueTask (LIA-367)#985
Merged
Merged
Conversation
…e.enqueueTask (LIA-367) The /compact self-dispatch fired un-awaited mid-callback, bypassing GroupQueue's one-container-per-group serialization and racing the primary turn's container on the same unscoped IPC directory. Defer the dispatch until the primary turn returns and route it through the existing enqueueTask primitive (already used identically in odysseus-server.ts and task-scheduler.ts), which always queues while the group is still active. Note: contextStats/compactionEvent never reach this callback today (the RuntimeEvent/eventSink plumbing doesn't forward them, see config.ts:184-191), so this is preventive hardening for a currently-dead code path, not a fix for an active race — a follow-up ticket will track closing that forwarding gap so DEUS's own auto-compact trigger can actually fire. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/compactself-dispatch inmessage-orchestrator.tsfiredrunAgent(group, '/compact', ...)un-awaited, mid-callback, guarded only by a closure-scopedSet<string>. This bypassedGroupQueue's one-container-per-group serialization entirely — a second container could spawn and race the primary turn's container on the same host IPC directory (neither call site setsipcRunKey, so both resolve to the identical unscoped path).GroupQueue.enqueueTaskprimitive — the same pattern already used identically inodysseus-server.tsandtask-scheduler.tsfor "serialize a self-dispatch against the group's normal turn." The compact dispatch now only fires after the primaryrunAgentcall has fully resolved, and becauseGroupQueue.state.activeis still true at that point,enqueueTaskalways queues rather than running concurrently —drainGrouppicks it up under full serialization once the primary container's state is cleanly reset.Important context: this specific race is currently unreachable in production
Traced the full data path for
result.contextStats/result.compactionEvent(the fields that gate the dispatch):ContainerRuntime.runTurn'sonOutputonly forwardsoutput_text/activity/session/error/turn_complete;RuntimeEventhas no variant carryingcontextStats/compactionEventat all;runAgent'seventSinkonly synthesizes{status,result}-shaped callback objects. Soresult.contextStats?.autoCompactis alwaysundefinedtoday — confirmed independently by an existing code comment atsrc/config.ts:184-191("the runtime eventSink does not forward contextStats... tracked follow-up") and bygit log -S contextStatsshowing zero commits ever wiring that forwarding path since the auto-compact feature was introduced (a08d0e71, LIA-94).This means DEUS's own proactive auto-compact trigger has been silently inert since introduction — the SDK's own internal auto-compaction (a separate mechanism) still works, so nothing is on fire. This PR ships as cheap, precedented preventive hardening for a landmine that would activate the moment the forwarding gap closes (a follow-up ticket will track that separately) rather than as a fix for an actively-reproducing bug — flagging so reviewers don't expect an observable behavior change or an E2E repro.
Test plan
npx tsc --noEmit && npx tsc— cleannpx vitest run— 109 files, 1942 tests, 0 failuresmessage-orchestrator.test.ts(regression-pin: real event pipeline never callsqueue.enqueueTask, documenting current unreachability),group-queue.test.ts(pending-task dedup branch, previously only covered for running tasks)Note on merge order
This PR and #984 (silent agent-run failure notice, separate PR) both touch
message-orchestrator.test.ts'smakeQueue()test helper (different new keys) and the top ofcreateMessageOrchestrator. Whichever merges second will need a small rebase to reconcile both.🤖 Generated with Claude Code