Skip to content

fix(vscode): stop transcript re-render loop on session switch - #14534

Merged
marius-kilocode merged 1 commit into
mainfrom
investigate-worktree-switch-performance
Sep 24, 2026
Merged

marius-kilocode merged 1 commit into
mainfrom
investigate-worktree-switch-performance

Conversation

@marius-kilocode

Copy link
Copy Markdown
Collaborator

What Problem This Solves

Switching to some Agent Manager sessions never finished loading. The transcript kept mounting and unmounting a row every frame, which burned CPU and made worktree and session switching feel slow. It reproduced after a restart, because it depends on the session content rather than on warm state.

Why This Change Was Made

The transcript virtual list sizes its container from cached row heights and places rows absolutely inside it. An expanded edit diff can mount taller than its cached height (measured: 577 px against a cached 34 px). The extra height overflowed the container and grew the scroll height. The bottom pin then scrolled that row out of range, so it was removed before Virtua measured it. The scroll height shrank, the row came back into range, and this repeated about 60 times per second. In one capture the loop used about 43% of the webview main thread and created 131k DOM nodes in 3.35 s.

The Virtua container now clips only vertical overflow (overflow: visible clip). Only measured heights set the scroll height, so the row is measured on the next frame and the view settles. Horizontal overflow stays visible for the row hover bleed, and sticky content keeps working because clip does not create a scroll container.

Changes:

  • MessageList.tsx: a VirtualBox container passed to Virtualizer as as={VirtualBox}.
  • chat-layout.css: the vertical clip rule.
  • tests/unit/transcript-virtual-clip.test.ts: a source contract test. The unit test DOM has no layout engine, so it cannot reproduce the loop itself.
  • .changeset/transcript-virtual-clip.md.

User Impact

Sessions that used to loop now settle. Normal switch times are unchanged, and the fix also protects against the same feedback loop from other causes of a stale cached row height.

Evidence

Measured in an isolated VS Code instance on a copy of real session data, before and after the change. The loop fixture is a session whose turn ends with several expanded edit diffs. Median over the benchmark runs.

Scenario Before After
Endless loop on cold switch (edits expanded) 3 of 3 runs, plus the full benchmark 0 of 3 runs, 0 in the full benchmark
Cold switch: first paint / settled 74 / 273 ms 74 / 280 ms
Warm switch: first paint / settled 32 / 193 ms 33 / 194 ms
Edits collapsed: cold / warm first paint 66 / 30 ms 70 / 33 ms

Expanded edit diffs still render fully after the clip change:

Transcript with expanded edit diffs rendering fully after the clip change

Validation

  • Typecheck, lint, knip and the kilocode_change check pass.
  • Unit tests: 6383 passed, 2 skipped, 0 failed.
  • Manual checks in the isolated VS Code: expanded diffs render without bottom clipping, collapsing and expanding an edit card keeps the bottom pin stable, scrolling up loads older messages without overlapping rows, and returning to the bottom works.

The transcript virtual list sizes its container from cached row heights and
places rows absolutely. An expanded edit diff can mount taller than its cached
height, overflow the container, and grow the scroll height. The bottom pin then
scrolls that row out of range before Virtua measures it, so the row unmounts
unmeasured and the transcript mounts and unmounts it every frame.

Clip only vertical overflow on the Virtua container so the scroll height comes
from measured sizes. Rows keep their horizontal bleed and sticky content keeps
working because clip does not create a scroll container.
@kilo-code-bot

kilo-code-bot Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Reviewed the transcript virtual-clip fix at HEAD 4bbed242. The VirtualBox container forwards ref/style/children exactly as Virtua's as contract expects (stable module-level component reference, so no remount/reactivity churn), the [data-slot="message-list-virtual"] { overflow: visible clip } rule intentionally keeps overflow-x: visible (a visible+clip pair is the one combination that does not compute to auto), and no new subscriptions, observers, or cleanup paths are introduced. No memory-leak or correctness concerns found.

Files Reviewed (4 files)
  • .changeset/transcript-virtual-clip.md
  • packages/kilo-vscode/tests/unit/transcript-virtual-clip.test.ts
  • packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx
  • packages/kilo-vscode/webview-ui/src/styles/chat-layout.css

Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

@WebReflection WebReflection left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure we should fix the underlying issue but this change makes sense as it is

@marius-kilocode
marius-kilocode merged commit 47fd656 into main Sep 24, 2026
25 checks passed
@marius-kilocode
marius-kilocode deleted the investigate-worktree-switch-performance branch September 24, 2026 10:59
iscekic pushed a commit that referenced this pull request Sep 26, 2026
…4554)

## Automated docs sync — 2026-09-25

