Skip to content

feat: add durable app-server queued turns#21466

Draft
efrazer-oai wants to merge 2 commits intomainfrom
dev/efrazer/app-server-queue-gated
Draft

feat: add durable app-server queued turns#21466
efrazer-oai wants to merge 2 commits intomainfrom
dev/efrazer/app-server-queue-gated

Conversation

@efrazer-oai
Copy link
Copy Markdown
Contributor

Summary

Queued follow-up turns are currently owned by clients, so a renderer reload can lose the next user command before it ever reaches app-server. This change moves the durable queue primitive into app-server so clients can enqueue future turns once, recover them after reconnecting, and let app-server drain them when the active turn reaches a terminal state.

This adds persistent queued-turn storage, v2 JSON-RPC methods under thread/queue/*, queue-change notifications, and queue draining after terminal turns. Queue and steer remain separate operations: clients still choose whether to steer the active turn or enqueue a future turn.

Design decisions

  • Store normalized future turn params in SQLite, not client composer snapshots, so app-server owns the recoverable execution state while each client keeps its own local UI state.
  • Keep the API narrow: add, list, delete, and reorder match the behavior clients already expose today.
  • Treat a failed head item as blocking. App-server records lastDispatchError on that queued turn instead of silently skipping ahead and changing user-visible order.
  • Emit the queue snapshot on resume and queue-change notifications on mutation so reconnecting clients can rebuild their view from server state.

Testing

Tests: targeted app-server queue integration tests, state-store tests, schema fixture tests, and local Codex App smoke testing with a custom CLI build proving a queued follow-up survives a renderer reload and later drains into a real second turn.

@efrazer-oai
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8aed86715d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread codex-rs/state/src/runtime/queued_turns.rs Outdated
@efrazer-oai efrazer-oai force-pushed the dev/efrazer/app-server-queue-gated branch 2 times, most recently from 50245ae to 1a25424 Compare May 7, 2026 03:20
@efrazer-oai
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a254249bc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread codex-rs/state/src/runtime/queued_turns.rs
@efrazer-oai efrazer-oai force-pushed the dev/efrazer/app-server-queue-gated branch from 1a25424 to 8b0004c Compare May 7, 2026 03:30
@efrazer-oai
Copy link
Copy Markdown
Contributor Author

@codex review

@efrazer-oai efrazer-oai force-pushed the dev/efrazer/app-server-queue-gated branch from 8b0004c to bebdc22 Compare May 7, 2026 03:32
@efrazer-oai
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bebdc228e9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@efrazer-oai efrazer-oai force-pushed the dev/efrazer/app-server-queue-gated branch from bebdc22 to cf489a3 Compare May 7, 2026 03:39
@efrazer-oai
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cf489a3b7a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@efrazer-oai efrazer-oai force-pushed the dev/efrazer/app-server-queue-gated branch from cf489a3 to bc3d438 Compare May 7, 2026 03:52
@efrazer-oai
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc3d4381ee

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread codex-rs/app-server/src/request_processors/thread_queue_processor.rs Outdated
@efrazer-oai efrazer-oai force-pushed the dev/efrazer/app-server-queue-gated branch from bc3d438 to bc8a27f Compare May 7, 2026 03:59
@efrazer-oai
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc8a27f5d8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@efrazer-oai efrazer-oai force-pushed the dev/efrazer/app-server-queue-gated branch from bc8a27f to 4414e1a Compare May 7, 2026 04:08
@efrazer-oai
Copy link
Copy Markdown
Contributor Author

@codex review

@efrazer-oai efrazer-oai force-pushed the dev/efrazer/app-server-queue-gated branch from 4414e1a to 61d9aab Compare May 7, 2026 04:18
@efrazer-oai
Copy link
Copy Markdown
Contributor Author

@codex review

@efrazer-oai efrazer-oai force-pushed the dev/efrazer/app-server-queue-gated branch from 61d9aab to f3477d8 Compare May 7, 2026 04:27
@efrazer-oai
Copy link
Copy Markdown
Contributor Author

@codex review

1 similar comment
@efrazer-oai
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f3477d8b76

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +175 to +179
if self
.thread_has_live_in_progress_turn(thread_id, thread.as_ref())
.await
{
return;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge Mark dispatched turns active before draining again

This idle check can still return false just after start_queued_turn succeeds, because submit_with_trace only enqueues the core op and the listener has not necessarily observed turn/started yet. A second queue mutation in that window can dispatch the next queued item immediately instead of waiting for the first queued turn to reach a terminal state.

Useful? React with 👍 / 👎.

Comment on lines +78 to +80
self.turn_start_inner(
/*request_id*/ None, params, /*app_server_client_name*/ None,
/*app_server_client_version*/ None,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Preserve client compatibility when draining queued turns

start_queued_turn passes None for the app-server client name/version, but turn_start_inner still calls set_app_server_client_info. For Xcode 26.4 sessions this recomputes mcp_elicitations_auto_deny as false, undoing the compatibility auto-deny setting before queued turns run.

Useful? React with 👍 / 👎.

Comment on lines +58 to +63
let turn_start_params_json =
serde_json::to_string(&params.turn_start_params).map_err(|err| {
internal_error(format!("failed to serialize queued turn params: {err}"))
})?;
let queued_turn = state_db
.append_thread_queued_turn(thread_id, turn_start_params_json)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Cap queued payload size before persisting

thread/queue/add validates only text characters, then persists the full serialized turnStartParams. Non-text fields such as image URLs, paths, metadata, and outputSchema remain unbounded, so a client can store arbitrarily large queued rows and repeatedly force DB reads/notifications with those payloads.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f3477d8b76

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +208 to +210
if let Err(err) = state_db
.delete_thread_queued_turn(thread_id, queued_turn.queued_turn_id.as_str())
.await
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Avoid resubmitting queued turns after a crash

After start_queued_turn succeeds, the durable row is deleted in a separate later DB operation. If app-server exits or is killed in this window, the row remains; on the next resume/drain, first_dispatchable_thread_queued_turn will submit the same queued user input again, duplicating commands despite the queue being the recoverable execution state.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f3477d8b76

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +202 to +204
match self
.turn_processor
.start_queued_turn(turn_start_params)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge Serialize queued dispatch until the turn is observed running

start_queued_turn only enqueues an op to core; it does not wait for turn/started or mark app-server state active. A second thread/queue/add on an idle thread can therefore pass the idle check before the listener sees the first turn and dispatch the next queued item too, causing core to steer/replace the active turn instead of preserving one durable turn per terminal boundary.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f3477d8b76

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +202 to +209
match self
.turn_processor
.start_queued_turn(turn_start_params)
.await
{
Ok(_) => {
if let Err(err) = state_db
.delete_thread_queued_turn(thread_id, queued_turn.queued_turn_id.as_str())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge Persist dispatch state before starting queued turns

The queue row is deleted only after start_queued_turn returns. If app-server exits or is killed after core accepts the turn but before this DELETE commits, the durable row survives; on the next resume/drain the same user input is submitted again, duplicating a turn and potentially repeating tool actions. The durable queue needs an atomic in-flight/claimed state or idempotent dispatch before starting.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f3477d8b76

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +175 to +177
if self
.thread_has_live_in_progress_turn(thread_id, thread.as_ref())
.await
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge Treat submitted queued turns as active immediately

start_queued_turn only enqueues the core op; agent_status/active_turn_snapshot do not change until the later TurnStarted event is processed. A second queue mutation in that window passes this idle check and dispatches/deletes another queued turn before the first queued turn reaches a terminal state, breaking the durable one-at-a-time queue semantics.

Useful? React with 👍 / 👎.

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