Skip to content

perf(renderer): isolate pane runtime updates from workspace layout - #810

Merged
Juliusolsson05 merged 5 commits into
mainfrom
perf/pane-runtime-isolation
Sep 6, 2026
Merged

perf(renderer): isolate pane runtime updates from workspace layout#810
Juliusolsson05 merged 5 commits into
mainfrom
perf/pane-runtime-isolation

Conversation

@Juliusolsson05

@Juliusolsson05 Juliusolsson05 commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Problem

Every runtime-map replacement rerenders the workspace controller and traverses unrelated panes; draft edits also increment invisible App state for autosave.

Implementation

  • Move runtime observation and draft-save invalidation below the controller.
  • Keep command/IPC runtime refs synchronous through a cleaned-up store subscription; layout action closures still refresh normally.
  • Add subscribed memoized leaf boundaries before provider/view selection, plus explicit Reader and status-header subscriptions.
  • Keep broad inspection context consumers reactive rather than risk stale UI.
  • Preserve late hidden-picker/lease cleanup and lifecycle layout-effect chronology.
  • No backend, readiness, screen transport, dependency, persisted schema or migration change.

Evidence

Real controller/store/tile test with provider paint and external ingress mocked: 100 single-session updates produce 100 controller and 100 unrelated-pane renders with the old subscription restored as a control, versus zero of either in the optimized path. The affected pane renders 100 times in both. This is render-count evidence, not an app-wide CPU claim.

Six new regressions also cover fresh draft reads before commit, debounced saving, clear/undo, leaf session replacement, focus actions, hidden-mode lease cleanup, and exactly-once lifecycle publication before passive visibility with original run attribution.

Checks

Final full renderer run on Node 24 (two workers) on the merged head 1c79980 (this branch + current main incl. external operator toolkit #812 and MCP tool policy #818): 123 files / 523 tests passed (forced typecheck clean), including the three PaneHeader phone-coupling regressions and main's new control feature tests. Earlier runs recorded: 115 files / 499 tests before the final isolation cases, 116 files / 505 tests on review-fix head c8126f4, then 501 with one timeout in the unchanged lazy-prose dynamic-import test (existing #700) under increased host load; no assertions, timeouts, or retry policy weakened.

The merged-tree quality-gate caught two type errors from main-added control tests that called the pre-isolation hook signatures (the stale worktree base could not surface them); both were adapted, no production code changed. Local Node 25 tests use --no-experimental-webstorage so happy-dom owns storage; CI uses Node 24. Updated four narrow test mocks to expose the runtime/context boundary.

Review

Two independent orchestration reviewers approved the implementation with no blockers. Review-fix head c8126f4 resolves:

  • PaneHeader's related-status store read is now optional-chained for the phone bundle, whose stub store (appStateHooks.ts) has no workspaceRuntimes key; a dedicated PaneHeader phone-coupling renderer regression pins the empty-chip phone shape, the keyless-store-with-chips shape, and the desktop store path.
  • The shared empty fallback runtime documents its never-mutate invariant; useFeedDebugPersist no longer takes an ignored render-time snapshot; the draft-version signal types honestly as () => void instead of a React state setter.

Both reviewers confirmed the merged head 1c79980 (the delta since c8126f4 is only the origin/main merge plus two test adaptations).

The one remaining runtime-derived root invalidation is the picker/lease shallow subscription in useRenderedLeaseHygiene: it fires only on user commands, is no-op guarded, and is the previous universal behavior.

Remaining work

Independent from worktree reconciliation #808; no mixed earlier experimental worktree changes or lockfile edits. Refs #763 rather than closing it until the remaining broad inspection consumers and live multi-agent acceptance are reviewed. Screen interest #762 and corrected diagnostics #767 remain subsequent increments; A6 #802-805 are separate.

Merge order after review and CI: #808 first (reconciliation), then this PR, per both reviewers.

Move runtime and draft invalidation below the controller while retaining synchronous command reads, autosave and layout-phase lifecycle observations. Subscribe leaf ownership, Reader and header status consumers explicitly; preserve late hidden-lease cleanup.

Refs #763
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Final local typecheck completed successfully. The final renderer run remains recorded as 501 passed / 1 existing lazy-prose timeout (#700), not an all-green result. Current CI has passed the minimum-node-fixture gate; quality-gate is still running. No lockfile, dependency, readiness, screen-forwarding or provider changes in this increment.

@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Owner authorized review, fixes, and merge of #808/#810 after final checks. Two independent Agent Code orchestration reviewers each cover both complete diffs and their interaction at cccae6e / a96f00a. Both current CI gates now pass. Planned order is #808, then incorporation of resulting main into #810 and fresh combined verification. No other PR is included in this merge authorization.

Harden PaneHeader's related-status store read for the phone bundle: the phone
stubs @renderer/app-state/hooks to a settings-only store with no
workspaceRuntimes key, so the bare index was safe only because SessionView
always passes an empty chip list. Optional-chain the read with a WHY comment
and pin the phone call shapes (empty chips, keyless store with chips, desktop
store path) with a dedicated renderer test.

Also adopt the remaining review items: document the never-mutate invariant on
the shared empty fallback runtime, drop the ignored render-time snapshot
parameter from useFeedDebugPersist (its timer reads current refs), and type
the draft-version signal honestly as () => void instead of a React state
setter whose updater argument was silently discarded on four call sites.

Refs #763
…signatures

origin/main advanced past the review base (external operator toolkit #812,
MCP tool policy #818) while pane-runtime-isolation was in review, and those
PRs added control tests that call the pre-isolation hook signatures. The
merged-tree quality-gate caught two TS errors that the stale worktree base
could not surface:

- control.renderer.test.tsx passed a Dispatch<SetStateAction<number>> as the
  draft-version signal, which now types as a plain () => void bump; the test
  reads drafts imperatively via inspectAgentDraft, so a stable no-op bump is
  behaviorally faithful.
- preferences.renderer.test.tsx passed the render-time runtimes snapshot that
  useWorkspaceHelpers no longer takes; the helper reads runtimes through refs
  and toggles via the setRuntimes updater, so only (setRuntimes, refs) remain.

No production code changed. Merged-tree verification: forced typecheck clean,
full renderer suite 123 files / 523 tests green on Node 24 with two workers.
@Juliusolsson05
Juliusolsson05 merged commit 408e3e3 into main Sep 6, 2026
2 checks passed
@Juliusolsson05
Juliusolsson05 deleted the perf/pane-runtime-isolation branch September 6, 2026 01:06
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.

1 participant