CL-6451: one room participant, one live run — command paths reuse resident agents; stale turns fail - #181
Merged
Conversation
An @mention or workflow command naming a definition already resident in the room must route into the existing participant's run — never mint a sibling — while an explicit re-invite still deliberately places a second instance. A turn row still running past the occurrence timeout must read back failed instead of zombieing (CL-6451).
…ts; stale turns fail An @name resolving to a workflow command is now checked against the definitions the room's agents were launched from — a resident match routes the message into the existing participant's run through the ordinary turn pipeline (queueing behind an in-flight turn) instead of dispatching a launch, and startWorkflowCommand applies the same rule to slash/command dispatch. The explicit invite affordance still always launches: a deliberate second instance of one definition stays possible, which is what handle de-duplication exists for. Turn rows still running past the per-occurrence timeout plus grace are failed on the store's next read or write: a dispatch the supervisor's terminal-or-park backstop killed (or a hub death mid-turn) never sends the closing event, and previously left the turn — and its typing indicator — running forever. Fixes CL-6451
…urns One real stack (scratch DB, real Ollama): invite 'assistant' (handle 'myra'), send '@Assistant' — the message is never intercepted as a command, the room keeps exactly one agent participant, and the reply lands as turn__0 of the invited run. A second '@Assistant' answers as turn__1 of the same run and recalls a fact from turn__0, proving the bootstrap exchange lives in the same stepId-keyed durable history.
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.
Root cause
Two independent defects, one wedge:
Myra->@myra), while the workflow-command registrar names commands after the definition's wire name (assistant).dispatchWorkbenchCommand's known-handle guard compared the handle against the command name, so@assistantsailed past it intodispatchAtCommand->startWorkflowCommand->launchAndJoinAgent, minting run B for an agent the room already held as run A. Later messages fanned out to both, run A never parked, and the dispatch died at the supervisor'swaitForRunTerminalOrPark300s backstop. The same class hit/namere-invocations and any handle drift (dedupe suffixes, slugging).runningforever.dispatchTurncloses its row only whensendMailthrows; a dispatch that fails later in the workflow-host supervisor (the backstop) emits nomessage.run.ended, so nothing ever closed the row — stuck typing indicator, no failed-turn strip.Fix
dispatchWorkbenchCommandnow resolves an@namecommand against the definitions the room's agents were launched from (findResidentAgentForDefinition, comparing by definition asset so re-deployed rows still match). A resident match returns a routing decision instead of a command dispatch: the message posts normally andsendWorkbenchMessageforces the resident participant into the recipient set, so the turn rides the ordinary pipeline — turn row, one-in-flight queueing, reply correlation — into the run the room already has.startWorkflowCommandapplies the same residency rule to slash/command dispatch: a resident definition gets the args delivered as mail to the existing run, never a second launch.echo/echo-2exists for, and what the CL-6329 live proof pins), guarded by a new test.AgentTurnStoreimplementations now fail any row stillrunningpastAGENT_TURN_STALE_MS(per-occurrence timeout + grace) on their next read or write — an interrupted dispatch surfaces as a visible failed turn instead of zombieing, and a later reply can never be attributed to a dead row.Reproduced at scale (longevity campaign, live forensics)
The longevity campaign wedged on exactly this defect while the fix was being built. Read-only forensics from its scratch DB (
longevity_scratch), captured mid-wedge:daily-heartbeatparticipants (daily-heartbeatthroughdaily-heartbeat-13), one minted per scheduled command invocation (18:00, 18:01, 18:04, 18:27, 18:47, 19:00, 19:06, 19:44, 20:00, 20:07, 20:32, 21:00) — thestartWorkflowCommandmint point this PR removes.runningturn rows, 849 older than 10 minutes, 0 ever marked failed — the zombie-row defect this PR's stale-turn expiry closes. All 905 wedged rows sit on the one working agent (sales-analyst).@nameform rides the full turn pipeline (turn row, queueing, reply correlation).Verification
turn__0/turn__1on the same occurrence sequence; command reuse by id and by asset; stale-turn expiry in both stores (drizzle suite run against a scratch Postgres).scripts/e2e/cl-6451-single-run-proof.ts, scratch DB + real Ollama): inviteassistant(handlemyra), send@assistant— no command interception, one agent participant, reply asturn__0of run A; a second@assistantmessage answers asturn__1of the same run and recalls a fact stated in turn 1, proving the bootstrap exchange lives in the same stepId-keyed durable history (CL-6453's symptom).bun run checkgreen.Fixes CL-6451
Fixes CL-6453