Repository navigation
perf(agents): parse activity records once per snapshot - #1449
Conversation
requestwork runs in per-row activity selectors on every observer frame. each call re-parsed every retained record of the channel's agents, re-stringified each matching batch child and parsed it again, so the cost scaled with loaded rows times retained records. snapshot record arrays are immutable, so cache the parsed agent/channel projection in a weakmap keyed by the record array. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Kalvin Chau <[email protected]>
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, integrating independent Rocket (systems/performance), Mantis (correctness/security), and Groot (product/accessibility) reviews.
No actionable findings in the two-file change. Source assessment: 9/10 for minimalness, elegance, and correctness.
Reviewed head e4538a66130f9f18128aa4ff509fd0719a339982 against base/merge-base 9366357512d18b5b43c6b86d401a0a9cf8ee4eb9.
The cache matches the current production ownership contract: activity records are frozen, and admission, trimming, reset and access changes replace the records array. Per-request selection and lifecycle classification remain fresh. I traced the shared projection through requestWork, the message accessory and thread indicators, including error/stop/dismiss and focus-removal paths; I found no introduced behavioral regression. The new test covers reuse across requests and invalidation on a new array. Agent/channel separation remains supported by observerFrame’s fixed-hex agent validation and the existing channel filtering.
The public PR description, both attached performance charts, and feature commit metadata were inspected; no disclosure finding.
Validation limits: source-only review, with no tests, builds, app launches or benchmark execution. The PR’s packaged-app measurements and test/CI results are author-reported evidence, not independently reproduced or verified here. Runtime performance, heap/GC behavior, rendered accessibility/focus and human acceptance remain unverified by this review. This COMMENT is not approval or merge authorization.
…r-emoji-size * origin/main: (26 commits) perf(profiles): notify once per batch of owner attestations (#1650) feat(pi): load Pi durable extensions and providers from Pi packages (#1610) Teach agents memory links and source metadata; frame memory as information (#1562) Make avatar choices visible in agent creation (#1631) fix(pi): take plugin wakes instead of ignoring them (#1612) perf(agents): parse activity records once per snapshot (#1449) perf(terminal): parse hidden terminal output in slices (#1459) perf(composer): reuse the document projection for editor reads (#1463) perf(read-state): load peer journals without a strict writeback (#1448) perf(channels): keep openLink stable across tab changes (#1525) fix: name the Cargo package Buzz so the dev build shows "Buzz" (#789) test(browser): re-type a dropped page name in the search palette (#1635) test(browser): fix two main CI timing failures (#1567) feat(agents): deliver Attention watches and timers to conversations (#1578) fix(inbox): stop listing tag-only composer state as drafts (#1531) Choose Builderlab agent models at creation and in Settings (#1533) feat(communities): sync known communities through Builderlab accounts (#644) Tag the last-tagged agent when a Solo chat composer is shown empty (#1615) perf(usage): read Claude usage once, then only what changed (#1586) feat(chats): ask to share access when sending a chat link into a DM (#1614) ...
Problem
requestWorkruns inside per-message-row activity selectors:ActivityAccessory'shasWorkandMessageRow'suseThreadAgents. The agent activity store notifies subscribers synchronously on every admitted observer frame, about once per agent per second. EachrequestWorkcall reachesrequestActivity, which runs three passes over the agent's data:activityRecordsJSON.parses every retained record of each agent active in the channel.stringifys each matching batch child.Both selectors return primitives, so React bails out, but only after that work runs. The cost therefore scales with loaded rows × retained records × frame rate. It only applies while the viewed channel contains an active agent's records. Because the tree stays mounted, it also continues while the window is hidden.
Change
Snapshot record arrays are frozen and replaced whenever records change.
requestActivitynow caches the parsed agent/channel projection in aWeakMapkeyed by that array, so all rows share one parse per snapshot. Request matching, turn selection and terminal classification are unchanged and still run per call. The cache is released together with the snapshot.Evidence
Setup
tauri build --bundles app) on a disposable macOS 26.6 VM (6 vCPU), with an owned local relay and a generated identity.proc_pid_rusagedeltas, covering the native host, the WebKit WebContent, GPU and Networking processes, and WindowServer.Reconciliation
Attribution
requestWorkshort-circuited.requestWorkablation moved foreground cost.A/B
WebContent CPU, as % of one core (means):
Validation
src/bundled/agent-activity/request-activity.test.ts: a new case asserts that repeated requests against one snapshot parse no further records, and that a newer record array is not served stale. Without the change it fails (12 parses vs 4); with the change it passes.pnpm typecheck,design:typecheckanddesign:checkpass.biome checkpasses on changed files.vitest runwas not green: 30 of 10,922 tests timed out at 5 s while the machine was under parallel load. All 14 affected files pass when rerun, but that rerun does not establish a full pass, so hosted CI is the package receipt. Hosted CI is green. One WebKit browser-journey shard failed once inagent-search-chat.spec.mjs(agent search choices, a path that does not touch activity parsing) and passed on rerun. Locally the test passes 22/22 under--repeat-each.turn_startedcarryingtriggeringEventIds, 20 s of activity, thenturn_completed). In both builds the request row shows the working accessory during the turn and clears it after completion. An earlier, completed request is not re-associated.