[feat] Session control: server-held pending input, Queue then Steer - #6555
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
1fff42e to
f092282
Compare
f092282 to
47bbb11
Compare
mmabrouk
left a comment
There was a problem hiding this comment.
Agent-generated Codex review, low weight.
CHANGES NEEDED
Seven inline correctness findings block this increment: successful-completion classification, admission/settlement fencing, retry idempotency, cross-tab admission, Steer binding, recoverable-input visibility, and an optional delivery receipt dereference.
The oss000000028 migration is additive; its nullable audit/promotion columns are safe, and the expanded command-kind check accepts every previously valid live value. I found no dropped release code in the removed lines. With Queue and Steer disabled, existing clients omit on_busy and both desktop and mobile retain the legacy send path. The runner's explicit error stopReason does not itself add a terminal event or change parking, but the completion watchdog must honor that new non-completing value as noted inline.
Validation: api/oss/tests/pytest/unit/sessions/test_session_inputs_dao.py passed 7 tests with xdist explicitly disabled; web/packages/agenta-chat/tests/unit/hooks/useAgentChatQueue.test.ts passed 21 tests with maxWorkers=2. Both ran serially at nice 19. The API file was rerun with -n 0 after the repository defaults unexpectedly started xdist on the first invocation.
Non-blocking simplification: SessionReadSnapshot/read is a constant latest_sequence=0, history_complete=true placeholder unused by Queue or Steer. It and its generated client type can be deferred until replay/cursor support without losing this milestone's RFC behavior.
mmabrouk
left a comment
There was a problem hiding this comment.
Agent-generated Codex review, low weight.
CHANGES NEEDED
Two P1 interleavings remain. The other five findings and the SessionReadSnapshot cut are closed. The 408023c admission lock adds no cycle with normal completion or answer transactions, and no external delivery or invoke work runs while it is held. bb6b6fe preserves the Queue-off Send control flow. e9ce73d removes only unused projection definitions, generated export, schema default, and fixtures.
Focused checks passed at nice 19: test_session_inputs_dao.py, 8 tests with pytest -n 0; useAgentChatQueue.test.ts, 22 tests with maxWorkers=2.
mmabrouk
left a comment
There was a problem hiding this comment.
Agent-generated Codex review, low weight.
CHANGES NEEDED
The Stop/Steer lock inversion and stale command read are fixed: terminal settlement and collapsed binding now both acquire the source execution before the command. The non-empty settlement regression covers a recoverable successor, but the first P1 remains when that promoted continuation has successfully started; see the inline finding.
mmabrouk
left a comment
There was a problem hiding this comment.
Agent-generated Codex review, low weight.
SHIP
Reviewed 47bbb114d3..1d7d250b3e as one unit. The round 4-6 fixes preserve durable retry identity, serialize admission with settlement, keep promoted/recoverable work visible without changing the running snapshot semantics, bind Steer to the winning Stop under a consistent execution-first lock order, and handle lost delivery reservations. The final active-successor revalidation also locks the unsettled successor while inserting, so a running continuation cannot be overtaken or settle past the new input.
No remaining correctness findings. The full API sessions suite passed (744 tests), Ruff 0.15.12 format and lint passed, the pushed SHA matches the remote, and all review threads are resolved.
mmabrouk
left a comment
There was a problem hiding this comment.
SHIP
The shared capability helper now keeps the stream and snapshot responses aligned for durable approvals, queue, and queue-gated steer. The API regression covers enabled and disabled flag combinations. Desktop and mobile both use the shared snapshot-backed session-input adapter, and the new client regression verifies that a queue-capable snapshot produces durable admission with on_busy: "queue".
Validated with the API sessions suite (746 passed), chat unit suite (674 passed), OSS unit suite (431 passed, 1 skipped), both package typechecks and lints, and Ruff 0.15.12.
Reviewed-by: Codex
mmabrouk
left a comment
There was a problem hiding this comment.
SHIP
Agent-generated, low weight.
Reviewed the two Round 8 commits at 7f6fd63. Stop and Escape now remain available for both locally streaming and server-owned durable runs on desktop and mobile, including a server-busy turn with one queued input. Enter and the Queue action now share awaited durable on_busy admission; a failed admission restores the draft instead of creating a browser-only queued badge.
No blocking findings. Full @agenta/chat, @agenta/oss, and @agenta/mobile unit suites, type checks, package lint, repository lint-fix, and the @agenta/chat dependency build passed.
mmabrouk
left a comment
There was a problem hiding this comment.
SHIP
Round 9 review: the live delay came from the done-record settlement promoting immediately while the completed runner generation still owned Redis liveness; the continuation heartbeat was rejected, leaving the abandoned-command sweep to retry it 98 seconds later. The shared normal-completion settlement now reconciles that source generation after commit and before delivery, so both record-ingest and watchdog completion paths admit the promoted continuation immediately. The regression requires done-record-to-admission latency below one second and makes delivery contingent on source reconciliation.
The client now suppresses the stale running-elsewhere banner only when the authoritative session snapshot is idle and a durable queued item is visible; genuinely running remote work still shows the warning and the queued badge remains.
Reviewed the round-9 diff and the live thread set (10/10 resolved). Validation: API sessions 747 passed; ruff 0.15.12 format/check clean; chat 677 passed plus types/lint; OSS 434 passed, 1 skipped, plus types/lint.
mmabrouk
left a comment
There was a problem hiding this comment.
SHIP
Round 10 review found the live Steer loss was confined to the client admission path. The initial fresh-run HTTP 200 kept the composer single-flight guard locked for the full SSE lifetime, so the later Steer click returned without issuing a second admission request. The Steer promise was also fire-and-forget, which prevented a real admission refusal from reaching the existing draft-restoration failure card.
The pushed fix releases the submit guard once accepted response headers arrive while draining the active stream in the background, and propagates Steer admission failures through both desktop and shared/mobile conversation paths. The API/runner chain remains intact: durable priority insert -> Stop command bound to steer_input_id -> registered execution abort -> atomic promotion of only that Steer input.
Verified locally: API sessions 747 passed; runner 2712 passed; chat 681 passed; OSS 434 passed with 1 skipped; mobile 150 passed. Chat/OSS/mobile and runner typechecks pass; requested lint and Ruff 0.15.12 checks pass. No blocking findings remain.
mmabrouk
left a comment
There was a problem hiding this comment.
SHIP
Round 11 reviewer pass found no blocking findings.
The 8880 API and runner logs confirm the reported Queue losses died before durable admission: neither 08:17:47Z nor 08:22:11Z has an inputs/admit request or control event. Round 10 already fixed the shared cause for both Queue and Steer: the fresh 200 admission releases the common submit guard at response headers, and both policies then route through the same durable server adapter.
The new regression recreates the detached local stream/server-running state while the first response body remains open. Enter, the Queue button, and Steer each send the expected on_busy admission and render the pending badge; a 409 restores the draft and shows the failure card.
Local validation: chat 685 passed; OSS 434 passed and 1 skipped; mobile 150 passed; runner 2712 passed. Chat, OSS, mobile, and runner type checks passed; lints passed with the three pre-existing mobile hook warnings.
9fdf78b to
c5659f2
Compare
81de3a5 to
4ff4d9a
Compare
Include promoted inputs while their continuation is pending delivery or recoverable so snapshots do not hide accepted work after delivery failure. Remove them from the queue view once the continuation reaches running. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Treat a missing delivery reservation receipt as a recoverable continuation failure after normal completion and Steer settlement. Cover both paths when another worker moved the continuation command first. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Remove the unused constant read projection from the session snapshot contract and generated TypeScript client. Keep the durable queue snapshot focused on execution, pending work, and capabilities until replay semantics are implemented. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Recheck pending inputs after a source execution settles so admission preserves the successor selected by settlement. Queue the new input behind that continuation and report its execution id. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Serialize Steer binding and Stop settlement by locking the execution before reading or updating the command. Re-read the command inside settlement so a winning bind is promoted. Cover both bind-first and settlement-first interleavings against Postgres. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Revalidate promoted successors independently of the public pending list and lock the active continuation until admission finishes. Cover delivery advancing the promoted continuation to running before admission resumes. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Build session capabilities through one helper so stream and snapshot responses expose the same feature flags. Cover both API flag combinations and the shared desktop/mobile durable admission request. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Derive the composer Stop state from local or server-owned execution state on desktop and mobile. Keep Escape scoped to the active composer and use the mobile server command when only durable liveness remains. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Await Queue-capable composer submissions and propagate admission failures instead of presenting them as browser-only queued messages. Restore the desktop draft on failure; mobile reuses its existing retry surface. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Reconcile the completed execution's Redis generation after the durable settlement commit and before delivering its promoted continuation. This prevents the continuation heartbeat from colliding with the source turn's final heartbeat and waiting for the abandoned-command sweep. Add a release-aware regression that measures done-record-to-admission latency below one second. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Expose the authoritative snapshot execution state to the desktop conversation and suppress stale running-elsewhere liveness only when that execution is idle with durable queued work. Keep the queued badge visible, and preserve the banner for genuinely running remote executions. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Release accepted fresh-run admissions when their response headers arrive while continuing to consume the stream in the background. This lets later Queue and Steer actions reach durable admission during the active turn. Propagate refused Steer admissions through desktop and mobile callers so the existing failure card restores and keeps the draft instead of clearing it silently. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Exercise Enter, Queue, and Steer after a fresh admission releases at response headers while the source tab is observing the server-owned run. Assert each durable request refreshes the pending badge, and a refusal restores the draft with the existing failure card. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
The strip was reworded on the milestone 2 base: it now says the turn is still running and warns that a still transcript may mean the run already ended. The durable admission suite still asserted the older sentence. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
Milestone 2 renames the liveness flag this component destructures, and its steer sender takes only text and file parts. The rebase carried increment 7's call sites unchanged, so the remote-run gate referenced a name that no longer exists and the steer call passed a staged-file list the sender does not accept. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
4ff4d9a to
b92f46e
Compare
Milestone 2 and increment 7 each built a snapshot endpoint on GET /sessions/{session_id},
independently: increment 7 was cut from a milestone 1 head that did not carry milestone 2's.
Assembled, the API defined SessionSnapshotResponse twice and registered two handlers on one
path, so the second definition shadowed the first and only one endpoint was reachable. The
web client could export only one fetchSessionSnapshot, which orphaned the other reader.
Milestone 2 merges first, so its contract is the base. There is now one response model that
keeps milestone 2's required session and read watermark and adds increment 7's fields, one
handler that fills both halves, and one client function both readers call.
The two execution questions stay separate because they are different questions. `execution`
is the last turn, whose end_time tells the live preview whether that turn is still running.
`execution_state` is the session's current lifecycle, derived from the stream row, which is
what the durable queue admits against. Capabilities come from the same helper the streams
endpoint uses, so a client can never see the two disagree. The pending-input list is
optional in effect: a deployment without the inputs service still gets the reconnect half
and reports an empty queue.
The generated client was hand-edited rather than regenerated. The generator builds from a
live OpenAPI document; the spec this worktree can produce covers OSS only, so a full
regeneration would have deleted the cloud surface the committed client carries.
Both feature sets keep their tests: the milestone 2 snapshot, sequence and replay suites and
the live preview hook, and increment 7's queue admission and capability suites. Two new API
tests pin that one call carries both halves, and that the queue half degrades to empty when
the inputs service is absent.
Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
mmabrouk
left a comment
There was a problem hiding this comment.
Agent-generated Codex review, low weight.
CHANGES NEEDED
The 24-commit range-diff preserves the reviewed queue/steer ordering and delivery invariants, and the unified model/client supplies both reader contracts. One availability regression remains: the queue half is unreachable with the shared-reader flag off or before a fresh session has its first stream row, so desktop and mobile disable durable Queue/Steer and use the browser-local fallback. See the inline P1.
Focused validation: unified snapshot tests 4 passed; API client, entities, and chat typechecks passed.
Return queue lifecycle, pending work, and capabilities when the shared reader is disabled or a fresh session has no stream row yet. Make reconnect-only fields nullable across the API and client contracts, and keep reconnect readers from consuming missing session data. Add API and client regressions for flag-off, fresh-session, full-contract, and queue-enable behavior. Claude-Session: https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk
mmabrouk
left a comment
There was a problem hiding this comment.
Agent-generated, low weight.
SHIP
Reviewed dcf4753 as the Round 12 fix. The unified snapshot now preserves the queue/steer control plane when reconnect data is unavailable, keeps reconnect fields nullable across the API, Fern, and Zod contracts, and guards both milestone-2 reconnect reads. The shared-reader-off, fresh-session, full-contract, schema-boundary, queue-hook, and null-session reader paths are covered.
Validation: API sessions 859 passed; chat 815 passed; entities 1,553 passed; OSS 483 passed with 1 skipped; mobile 163 passed. Ruff 0.15.12, generated client build, all requested typechecks, and lint completed cleanly (mobile retained 3 pre-existing warnings).
Railway Preview Environment
|
Context
Today a message sent while a turn runs is refused, and the desktop keeps a local queue in the browser that other tabs and the mobile app cannot see. Increment 7 of the session-control design (PR #6495) moves pending input to the server: Queue first, then Steer. It builds on the durable approvals of #6530 because Steer extends the same command path.
What this adds
on_busy=queuewhile a turn runs is stored as a pending input with a stable position and an idempotency key, and the API answers 202 with the input id. A pending input can be removed until it is promoted. The session snapshot lists pending inputs in order. Migrationoss000000028adds the table, nullable and with a downgrade, after 027.Both behaviours ship dark behind their own switches in
env.py, default off. With the switches off a busy session still answers 409 as today.Tests
Agent-generated, low weight. Draft until the review and the browser pass are done.
https://claude.ai/code/session_0164kzT6ttwpBtzvcDC6YzYk