Skip to content

fix(server): assistant streaming no longer rescans thread history - #5855

Open
cheruvian wants to merge 2 commits into
pingdotgg:mainfrom
cheruvian:t3code/incremental-thread-projections
Open

fix(server): assistant streaming no longer rescans thread history#5855
cheruvian wants to merge 2 commits into
pingdotgg:mainfrom
cheruvian:t3code/incremental-thread-projections

Conversation

@cheruvian

@cheruvian cheruvian commented Aug 9, 2026

Copy link
Copy Markdown

Fixes #5719.

Problem

The threads projector handled thread.message-sent, thread.activity-appended and four other event types with one blanket refreshThreadShellSummary, which reloaded the thread's entire messages, proposed plans, activities and pending approvals to recompute four summary columns. With assistant streaming enabled every provider text delta is a durable thread.message-sent event, so projection work scaled with streamed chunks times accumulated thread history.

Fix

Each of the four shell-summary fields is owned by exactly one projection, and assistant text can change none of them. The projector now refreshes only the fields an event can actually invalidate:

  • latestUserMessageAt — user messages only, advanced in place as a running maximum, since messages are append-only outside revert
  • pendingApprovalCount — approval activities and thread.approval-response-requested
  • pendingUserInputCount — user-input activities and thread.user-input-response-requested
  • hasActionableProposedPlan — plan upserts and the events that move latestTurnId
  • thread.reverted still re-derives everything, since it is the only path that removes projected rows

Fields are recomputed from the owning projection rather than incremented. That matters for rebuilds: each projector keeps its own cursor and bootstraps over the whole stream in turn, so the threads projector sees the upstream projections already fully caught up — recomputation converges to the correct value where counter arithmetic would drift.

Streaming, event volume and event ordering are unchanged. Only the cost of projecting each event drops.

Measurements

The new test installs a SQL client decorator that records every statement the pipeline issues. For 8 streamed deltas:

statements full-thread scans
before 160 (20/delta) 32 (4/delta)
after 112 (14/delta) 0

The same test pins history-independence: identical statement counts against a 4-activity and a 200-activity thread. A third test appends events without projecting them, runs bootstrap, and asserts the replayed summary matches live projection.

Worth noting for expectations: buffered delivery is the default now, so the ~36 events per message in the issue report only apply to enableLegacyTokenStreaming. This change still helps every setup, because thread.activity-appended fires constantly in both modes and was paying the same full-thread scans.

Verification

  • vp test run apps/server/src/orchestration apps/server/src/persistence apps/server/src/relay — 45 files, 298 tests passed
  • vp run --filter t3 typecheck — clean
  • targeted lint and format — clean

Risks

  • Replay/rebuild — the threads projector bootstraps after the others have caught up, so it sees future rows at every event. Recomputation converges correctly under that ordering; incrementing would not. Covered by the bootstrap test.
  • Reconnect — no behavior change. The old blanket refresh incidentally self-healed on thread.session-set; that is gone, but every writer of the underlying rows now triggers its own field refresh, and only this pipeline writes these columns.
  • Concurrent activity updates — projectors run serially with the owning projector ahead of the threads projector, so a recomputed counter always reads post-write state. One residual gap: if an existing activityId were re-upserted with a different kind, from a user-input kind to a non-user-input one, the counter would not be revisited. No provider path does that today.

Model and harness: Claude Opus 5 (1M context) in Claude Code.

🤖 Generated with Claude Code


Note

Medium Risk
Changes core orchestration projection semantics (transactions, cursor invariants, summary derivation) that affect every live event; risk is mitigated by extensive new cost/rebuild/revert tests and stricter upsert guards.

Overview
Stops assistant streaming deltas from reloading whole thread collections when updating projection_threads shell fields. Shell refresh is now field-scoped via threadShellSummaryFieldsForEvent / refreshThreadShellSummary(..., fields) so assistant thread.message-sent events only bump updatedAt (user messages still advance latestUserMessageAt incrementally); approvals, user-input, plans, and revert paths refresh only the summaries they can invalidate.

Reworks live projectEvent into one SQL transaction: enforce all projector cursors at sequence - 1, run only subscriber projectors per event (projectorNamesForEvent), then advance every cursor in a single projection_state batch (upsertMany). Bootstrap/replay still uses per-projector transactions.

Hardens projection upserts when IDs collide across threads: conditional ON CONFLICT updates for messages, activities, plans, and pending approvals; message/approval apply paths ignore conflicting thread or role; thread.reverted prunes pending approvals tied to removed turns.

Adds ProjectionPipeline.summaryCost.test.ts (SQL statement recording, history-independence, targeted summary scans, bootstrap/revert parity) and extends ProjectionPipeline.test.ts for rollback cursor invariants and bootstrap resume after partial failure.

Reviewed by Cursor Bugbot for commit baa977a. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix assistant streaming to avoid rescanning thread history on each delta event

  • Projection of live events now applies all subscriber projectors and advances all cursors atomically in a single transaction, replacing per-projector individual transactions.
  • Adds projectorNamesForEvent routing so only projectors subscribed to a given event type run apply; non-subscriber projectors still advance their cursor.
  • Adds assertLiveProjectorCursors to enforce that all projector cursors are at sequence - 1 before applying a live event, failing fast on out-of-order projection.
  • Thread shell-summary refreshes are now field-targeted via threadShellSummaryFieldsForEvent, so events like assistant streaming deltas no longer trigger full thread-collection scans.
  • Upsert guards added to projection tables (messages, activities, pending approvals, proposed plans) to skip updates when thread_id, role, or kind conflict with a different owner.
  • ProjectionStateRepository gains a upsertMany method for batching all projector cursor advances in one SQL statement.
  • Risk: projectEvent now fails before applying if any projector cursor is not at the expected prior sequence, which is a new hard precondition not previously enforced.

Macroscope summarized baa977a.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8686d840-8869-4906-b5a5-a02eb64197db

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the vouch:unvouched PR author is not yet trusted in the VOUCHED list. label Aug 9, 2026
@github-actions github-actions Bot added the size:L 100-499 changed lines (additions + deletions). label Aug 9, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR significantly refactors the projection pipeline architecture, introducing event-to-projector subscription routing, targeted shell summary refresh, transaction model changes, and batched cursor updates. While it includes extensive tests, changes to core infrastructure processing behavior warrant human review.

You can customize Macroscope's approvability policy. Learn more.

Refresh only shell fields invalidated by each event so assistant deltas and ordinary activities stay independent of thread history. Recompute approval, user-input, plan, and revert summaries from their owning projections, with SQL-cost and replay-equivalence coverage.
Route events only to projectors that can mutate their projections while advancing every live cursor atomically after successful projection.

Preserve first ownership bindings for projected identities, order bootstrap dependencies, and prune approvals owned by reverted turns so live projection and replay stay equivalent.

Defmon3 commented Aug 13, 2026

Copy link
Copy Markdown

We investigated a Windows T3 instance where agent responses were delayed by minutes even though the provider CLI responded normally outside T3.

Server traces showed thread-shell summary refreshes repeatedly rereading growing thread histories. During the incident, a single refresh took as long as 4.97 seconds, creating a backlog that delayed provider events and UI updates.

After applying this PR:

  • the focused projection-cost suite passed 7/7;
  • a regression scenario containing 14,625 activities kept the SQL work bounded instead of growing with thread history;
  • a fresh isolated 10-subagent run measured the same refresh at no more than approximately 0.6 ms.

This confirms the PR addresses a major bottleneck we observed. We are not claiming it resolves every possible source of T3 latency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Assistant streaming causes full-thread projection scans for every text delta

2 participants