🤖 feat: boundary-windowed chat loading + metadata-only workspace activity#2493
🤖 feat: boundary-windowed chat loading + metadata-only workspace activity#2493
Conversation
Change-Id: I34b8bbedfb456c8b3a27b4906047082b9a448284 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Switch startup replay to the latest boundary and add backend paging for older compaction epochs. - changed AgentSession replay to start at skip=0 (latest compaction boundary) - added HistoryService.getHistoryBoundaryWindow() to return one older epoch window plus hasOlder - exposed workspace.history.loadMore in ORPC schema/router/workspace service - added historyService tests covering boundary-window paging behavior Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I7f1ada3dd8daf93ec12ff18bc09edbcfe63758ed Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I3da0fed5263b45bc367ef1e243cf083bfbac57c7 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Id0ffd056c360c8879fdf33c6fc1ddd3fc275fb48 Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f2200cbcfc
ℹ️ 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".
Change-Id: Iabe3f84b0cefe77a87863146377117904853bc62 Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review |
Change-Id: Ifebd5176f6d91f2f393e082754571819265a76f4 Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review Addressed both review comments:
|
Change-Id: I8b332c398a28b788587425f78d634bd72b1b3d99 Signed-off-by: Thomas Kosiewski <tk@coder.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 48b6215b01
ℹ️ 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".
…ed pagination threshold Change-Id: Ia78e76e48c4466a1f68a2110c511e12e9f44a43c Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review Addressed both new review comments:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5f09caa963
ℹ️ 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".
Change-Id: Ic7544045454bbdc5e3d544ba678a67ddfb77d1d7 Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I5b43c0ea2aba795d3b5ea441c56a642c09a0fe25 Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review |
Change-Id: I723b02f703e4401310ecc2f88a3e4891811a9be9 Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 019062e776
ℹ️ 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".
Change-Id: Ic6b8914985a58d6c4a4cb7f81de5ef0ee01868ea Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I3020443107312cd5ea5eeb0e4bbf8c367571bf9e Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review |
When switching to a workspace that was streaming in the background, there's a brief window where the aggregator is cleared and replaying history. During this window, trust the activity snapshot for canInterrupt/model/thinkingLevel instead of the empty aggregator state. Uses the existing transient.caughtUp flag as the guard: only trust the aggregator once the onChat replay has delivered the caught-up marker. Change-Id: I6d88bb91ced5ba6ce2911ffefdce4b48a6342312 Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a6687897b9
ℹ️ 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".
The setActiveWorkspaceId call was a no-op when the workspace hadn't been registered in the store yet (isWorkspaceRegistered returns false). In integration tests, the WorkspaceContext sync may not have completed by the time setupWorkspaceView runs. Call addWorkspace(metadata) first to guarantee registration before activation. Also expose addWorkspace on the workspaceStore wrapper for test access. Change-Id: I972c6ecc5ce02e0954bbcd8a73131be4ba9727e8 Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review |
b0773a5 to
a044f61
Compare
|
@codex review |
a044f61 to
4103704
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4103704408
ℹ️ 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".
Two force-compaction UI integration tests regularly hit the 60s Jest timeout on CI while still completing locally and in slower runners. Increase only those two test-level budgets to 120s to reduce false failures without relaxing per-assertion waits or changing production behavior. Change-Id: I200ca33fec20b51032cc8a4597c29f570fe0d43c Signed-off-by: Thomas Kosiewski <tk@coder.com>
4103704 to
5063ef2
Compare
|
@codex review |
When a live compaction boundary prunes older messages, update establishedOldestHistorySequence to the incoming boundary sequence. Without this, getOnChatCursor() could keep advertising a pre-compaction oldestHistorySequence, causing AgentSession's since-replay floor guard to mismatch and force unnecessary full replays after disconnects. Also add a regression test covering the cursor floor update after a live compaction boundary. Change-Id: I73b22ba5a882b651496713793da2f980c5790e1d Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 405e73e2ae
ℹ️ 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".
Change-Id: I310face18e827b6a7c0e40e2035ffca1825e0e22 Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 94634553b3
ℹ️ 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".
Change-Id: I3de86f43b80fcc5bc7487eb03e2009e0a1ce33a8 Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review |
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e676f97ebf
ℹ️ 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".
Change-Id: I8536eef3c59bc338ecb324719dac12294bc7a947 Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review |
Change-Id: Iadd5dc7f06c81df008c924efdf96a93153371c20 Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fb3a0eaa28
ℹ️ 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".
Change-Id: I6f598ad156509e5f29872ebc53d3ba235e2da49e Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4e59cbc0f1
ℹ️ 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".
Change-Id: I90cd758157633205385023cc71731baf5a13cec4 Signed-off-by: Thomas Kosiewski <tk@coder.com>
|
@codex review Addressed the remaining background-compaction callback feedback and added regression coverage. Please take another look. |
|
Codex Review: Didn't find any major issues. Another round soon, please! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Summary
Overhaul chat subscription architecture to load only the current compaction epoch on startup, scope full transcript streaming to the active workspace, and add cursor-based "Load More" pagination for older history.
Background
Previously, every workspace started a full
onChatsubscription that replayed from the penultimate compaction boundary, meaning all workspaces eagerly loaded two epochs of history regardless of whether they were visible. This caused unnecessary data transfer at startup and steady-state bandwidth waste.This PR introduces three key changes:
skip=0(latest boundary only) instead ofskip=1onChatstream; all others use a lightweight metadata-only activity feed for sidebar indicatorsImplementation
Backend (
agentSession.ts,historyService.ts, ORPC schemas/router)emitHistoricalEvents()now callsgetHistoryFromLatestBoundary(workspaceId, 0)workspace.history.loadMoreendpoint returns a single older boundary window with cursor-based paginationgetHistoryBoundaryWindow()helper scans boundaries to return exactly one epoch window at a timeFrontend aggregator (
StreamingMessageAggregator.ts)pruneBeforePenultimateBoundary→pruneBeforeLatestBoundaryFrontend store (
WorkspaceStore.ts,WorkspaceContext.tsx)addWorkspace()no longer startsrunOnChatSubscription()— subscription is managed byensureActiveOnChatSubscription()onChatstream active at a time, switched viasetActiveWorkspaceId()workspace.activity.list/subscribe) provides streaming/recency/model fallbacks for non-active workspacescaught-up, exposed ashasOlderHistory/loadingOlderHistoryinWorkspaceStateFrontend UI (
ChatPane.tsx)hasOlderHistoryis trueValidation
make typecheck✅make lint✅make fmt-check✅bun testacross 3 targeted test suites (150 tests, 427 assertions) ✅bun test src/node/services/agentSession(43 tests) ✅Risks
historySequencemetadata may cause pagination to stop earlier than expected (safe degradation rather than crash).📋 Implementation Plan
Plan: boundary-windowed chat loading + metadata-only workspace activity
Context / Why
We want chat startup to feel snappier by reducing unnecessary replay volume and decoupling sidebar status from full transcript streams.
Requested outcome:
workspace.onChatstreams for every workspace; use a metadata-only subscription for cross-workspace status (new activity / stream finished), while keeping full chat streaming focused on the active workspace.This reduces startup data transfer, avoids replaying large historical tails by default, and preserves user-controlled access to older history.
Evidence
src/node/services/agentSession.tsemitHistoricalEvents()currently callshistoryService.getHistoryFromLatestBoundary(workspaceId, 1)(penultimate boundary replay) beforecaught-up.onChatreplay modes already exist (full/since/live) with cursor-based reconnect safety checks.src/node/services/historyService.tsgetHistoryFromLatestBoundary(workspaceId, skip)already supports boundary-window selection (skip=0latest,skip=1previous, etc.), which can back a Load More flow.src/browser/utils/messages/StreamingMessageAggregator.tspruneBeforePenultimateBoundary) and includes an explicit TODO for paginated older-history support.src/browser/stores/WorkspaceStore.tsaddWorkspace()immediately startsrunOnChatSubscription()for every workspace duringsyncWorkspaces().runOnChatSubscription()already usessincecursor reconnects viaaggregator.getOnChatCursor().src/common/orpc/schemas/api.ts+src/node/orpc/router.ts+src/common/orpc/schemas/workspace.tsworkspace.activity.list+workspace.activity.subscribewithWorkspaceActivitySnapshot { recency, streaming, lastModel, lastThinkingLevel }.Storage layout assessment: split
chat.jsonlinto epoch files?Recommendation: not in this iteration (keep single
chat.jsonl+ boundary/cursor pagination).Why:
HistoryService.findLastBoundaryByteOffset+readHistoryFromOffset).HistoryServicemutation APIs (appendToHistory,updateHistory,truncateHistory,migrateWorkspaceId) currently treat history as one atomic file.chat.jsonlpaths.~/.mux/sessions/<workspace>/chat.jsonl.If we eventually shard by compaction epoch, what must be added?
updateHistory(historySequence)and delete/truncate operations.Estimated additional product LoC beyond the current plan: ~400–700 LoC (+ substantial test churn).
Recommended approach (A): active
onChat+ boundary pagination + activity metadata feedNet LoC estimate (product code): ~260–360 LoC
1) Scope full
onChatstreaming to the active workspace onlyKeep
addWorkspace()for registration/aggregator creation, but manage exactly one full chat stream (the workspace currently displayed).Files/symbols:
src/browser/stores/WorkspaceStore.tsaddWorkspace,removeWorkspace,syncWorkspacesactiveWorkspaceId,activeOnChatWorkspaceIdsetActiveWorkspaceId,ensureActiveOnChatSubscriptionsrc/browser/contexts/WorkspaceContext.tsxworkspaceStore.setActiveWorkspaceId(currentWorkspaceId)in an effectDefensive points:
onChatsubscription.mode: "since"cursor,fullfallback) insiderunOnChatSubscription().2) Replay only from the latest compaction boundary on initial load
Switch
onChatfull replay baseline from penultimate boundary (skip=1) to latest boundary (skip=0).Files/symbols:
src/node/services/agentSession.tsemitHistoricalEvents()history load call + commentssrc/browser/utils/messages/StreamingMessageAggregator.tsThis keeps live behavior aligned with fresh loads: once a new boundary arrives, older epochs are pruned by default.
3) Add explicit “Load More history” API with cursor pagination
Expose a non-stream endpoint that pages older compaction epochs via a stable cursor (not page index).
Files/symbols:
src/common/orpc/schemas/api.tssrc/node/orpc/router.tssrc/node/services/workspaceService.tssrc/node/services/historyService.tsRecommended request/response shape:
Why cursor (vs skip index):
4) Implement Load More UX in chat transcript
Add a top-of-transcript control that prepends exactly one older boundary window per click.
Files/symbols:
src/browser/stores/WorkspaceStore.ts{ nextCursor, hasOlder, loading }loadOlderHistory(workspaceId)src/browser/components/ChatPane.tsxImplementation note: if
appendcannot safely preserve strict chronological order for prepends, add a dedicatedprependHistoricalMessages()path in the aggregator and assert sequence monotonicity after merge.5) Use metadata-only activity feed for non-active workspace status
Leverage existing
workspace.activity.list/subscribeas the metadata channel for unread + streaming indicators.Files/symbols:
src/browser/stores/WorkspaceStore.tsrunActivitySubscription()getWorkspaceState()/ sidebar derivationsrc/browser/contexts/WorkspaceContext.tsxThis preserves sidebar responsiveness (unread dot, streaming state, model tooltip) without paying transcript-stream costs for every workspace.
6) Tests to update/add
Files:
src/node/services/agentSession*.test.ts(or add targeted replay test)src/browser/utils/messages/StreamingMessageAggregator.test.tssrc/browser/stores/WorkspaceStore.test.tssrc/browser/components/ChatPane*.test.tsx(or nearby transcript tests)Test cases:
skip=0) and emits expectedcaught-upmetadata.loadOlderHistory()advances cursor page-by-page, prepending one boundary window per click untilnextCursor=null/hasOlder=false.onChatstreams.onChatsubscription and keepssincereconnect behavior intact.Why this approach over sequential full subscriptions?
Sequential full subscriptions reduce startup spikes but still replay transcript data for every workspace. Using one full stream (active workspace) plus metadata-only activity for the rest cuts both startup and steady-state bandwidth much more aggressively while keeping sidebar signal quality.
Validation plan
bun run test src/node/services/historyService.test.tsbun run test src/node/services/agentSession*.test.ts(targeted replay-focused cases)bun run test src/browser/utils/messages/StreamingMessageAggregator.test.tsbun run test src/browser/stores/WorkspaceStore.test.tsbun run test src/browser/components/Messages/MessageRenderer.test.tsxand/orChatPanetests (if Load More UI is touched there)make typecheckGenerated with
mux• Model:anthropic:claude-opus-4-6• Thinking:xhigh• Cost:$0.95