fix(session): freeze hidden session screens to stop cumulative slowdown - #386
Open
RonenMars wants to merge 89 commits into
Open
fix(session): freeze hidden session screens to stop cumulative slowdown#386RonenMars wants to merge 89 commits into
RonenMars wants to merge 89 commits into
Conversation
Add typed cache integrity alert contracts across REST, WebSocket, and per-server Zustand state. Sync pending alerts through websocket broadcasts and foreground-aware REST queries so clients converge after reconnects or missed unicasts. Present low-severity alerts in a banner and auto-open high-severity alerts in a localized resolution modal. Support prune-all, prune-selected, ignore, and reset-rescan actions; require confirmation for destructive choices; refetch on fingerprint conflicts; and surface backup paths on success. Cover store lifecycle, websocket dispatch, banner and modal interactions, locale parity, backward-compatible 404 handling, successful resolution, and fingerprint conflicts with unit and mocked-transport integration tests.
Mark Features 35/36 and Bugs 5/16 as in-flight; freeze the 2026-07-18 analysis as a snapshot with an updated release verdict. Co-authored-by: Cursor <cursoragent@cursor.com> [skip-ci]
Discard empty PTYs the user never typed into so they do not linger in the hub after abandoning a new session from browse. Co-authored-by: Cursor <cursoragent@cursor.com>
Add typed cache integrity alert contracts across REST, WebSocket, and per-server Zustand state. Sync pending alerts through websocket broadcasts and foreground-aware REST queries so clients converge after reconnects or missed unicasts. Present low-severity alerts in a banner and auto-open high-severity alerts in a localized resolution modal. Support prune-all, prune-selected, ignore, and reset-rescan actions; require confirmation for destructive choices; refetch on fingerprint conflicts; and surface backup paths on success. Cover store lifecycle, websocket dispatch, banner and modal interactions, locale parity, backward-compatible 404 handling, successful resolution, and fingerprint conflicts with unit and mocked-transport integration tests.
Explicit Help & Feedback submissions self-init Sentry like reportOneShot, show delivery method in the success UI, and keep standing reporting opt-in. Co-authored-by: Cursor <cursoragent@cursor.com>
Legacy uploads with spaces in filenames broke Claude Code path parsing; escape spaces so multi-attachment sends remain valid single-token refs. Co-authored-by: Cursor <cursoragent@cursor.com>
…orktree-merge-open-prs # Conflicts: # __tests__/unit/components/servers/CacheAlertModal.test.tsx # components/servers/CacheAlertModal.tsx # services/api-client.ts # services/query-client.ts # stores/servers.ts # types/api.ts
…orktree-merge-open-prs
…worktree-merge-open-prs
…to worktree-merge-open-prs
…19' into worktree-merge-open-prs [skip-ci] [skip-ci]
Adds missing viaSentry/viaEmail keys to feedback.json for ar, he, and ru. The English keys were added in this branch but the other locales were never updated, which i18n-completeness.test.ts catches.
Bumps the npm_and_yarn group with 1 update in the / directory: [shell-quote](https://github.com/ljharb/shell-quote). Updates `shell-quote` from 1.8.4 to 1.10.0 - [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md) - [Commits](ljharb/shell-quote@v1.8.4...v1.10.0) --- updated-dependencies: - dependency-name: shell-quote dependency-version: 1.10.0 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com>
The conversation screen now polls its delta drain while focused and consumes streamed conversation_events, so an externally launched session reads as a live transcript without resuming it. Resuming a conversation that may still be open elsewhere shows a confirmation naming what the server detected, and offers Take over only when a real process was matched. External sessions route to the read-only conversation view instead of the PTY screen, and the discovered-session overtake path is gated off for them. Session rows show a distinct external-alive indicator derived from the additive liveness fields, falling back to pid for older servers.
Pass Sentry credentials through to the Ship iOS workflow step so the source-map upload in the Xcode bundle phase can authenticate.
Automated version bump after successful TestFlight upload (build 165). Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
…search (#351) ## What Adds a "Rollup / Snapshot Tag Naming" section to `CLAUDE.md` documenting the `test-<env>/v<version>-<sha>-<date>` convention for throwaway pre-merge verification snapshots across the `dev` / `pre-release` / `production` environments. Updates the deploy workflow's `deploy_ref` input description and both non-main warning messages to state that a tag or commit SHA is accepted, not just a branch. Adds a research note under `docs/research/` comparing the common industry best practices for building one combined verification artifact from several open PRs, ranked by relevance to this repo. ## Why We routinely combine several open PRs into one build to verify them together before merging each individually, and the naming and deploy story for that snapshot was undocumented. The research note records the patterns behind the choice — immutable snapshot tag, build-once/promote-many with a release-candidate identifier, and Expo per-PR update channels — so the convention has a rationale on record. ## Notes `deploy_ref` already accepted tags via `actions/checkout`, so this is a wording clarification only with no change to workflow behavior. This branch bundles two commits: the research note and the convention change.
The conversation screen now polls its delta drain while focused and consumes streamed conversation_events, so an externally launched session reads as a live transcript without resuming it. Resuming a conversation that may still be open elsewhere shows a confirmation naming what the server detected, and offers Take over only when a real process was matched. External sessions route to the read-only conversation view instead of the PTY screen, and the discovered-session overtake path is gated off for them. Session rows show a distinct external-alive indicator derived from the additive liveness fields, falling back to pid for older servers.
Adds a dead-key test asserting every English key is referenced somewhere in the source, complementing the existing key-parity gate in i18n-completeness.test.ts.
The scanner reads `t('ns:key')` and bare `t('key')` calls, keeps the static prefix of dynamic keys as a prefix match, and picks up keys hoisted into consts before reaching t().
Arrays are treated as leaves since they are fetched whole via `returnObjects: true`.
Removes the 161 keys it flagged across all four locales — 654 removals, since ar and ru carry extra CLDR plural forms.
Every flagged key was verified against a raw grep to rule out false positives.
Repoints the plural and interpolation assertions in i18n.test.ts onto keys the app actually uses, since the previous fixtures were among the dead keys.
Both server remove confirmations built their message from a hardcoded English template literal, so the dialog body stayed in English for he, ru, and ar users while the surrounding title and buttons translated.
The servers:dialog.removeMessage key already existed but had gone unreferenced, which is how the hardcoding went unnoticed.
Points both call sites at the key and adds a {{server}} placeholder to it, since the previous wording was generic while the hardcoded string named the server.
The English output is unchanged; the other three locales are rephrased to match.
Mark in-flight and shipped items from the July launch batch, reconcile stale Open rows, and set the next mobile actions (typecheck, Maestro, onboarding). Co-authored-by: Cursor <cursoragent@cursor.com> [skip-ci]
Unblocks `voice_dictation` by granting speech-recognition and microphone via simctl privacy in ensure-release-build.js. Co-authored-by: Cursor <cursoragent@cursor.com>
Guard Skip/swipe so Welcome cannot dump into an empty Hub, advance immediately after QR exchange, add back-to-choose, and fix Mac-centric copy / default port 8766. Co-authored-by: Cursor <cursoragent@cursor.com>
Attribute browse failures to setup/XCUITest flake rather than FilterSortSheet, add layout/close testIDs, and make feat1 switch Classic→Tree before drilling. Co-authored-by: Cursor <cursoragent@cursor.com>
…to worktree-merge-open-prs # Conflicts: # jest.setup.js
…ge' into worktree-merge-open-prs
…o worktree-merge-open-prs
…kip-ci] [skip-ci]
…to worktree-merge-open-prs
… [skip-ci] [skip-ci]
…llow-up chain [skip-ci]
The API returns a JSONL-derived conversation name, but mobile discarded it: the list and detail adapters hardcoded title from project_name, and the live-session header never read the session's name field. Wire session_name through the Raw/Session types and both adapters, and add it to the live-view and session-row fallback chains (user rename -> session name -> project name).
…live pause toggle Keep the transcript on screen during a live reload instead of blanking. Every delta drain rebuilt ConversationDetail from raw pages, giving each Message a fresh object identity even when byte-identical, so FlashList (startRenderingFromBottom) dropped and re-measured every cell — a blank "blink". reuseMessageIdentities reuses the prior object for any unchanged id, so existing rows keep stable references and only genuine appends read as new. Fade newly-arrived tail messages in from the bottom with Reanimated FadeInDown, gated so only live appends animate — cold-open history and older-page backfill do not. Add a live indicator and pause/resume toggle to the read-only conversation view. Pausing stops the freshness poll and conversation_updated drains and freezes the rendered snapshot; resuming runs one catch-up drain and re-enables live updates. The control appears only while the session is actually live.
…live pause toggle
Reuse search-target + match navigation so users can search the open conversation without going through hub-wide list search. Co-authored-by: Cursor <cursoragent@cursor.com>
## Summary Live session screen messages could appear missing on arrival until a tiny (5-15px) manual scroll revealed them. Root cause: `LiveConversationView` drove scroll with JS `scrollToEnd()` effects that raced FlashList's cell-layout pass for the newly appended row, landing short. Fix: adopt FlashList's native `maintainVisibleContentPosition` bottom-anchoring (same config already used by `ConversationHistoryList`) and drop the racing scroll effects. ## Test plan - [x] `npx tsc --noEmit` — no new errors - [x] `npx eslint components/conversation/LiveConversationView.tsx` — clean - [ ] Manual verification on device/simulator: send messages in a live session, confirm no scroll-to-reveal needed
## Summary Messages with a tool/diff/thinking block plus a text block showed a visibly larger gap to adjacent rows than plain text-to-text messages. Root cause: `MessageBubble`'s own `marginVertical` stacked with `MessageItem`'s `toolContainer` margin when the bubble was nested inside it (the tool/diff + text branch), doubling the effective spacing there. Fix: added a `noOuterMargin` prop to `MessageBubble`, passed from the nested usage in `MessageItem`, so spacing in that branch comes only from `toolContainer`. ## Test plan - [x] `npx tsc --noEmit` — no new errors - [x] `npx eslint components/conversation/MessageBubble.tsx components/conversation/MessageItem.tsx` — clean - [ ] Manual verification: view a conversation with a tool-call + prose message adjacent to plain text messages, confirm consistent spacing
A session parked on a question/permission card replays only blank ring-buffer rows over the WS terminal_replay. The handler treated any replay as a successful load, latching replayReceivedRef and clearing the 2s HTTP fallback timer, so the terminal stayed blank forever even though GET /api/sessions/:id/output still held the full transcript. Only accept a replay that carries at least one non-blank line; otherwise leave the fallback timer armed so /output fills the screen.
Sessions open via router.push and the native stack keeps pushed-under screens mounted, so each opened session left its WS handlers firing on every frame and its VirtualTerminal buffer growing. The app slowed down linearly with the number of sessions opened in one run (noticeable after 5-6). freezeOnBlur suspends a hidden screen's render and effects until it's focused again.
RonenMars
added a commit
that referenced
this pull request
Jul 23, 2026
RonenMars
added a commit
that referenced
this pull request
Jul 24, 2026
RonenMars
added a commit
that referenced
this pull request
Jul 26, 2026
RonenMars
added a commit
that referenced
this pull request
Jul 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The app slows down progressively after opening 5-6 sessions in one run.
Sessions open with
router.push, and the native session Stack (app/session/_layout.tsx) had nofreezeOnBlur.In a native stack a pushed-under screen stays mounted, so every opened session kept its
useTerminalStreamWS handlers firing on every frame (including the'*'wildcard) and itsVirtualTerminalbuffer growing.Both scale linearly with the number of sessions opened, which matches the "slow after 5-6 sessions" report.
Fix
Add
freezeOnBlur: trueto the session Stack so a hidden screen's render and effects are suspended until it is focused again.Compatibility
Mobile-only, native-navigation config flag.
No server, API, or WS-contract changes.
Note on this PR's diff
This branch is cut from the integration branch, so the diff against
maincarries the in-flight integration history plus this fix.It is stacked ahead of #(cap-virtualterminal-scrollback) and is intended to merge first, in integration-branch order.