This PR keeps kilo.ai/docs in sync with features merged to [Kilo-Org/cloud](https://github.com/Kilo-Org/cloud) and [Kilo-Org/kilocode](https://github.com/Kilo-Org/kilocode). Every change below links to the merged PR it documents.

- Window: `2026-09-24T07:08:33.639Z` → `2026-09-25T07:05:29.302Z`
- Verification (docs build + tests): **passing**

### Surface: `cloud-mobile`

- Assignees / requested reviewers: @iscekic and @eshurakov
- Derivation: Derived from the repository layout. A product surface is a package under packages/ that ships a distinct client, plugin, backend, or hosted service: cli = packages/opencode/ + packages/tui/ + packages/server/ + packages/sdk/ + packages/plugin/; vscode = packages/kilo-vscode/ + packages/kilo-web-ui/ + packages/kilo-ui/; jetbrains = packages/kilo-jetbrains/; gateway = packages/kilo-gateway/; web = packages/kilo-console/ + packages/kilo-indexing/ + packages/kilo-memory/ + packages/kilo-sandbox/. Docs route from the IA tree packages/kilo-docs/pages/ plus docs/jetbrains-vscode-settings-parity.md: each surface lists the pages sections that document it, and the per-platform pages under packages/kilo-docs/pages/code-with-ai/platforms/ map to the matching extension surface (the vscode/ directory to vscode, jetbrains.md to jetbrains). A doc path belongs to the surface with the longest matching prefix; a path that matches none of those prefixes falls to `other` (the explicit other prefixes are listed under other.docs). The cloud surfaces are derived the same way from the Kilo-Org/cloud layout: cloud-mobile = apps/mobile/, cloud-web = apps/web/, cloud-extension = apps/extension/, and cloud-agent = the cloud-agent packages under packages/ (packages/cloud-agent-sdk/ + packages/cloud-agent-profile/). A cloud source names its repository while a bare string still means this repository. The pages under packages/kilo-docs/pages/collaborate/ document the cloud web app (app.kilo.ai: teams dashboard, billing, SSO, adoption dashboard), so they route to cloud-web. No page under packages/kilo-docs/pages/ documents the browser side-panel extension yet, so cloud-extension lists no docs prefix.
- Map: `.github/docs-sync/surfaces.json`
- Surface map: `cli`, `vscode`, `jetbrains`, `gateway`, `web`, `cloud-mobile`, `cloud-web`, `cloud-extension`, `cloud-agent`, `other`
- Source prefixes: `apps/mobile/` (Kilo-Org/cloud)
- Doc prefixes: `packages/kilo-docs/pages/code-with-ai/platforms/mobile.md`
- Paths that fall to `other`: `packages/kilo-docs/pages/community/`, `packages/kilo-docs/pages/kiloclaw/`, `packages/kilo-docs/pages/contributing/`, `packages/kilo-docs/LEARNINGS.md`, `docs/`
- Reviewers are ranked from `Kilo-Org/cloud`; the workflow needs a token with `contents: read` on that repository (repository secret `CROSS_REPO_ACCESS_TOKEN`, exposed to the upsert step as `CLOUD_REPO_TOKEN`).
- How the two were computed: Reviewers for `cloud-mobile` are ranked from `Kilo-Org/cloud` git history over `apps/mobile/` (a commit 180 days old counts half as much, half-life 180 days). Bots (author type "Bot" or a login matching /\[bot\]$/i) and people without admin, write, or maintain permission are excluded.

### Changes

<!-- docs-sync:changes:start -->
| Docs change | Source |
| --- | --- |
| updated pages/code-with-ai/platforms/mobile.md | [Kilo-Org/cloud#6386](Kilo-Org/cloud#6386) |
| updated pages/ai-providers/openai-chatgpt-plus-pro.md | [Kilo-Org/cloud#6702](Kilo-Org/cloud#6702) |
| updated pages/code-with-ai/platforms/cloud-agent.md | [Kilo-Org/cloud#6683](Kilo-Org/cloud#6683) |
| updated pages/getting-started/byok.md | [Kilo-Org/cloud#6692](Kilo-Org/cloud#6692) |
<!-- docs-sync:changes:end -->

### Pending — will retry

<!-- docs-sync:pending:start -->
_None._
<!-- docs-sync:pending:end -->

### Considered, no docs change needed

<!-- docs-sync:skipped:start -->
| PR | Reason |
| --- | --- |
| [Kilo-Org/cloud#6658](Kilo-Org/cloud#6658) | Internal sandbox lifecycle fix with no user-visible workflow or setting. |
| [Kilo-Org/cloud#6673](Kilo-Org/cloud#6673) | Internal container CA trust plumbing, no user-facing behavior. |
| [Kilo-Org/cloud#6672](Kilo-Org/cloud#6672) | Internal sandbox launch/recovery fix with no documented workflow change. |
| [Kilo-Org/cloud#6660](Kilo-Org/cloud#6660) | Internal cloud-agent queue delivery fix; no new command, setting, or workflow for users. |
| [Kilo-Org/cloud#6226](Kilo-Org/cloud#6226) | Internal gateway alias-routing change, not user-visible. |
| [#14490](#14490) | Tool-call animation and streaming UI polish; users do not need to learn a new workflow. |
| [#14530](#14530) | Bug fix restoring intended worktree-pool behavior, no doc change needed. |
| [#14529](#14529) | Bug fix restoring tab/panel state across project switches. |
| [#14531](#14531) | Reconnect recovery bug fix, restores already-documented behavior. |
| [#14532](#14532) | Bug fix keeping session tab title in sync on rename. |
| [Kilo-Org/cloud#6088](Kilo-Org/cloud#6088) | Removes internal/admin model-experiment surfaces, not public product docs. |
| [Kilo-Org/cloud#6682](Kilo-Org/cloud#6682) | Internal control-socket reconnect race fix, no user-facing change. |
| [#14534](#14534) | Transcript re-render performance bug fix. |
| [#14535](#14535) | Bug fix preserving the loaded browser page across context switches. |
| [Kilo-Org/cloud#6684](Kilo-Org/cloud#6684) | Reverted by Kilo-Org/cloud#6685. |
| [Kilo-Org/cloud#6678](Kilo-Org/cloud#6678) | Dead-code constant removal, no user-visible effect. |
| [Kilo-Org/cloud#6687](Kilo-Org/cloud#6687) | Removes internal model-experiment maintenance and retains tables, no user-facing change. |
| [#14515](#14515) | JetBrains plugin unload crash fix, no documented behavior change. |
| [#14520](#14520) | JetBrains transcript/list rendering performance work. |
| [Kilo-Org/cloud#6614](Kilo-Org/cloud#6614) | Mobile PR Review header and session title bug fix, no doc change needed. |
| [Kilo-Org/cloud#6625](Kilo-Org/cloud#6625) | Internal mobile secure-store error-handling refactor. |
| [Kilo-Org/cloud#6624](Kilo-Org/cloud#6624) | Mobile auth bug fix that stops a retry loop; restores expected sign-in behavior with no new setting or workflow. |
| [#14310](#14310) | Contributor/CI fix making the kilo-v2 checkout installable; not user-visible product behavior. |
| [Kilo-Org/cloud#6611](Kilo-Org/cloud#6611) | Mobile notification-tap fix that selects the session's organization; restores correct behavior rather than adding a learnable feature. |
| [Kilo-Org/cloud#6644](Kilo-Org/cloud#6644) | Mobile sign-in layout/alignment polish; no change to what a user must do. |
| [Kilo-Org/cloud#6601](Kilo-Org/cloud#6601) | Mobile layout fix keeping empty states clear of the tab bar; purely visual. |
| [#14543](#14543) | CI/release infrastructure adding Windows binary code signing; no public docs impact. |
| [Kilo-Org/cloud#6616](Kilo-Org/cloud#6616) | Mobile visual defect fixes and a session-title fallback; no new user workflow or setting. |
| [Kilo-Org/cloud#6630](Kilo-Org/cloud#6630) | Reports an edge-case partial worktree restore; failure-path plumbing with no new user-facing workflow, target setting, or config. |
| [Kilo-Org/cloud#6699](Kilo-Org/cloud#6699) | Cloud Agent e2e stabilization plus internal idle-sandbox capacity handling; not user-visible. |
| [#14545](#14545) | Automated JetBrains release/changelog PR; underlying user-facing changes are triaged from their own PRs. |
| [Kilo-Org/cloud#6708](Kilo-Org/cloud#6708) | Internal AI-gateway request-logging policy change in the admin panel; no existing public docs surface and no change to how users run Kilo Code. |
| [#14533](#14533) | Documentation already shipped with the merged PR. The experimental.task_model_selection flag is gone from the current source, and pages/code-with-ai/agents/model-selection.md, pages/code-with-ai/agents/context-mentions.md, and pages/getting-started/settings/index.md already describe per-task selection as default-on with no stale experiment references. |
| [#14510](#14510) | Documentation already shipped with the merged PR. Marketplace companion-skill support is present in the current source (packages/opencode/src/kilocode/marketplace/companions.ts and installer), and pages/customize/marketplace.md already documents installing, publishing, and removing MCP servers with companion skills. |
<!-- docs-sync:skipped:end -->

---

(bot) Generated by the docs-sync workflow. Humans review and merge; while this PR stays open, the next daily run appends new changes here. Branch: `docs/auto-sync-2026-09-25`.
<!-- docs-sync: processed-through 2026-09-25T07:05:29.302Z -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Sponsor
SponsoredKunjungi sekarang
Promo