Steps to reproduce
- Run T3 Code 0.0.32 with a mature thread containing substantial message and activity history.
- Enable Settings → Assistant output → Stream assistant messages.
- Start a Codex turn that streams a moderately long response.
- Observe CPU and RSS for the T3 server process (not only the provider subprocess).
- Compare with assistant streaming disabled.
The degradation grows with both the number of streamed response chunks and the amount of existing thread history.
Expected behavior
Streaming an assistant response should have approximately constant per-delta projection cost. Assistant text deltas should not repeatedly load and decode complete message, proposed-plan, activity, and approval collections.
Actual behavior
Each provider assistant-text delta is dispatched as a durable thread.message.assistant.delta command and becomes a thread.message-sent event.
Every event is then applied serially to all nine projectors. The threads projector handles every thread.message-sent event by invoking refreshThreadShellSummary, which reloads all messages, proposed plans, activities, and pending approvals for that thread.
Sanitized measurements from a real 0.0.32 database:
- 3,610 logical messages produced 128,972
thread.message-sent events
- Average: 35.7 persisted chunk events per logical message
- Maximum observed: 2,436 events for one 11,646-character message
- Busiest thread: 25,878 orchestration events
- With approximately one new orchestration event per second, the T3 server sustained 110–165% CPU
- Server RSS oscillated between approximately 1.4 GB and 2.6 GB
The observed cost therefore scales with streamed chunks × accumulated thread history. Repeatedly loading and decoding the full activity collection also creates significant allocation and garbage-collection pressure.
Relevant source paths
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts: when enableAssistantStreaming is true, each assistant delta is individually dispatched (currently around lines 1638–1683).
apps/server/src/orchestration/Layers/ProjectionPipeline.ts: refreshThreadShellSummary loads all four thread collections (currently around lines 547–589).
- The same projector calls that refresh for every
thread.message-sent event (currently around lines 845–860).
- Every event runs nine projectors serially, with a transactional projector cursor update for each one (currently around lines 1601–1692).
This appears to undermine the intent of #1647, which removed an earlier thread-wide scan from message projection.
Issue #2761 is adjacent but appears distinct: that report concerns large WebSocket snapshots and reconnect behavior, while this report concerns synchronous persistence/projection work during active streaming.
Suggested direction
The narrowest fix may be to avoid refreshThreadShellSummary for assistant thread.message-sent events. Assistant text cannot change latestUserMessageAt, pending approvals, pending user input, or actionable proposed-plan state.
More generally, shell-summary fields could be maintained incrementally or through targeted aggregate queries for the event types that can actually affect each field. A useful regression contract would be: projecting an assistant delta performs a constant number of queries independent of thread history and does not query activities, proposed plans, or approvals.
Impact
Major degradation or frequent failure.
Version or commit
T3 Code Alpha 0.0.32.
Environment
macOS on Apple Silicon, desktop app, Codex app-server provider, assistant streaming enabled.
Logs or stack traces
No raw logs or database artifacts are attached because they can contain private agent content. The measurements above contain aggregate counts only.
Workaround
Disable Stream assistant messages. The server checks this setting while processing deltas, so subsequent text is handled by the existing buffered path without terminating the provider session. Starting a fresh thread reduces the history-dependent portion of the cost but does not address the per-delta transaction amplification.
Steps to reproduce
The degradation grows with both the number of streamed response chunks and the amount of existing thread history.
Expected behavior
Streaming an assistant response should have approximately constant per-delta projection cost. Assistant text deltas should not repeatedly load and decode complete message, proposed-plan, activity, and approval collections.
Actual behavior
Each provider assistant-text delta is dispatched as a durable
thread.message.assistant.deltacommand and becomes athread.message-sentevent.Every event is then applied serially to all nine projectors. The threads projector handles every
thread.message-sentevent by invokingrefreshThreadShellSummary, which reloads all messages, proposed plans, activities, and pending approvals for that thread.Sanitized measurements from a real 0.0.32 database:
thread.message-senteventsThe observed cost therefore scales with streamed chunks × accumulated thread history. Repeatedly loading and decoding the full activity collection also creates significant allocation and garbage-collection pressure.
Relevant source paths
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts: whenenableAssistantStreamingis true, each assistant delta is individually dispatched (currently around lines 1638–1683).apps/server/src/orchestration/Layers/ProjectionPipeline.ts:refreshThreadShellSummaryloads all four thread collections (currently around lines 547–589).thread.message-sentevent (currently around lines 845–860).This appears to undermine the intent of #1647, which removed an earlier thread-wide scan from message projection.
Issue #2761 is adjacent but appears distinct: that report concerns large WebSocket snapshots and reconnect behavior, while this report concerns synchronous persistence/projection work during active streaming.
Suggested direction
The narrowest fix may be to avoid
refreshThreadShellSummaryfor assistantthread.message-sentevents. Assistant text cannot changelatestUserMessageAt, pending approvals, pending user input, or actionable proposed-plan state.More generally, shell-summary fields could be maintained incrementally or through targeted aggregate queries for the event types that can actually affect each field. A useful regression contract would be: projecting an assistant delta performs a constant number of queries independent of thread history and does not query activities, proposed plans, or approvals.
Impact
Major degradation or frequent failure.
Version or commit
T3 Code Alpha 0.0.32.
Environment
macOS on Apple Silicon, desktop app, Codex app-server provider, assistant streaming enabled.
Logs or stack traces
No raw logs or database artifacts are attached because they can contain private agent content. The measurements above contain aggregate counts only.
Workaround
Disable Stream assistant messages. The server checks this setting while processing deltas, so subsequent text is handled by the existing buffered path without terminating the provider session. Starting a fresh thread reduces the history-dependent portion of the cost but does not address the per-delta transaction amplification.