Skip to content

Bump react-dom from 18.3.1 to 19.2.0#1

Closed
dependabot[bot] wants to merge 1 commit intomainfrom
dependabot/npm_and_yarn/react-dom-19.2.0
Closed

Bump react-dom from 18.3.1 to 19.2.0#1
dependabot[bot] wants to merge 1 commit intomainfrom
dependabot/npm_and_yarn/react-dom-19.2.0

Conversation

@dependabot
Copy link
Copy Markdown
Contributor

@dependabot dependabot Bot commented on behalf of github Oct 21, 2025

Bumps react-dom from 18.3.1 to 19.2.0.

Release notes

Sourced from react-dom's releases.

19.2.0 (Oct 1, 2025)

Below is a list of all new features, APIs, and bug fixes.

Read the React 19.2 release post for more information.

New React Features

  • <Activity>: A new API to hide and restore the UI and internal state of its children.
  • useEffectEvent is a React Hook that lets you extract non-reactive logic into an Effect Event.
  • cacheSignal (for RSCs) lets your know when the cache() lifetime is over.
  • React Performance tracks appear on the Performance panel’s timeline in your browser developer tools

New React DOM Features

  • Added resume APIs for partial pre-rendering with Web Streams:
  • Added resume APIs for partial pre-rendering with Node Streams:
  • Updated prerender APIs to return a postponed state that can be passed to the resume APIs.

Notable changes

  • React DOM now batches suspense boundary reveals, matching the behavior of client side rendering. This change is especially noticeable when animating the reveal of Suspense boundaries e.g. with the upcoming <ViewTransition> Component. React will batch as much reveals as possible before the first paint while trying to hit popular first-contentful paint metrics.
  • Add Node Web Streams (prerender, renderToReadableStream) to server-side-rendering APIs for Node.js
  • Use underscore instead of : IDs generated by useId

All Changes

React

React DOM

... (truncated)

Changelog

Sourced from react-dom's changelog.

19.2.0 (October 1st, 2025)

Below is a list of all new features, APIs, and bug fixes.

Read the React 19.2 release post for more information.

New React Features

  • <Activity>: A new API to hide and restore the UI and internal state of its children.
  • useEffectEvent is a React Hook that lets you extract non-reactive logic into an Effect Event.
  • cacheSignal (for RSCs) lets your know when the cache() lifetime is over.
  • React Performance tracks appear on the Performance panel’s timeline in your browser developer tools

New React DOM Features

  • Added resume APIs for partial pre-rendering with Web Streams:
  • Added resume APIs for partial pre-rendering with Node Streams:
  • Updated prerender APIs to return a postponed state that can be passed to the resume APIs.

Notable changes

  • React DOM now batches suspense boundary reveals, matching the behavior of client side rendering. This change is especially noticeable when animating the reveal of Suspense boundaries e.g. with the upcoming <ViewTransition> Component. React will batch as much reveals as possible before the first paint while trying to hit popular first-contentful paint metrics.
  • Add Node Web Streams (prerender, renderToReadableStream) to server-side-rendering APIs for Node.js
  • Use underscore instead of : IDs generated by useId

All Changes

React

React DOM

... (truncated)

Commits

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot merge will merge this PR after your CI passes on it
  • @dependabot squash and merge will squash and merge this PR after your CI passes on it
  • @dependabot cancel merge will cancel a previously requested merge and block automerging
  • @dependabot reopen will reopen this PR if it is closed
  • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

@dependabot dependabot Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code labels Oct 21, 2025
@dependabot dependabot Bot force-pushed the dependabot/npm_and_yarn/react-dom-19.2.0 branch 6 times, most recently from 6434ff2 to 8883acf Compare October 25, 2025 21:31
@vercel
Copy link
Copy Markdown

vercel Bot commented Oct 25, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
superset-website Error Error Oct 28, 2025 0:00am

@dependabot dependabot Bot force-pushed the dependabot/npm_and_yarn/react-dom-19.2.0 branch from 8883acf to 265b8fc Compare October 27, 2025 03:23
@dependabot dependabot Bot force-pushed the dependabot/npm_and_yarn/react-dom-19.2.0 branch from 265b8fc to b10d6b6 Compare October 27, 2025 03:39
@dependabot dependabot Bot force-pushed the dependabot/npm_and_yarn/react-dom-19.2.0 branch from b10d6b6 to 331f1d7 Compare October 27, 2025 03:41
@dependabot dependabot Bot force-pushed the dependabot/npm_and_yarn/react-dom-19.2.0 branch from 331f1d7 to 82ea50b Compare October 27, 2025 20:30
@dependabot dependabot Bot force-pushed the dependabot/npm_and_yarn/react-dom-19.2.0 branch from 82ea50b to cbd6f25 Compare October 27, 2025 20:53
@dependabot dependabot Bot force-pushed the dependabot/npm_and_yarn/react-dom-19.2.0 branch from cbd6f25 to 89925be Compare October 27, 2025 22:41
Bumps [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) from 18.3.1 to 19.2.0.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.0/packages/react-dom)

---
updated-dependencies:
- dependency-name: react-dom
  dependency-version: 19.2.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot force-pushed the dependabot/npm_and_yarn/react-dom-19.2.0 branch from 89925be to c4ddf19 Compare October 27, 2025 23:59
@dependabot @github
Copy link
Copy Markdown
Contributor Author

dependabot Bot commented on behalf of github Oct 28, 2025

Looks like react-dom is no longer updatable, so this is no longer needed.

@dependabot dependabot Bot closed this Oct 28, 2025
@dependabot dependabot Bot deleted the dependabot/npm_and_yarn/react-dom-19.2.0 branch October 28, 2025 04:16
andreasasprou referenced this pull request in andreasasprou/superset Dec 27, 2025
…persistence

Fixes 3 bugs in terminal session restoration:
- Bug #1: Terminal blank after attach failure - auto-reconnect with retry backoff
- Bug #2: Wrong dimensions after restore - pass actual cols/rows to attachSession
- Bug #3: Cannot type after restore - lifecycle routes writes through connected PTY

Changes:
- Add SessionLifecycle class with states: disconnected→connecting→connected→reconnecting→failed→closed
- Add TmuxError classification for intelligent retry decisions
- Add -d flag to detach other clients before attaching
- Integrate lifecycle into TerminalManager (write, resize, kill, detach, cleanup)
andreasasprou referenced this pull request in andreasasprou/superset Dec 28, 2025
…persistence

Fixes 3 bugs in terminal session restoration:
- Bug #1: Terminal blank after attach failure - auto-reconnect with retry backoff
- Bug #2: Wrong dimensions after restore - pass actual cols/rows to attachSession
- Bug #3: Cannot type after restore - lifecycle routes writes through connected PTY

Changes:
- Add SessionLifecycle class with states: disconnected→connecting→connected→reconnecting→failed→closed
- Add TmuxError classification for intelligent retry decisions
- Add -d flag to detach other clients before attaching
- Integrate lifecycle into TerminalManager (write, resize, kill, detach, cleanup)
andreasasprou referenced this pull request in andreasasprou/superset Dec 28, 2025
…persistence

Fixes 3 bugs in terminal session restoration:
- Bug #1: Terminal blank after attach failure - auto-reconnect with retry backoff
- Bug #2: Wrong dimensions after restore - pass actual cols/rows to attachSession
- Bug #3: Cannot type after restore - lifecycle routes writes through connected PTY

Changes:
- Add SessionLifecycle class with states: disconnected→connecting→connected→reconnecting→failed→closed
- Add TmuxError classification for intelligent retry decisions
- Add -d flag to detach other clients before attaching
- Integrate lifecycle into TerminalManager (write, resize, kill, detach, cleanup)
andreasasprou referenced this pull request in andreasasprou/superset Dec 28, 2025
…persistence

Fixes 3 bugs in terminal session restoration:
- Bug #1: Terminal blank after attach failure - auto-reconnect with retry backoff
- Bug #2: Wrong dimensions after restore - pass actual cols/rows to attachSession
- Bug #3: Cannot type after restore - lifecycle routes writes through connected PTY

Changes:
- Add SessionLifecycle class with states: disconnected→connecting→connected→reconnecting→failed→closed
- Add TmuxError classification for intelligent retry decisions
- Add -d flag to detach other clients before attaching
- Integrate lifecycle into TerminalManager (write, resize, kill, detach, cleanup)
Kitenite pushed a commit that referenced this pull request Feb 7, 2026
feat(desktop): support multi-folder selection in Open Project dialog
NextAlone added a commit to NextAlone/superset that referenced this pull request Apr 17, 2026
Add backend + frontend for resolving jj conflict markers in-app:

- New jj-conflicts.ts router: jjConflictList (parses `jj resolve --list`),
  jjConflictContent (reads raw file + parses regions via shared marker
  parser), jjConflictResolve (writes user's merged content)
- ConflictEditor dialog: file list on the left, editable result pane
  in the middle, side superset-sh#1 / side superset-sh#2 read-only panels flanking it, with
  "Use side superset-sh#1 / base / side superset-sh#2" buttons that splice the first
  unresolved region. Uses plain textarea (fallback from codemirror/merge).
- Conflict marker parser lives in the main process and a tiny shared
  copy in the renderer so the editor can re-scan regions as the user
  types.
- JjChangesView shows a yellow warning bar with the conflicted file
  count and a Resolve… button opening ConflictEditor.
Haknt added a commit to Haknt/superset that referenced this pull request Apr 19, 2026
…ntics

Session survival testing on 2026-04-19 revealed the daemon was still
being killed on Electron quit despite the SIGHUP no-op from PR superset-sh#1.
Log showed "Terminal host disposed" → "Forced exit after SIGTERM
shutdown timeout" → daemon restart, but no "Received SIGTERM"
message — meaning stopServer was reached via a path that didn't log
the signal.

Extending the ignore to SIGTERM (in addition to SIGHUP) prevents
macOS app-quit signal propagation from reaching shutdownOnce at all.
Intentional daemon shutdown goes through the explicit `shutdown`
RPC (calls stopServer() directly) or SIGKILL via
killDaemonFromPidFile — neither of which pass through these handlers.

Also adds signal diagnostic to Session subprocess exit log so future
debugging can distinguish natural exits (code=0) from signal-killed
(code=null signal=SIGTERM).
Spectralgo referenced this pull request in Spectralgo/spectralSet Apr 21, 2026
… trap fix (ss-fa4)

Scaffolds the Today surface per spec-today §1 wireframe: five labeled region
placeholders (Masthead + Triage / Rigs / Mail / Verdict) under the new
/today route. Data layer lands in ss-c1-today-02.

Wiring:
- Root / now redirects to /today (was /workspace).
- GastownSidebarSection gains a Today row at position 1 with a "—" count
  badge placeholder (wired in C1-today-02).

Route-guard (ss-tw0 / ss-trap-router trap fix):
On gastownEnabled=false, probe error, probe.installed=false, or
probe.townRoot=null, the page redirects (replace) to /workspace — which
itself bounces to /welcome when no workspaces exist. Replaces the
"Failed to load" dead-end.

Follows renderer-safety rule: only electronTrpc / electronTrpcClient usage,
no value imports from @spectralset/gastown-cli-client. LoC: +104 net across
4 files (under 120 cap).
Spectralgo referenced this pull request in Spectralgo/spectralSet Apr 23, 2026
…al pane-state bags (ss-llz)

Wave A #1 foundation for Gas Town pane architecture migration.
Adds four gastown-* literals to PaneType, exports GastownPaneKind,
and adds optional gastownMail/gastownConvoys/gastownAgents bags to
the Pane interface. Types only — no runtime dispatch yet.
Haknt added a commit to Haknt/superset that referenced this pull request Apr 27, 2026
…ntics

Session survival testing on 2026-04-19 revealed the daemon was still
being killed on Electron quit despite the SIGHUP no-op from PR superset-sh#1.
Log showed "Terminal host disposed" → "Forced exit after SIGTERM
shutdown timeout" → daemon restart, but no "Received SIGTERM"
message — meaning stopServer was reached via a path that didn't log
the signal.

Extending the ignore to SIGTERM (in addition to SIGHUP) prevents
macOS app-quit signal propagation from reaching shutdownOnce at all.
Intentional daemon shutdown goes through the explicit `shutdown`
RPC (calls stopServer() directly) or SIGKILL via
killDaemonFromPidFile — neither of which pass through these handlers.

Also adds signal diagnostic to Session subprocess exit log so future
debugging can distinguish natural exits (code=0) from signal-killed
(code=null signal=SIGTERM).
Kitenite added a commit that referenced this pull request May 2, 2026
Three valid review comments on the registry:

1. Idempotency check compared the wrapper object reference, not the
   collection instance (greptile P2). With the wrapper-only check, every
   call site passing a fresh `{ v2WorkspaceLocalState: ... }` literal
   would have torn down all stores even when the underlying collection
   hadn't changed. Switched to instance-level comparison; passing the
   same collection instance is now a true no-op.

2. Pre-mount addLaunchPanes panes weren't reliably persisted (greptile
   P2 + cubic P1). When `addLaunchPanes` runs before the v2WorkspaceLocal
   State row exists, the store's write-back is skipped (no row), then
   `ensureWorkspaceInSidebar` later inserts the row with EMPTY paneLayout.
   The previous code's "compare incoming row to current store" guard
   would have called `replaceState(EMPTY)` and wiped the in-memory tabs.
   New logic uses three-way reconciliation: compare incoming, store, and
   the last-synced agreement to decide direction. If the store has
   unsynced mutations vs lastSyncedSnapshot, push store→row; otherwise
   pull row→store. Added a regression test.

3. `__resetWorkspacePaneRegistryForTests` removed from the public
   barrel (greptile P2). Tests now import it directly from the
   implementation file.

The useMemo-as-side-effect concern (cubic P2 + greptile P2) is
intentional for the synchrony requirement (see expanded comment in
CollectionsProvider). Mitigation: #1 above ensures any unintended memo
recomputation receiving the same collection is a true no-op.
Kitenite added a commit that referenced this pull request May 2, 2026
Three valid review comments on the registry:

1. Idempotency check compared the wrapper object reference, not the
   collection instance (greptile P2). With the wrapper-only check, every
   call site passing a fresh `{ v2WorkspaceLocalState: ... }` literal
   would have torn down all stores even when the underlying collection
   hadn't changed. Switched to instance-level comparison; passing the
   same collection instance is now a true no-op.

2. Pre-mount addLaunchPanes panes weren't reliably persisted (greptile
   P2 + cubic P1). When `addLaunchPanes` runs before the v2WorkspaceLocal
   State row exists, the store's write-back is skipped (no row), then
   `ensureWorkspaceInSidebar` later inserts the row with EMPTY paneLayout.
   The previous code's "compare incoming row to current store" guard
   would have called `replaceState(EMPTY)` and wiped the in-memory tabs.
   New logic uses three-way reconciliation: compare incoming, store, and
   the last-synced agreement to decide direction. If the store has
   unsynced mutations vs lastSyncedSnapshot, push store→row; otherwise
   pull row→store. Added a regression test.

3. `__resetWorkspacePaneRegistryForTests` removed from the public
   barrel (greptile P2). Tests now import it directly from the
   implementation file.

The useMemo-as-side-effect concern (cubic P2 + greptile P2) is
intentional for the synchrony requirement (see expanded comment in
CollectionsProvider). Mitigation: #1 above ensures any unintended memo
recomputation receiving the same collection is a true no-op.
Kitenite added a commit that referenced this pull request May 2, 2026
* feat(desktop): workspace pane store registry (v2 PR 3)

PR 3 of the canonical workspace.create() refactor — see
plans/20260430-pane-store-registry-pr3.md.

Decouples pane state ownership from the workspace route mount. The V2
workspace pane store now lives in a module-level registry keyed by
workspaceId, rather than being created inside `useV2WorkspacePaneLayout`
on mount. This unblocks PR 4 (`workspace.create()`) returning a list of
already-started sessions that the renderer can write directly into the
store before — or without — navigating.

Files
- New: apps/desktop/src/renderer/lib/workspace-pane-registry/
  - workspace-pane-registry.ts: initWorkspacePaneRegistry(deps),
    getOrCreateWorkspacePaneStore(workspaceId), dropWorkspacePaneStore,
    test reset helper. Bidirectional sync wired at store creation:
    seeds from v2WorkspaceLocalState.paneLayout, writes back on store
    changes (when row exists), pushes external row changes into store,
    snapshot guard prevents echo.
  - addLaunchPanes.ts: takes the doc-shape launches array, dedupes by
    terminalId / chatSessionId, focuses. Attach-only — no
    initialCommand. Existing presets / pending-launch path keep using
    addTab directly.
  - 14 unit tests against an in-memory localStorage-backed collection.
- Modified: useV2WorkspacePaneLayout — drops 87 lines of useState +
  useLiveQuery + persistence effects, replaced by a single
  getOrCreateWorkspacePaneStore call. ensureWorkspaceInSidebar still
  runs on mount.
- Modified: CollectionsProvider — initializes the registry inside the
  collections useMemo (synchronously, before children render) so the
  workspace route's useState initializer sees a ready registry. Reinits
  on org switch.

Out of scope (deferred)
- The pending-row launch-adoption path (useConsumePendingLaunch) is
  unchanged. PR 5 deletes it when the new workspace modal moves onto
  workspace.create().
- TerminalPaneData.initialCommand is already optional in current code
  and TerminalPane already handles undefined; no change needed.

* fix(panes,desktop): two regressions found while testing PR3

Manual testing of the modal → new workspace → agent flow on PR3 turned
up two bugs that combined to wipe the agent's `initialCommand` before
the terminal pane could send it.

panes/Tab.tsx — add `key={pane.id}` to <Pane>:
- Without a key, React reconciles Pane instances by position. Switching
  the active tab to one whose Pane occupies the same layout slot reuses
  the previous Pane's component instance. Hooks (notably
  `useRef(paneData.initialCommand)` in TerminalPane) keep stale values
  from the prior pane, so the agent's initialCommand never reached
  terminalRuntimeRegistry.connect.
- Latent bug — didn't manifest before PR3 because the legacy
  useV2WorkspacePaneLayout's effect-based "load row → replaceState"
  timing happened to give Pane a fresh mount each time. PR3's registry
  seeds the store before the route mounts, exposing the issue.

workspace-pane-registry.ts — strengthen the row→store guard:
- Compare incoming row state to the *current* store snapshot, not the
  tracked `lastSyncedSnapshot`. Two reasons:
    1. Tanstack DB delivers existing rows as initial-state `insert`
       events on subscribe, with the post-our-write value. The old
       guard mistakenly thought these were unrelated external updates.
    2. Immer's `update(draft => { draft.paneLayout = ... })` writes do
       not preserve key insertion order, so JSON.stringify produced
       different strings for structurally equal store/row states.
- Also introduce `deepSortKeys`-based stable serialization so snapshot
  equality is structural rather than sensitive to key order.

Validated end-to-end: modal-create → agent terminal opens → "claude
…" prompt runs and Claude responds. 14/14 unit tests still pass.

* fix(desktop): drop pane store on workspace deletion

Calls dropWorkspacePaneStore(workspaceId) from
useDashboardSidebarState.removeWorkspaceFromSidebar so registry entries
don't accumulate forever when workspaces are removed. Pairs with the
existing v2WorkspaceLocalState.delete + cleanupWorkspacePaneRuntimes
calls in the same function.

* fix(desktop): address PR review on pane-store registry

Three valid review comments on the registry:

1. Idempotency check compared the wrapper object reference, not the
   collection instance (greptile P2). With the wrapper-only check, every
   call site passing a fresh `{ v2WorkspaceLocalState: ... }` literal
   would have torn down all stores even when the underlying collection
   hadn't changed. Switched to instance-level comparison; passing the
   same collection instance is now a true no-op.

2. Pre-mount addLaunchPanes panes weren't reliably persisted (greptile
   P2 + cubic P1). When `addLaunchPanes` runs before the v2WorkspaceLocal
   State row exists, the store's write-back is skipped (no row), then
   `ensureWorkspaceInSidebar` later inserts the row with EMPTY paneLayout.
   The previous code's "compare incoming row to current store" guard
   would have called `replaceState(EMPTY)` and wiped the in-memory tabs.
   New logic uses three-way reconciliation: compare incoming, store, and
   the last-synced agreement to decide direction. If the store has
   unsynced mutations vs lastSyncedSnapshot, push store→row; otherwise
   pull row→store. Added a regression test.

3. `__resetWorkspacePaneRegistryForTests` removed from the public
   barrel (greptile P2). Tests now import it directly from the
   implementation file.

The useMemo-as-side-effect concern (cubic P2 + greptile P2) is
intentional for the synchrony requirement (see expanded comment in
CollectionsProvider). Mitigation: #1 above ensures any unintended memo
recomputation receiving the same collection is a true no-op.

* chore(desktop): tighten pane-registry comments

- addLaunchPanes JSDoc: remove the now-stale 'persists when the next
  store change writes back' line. The registry's three-way
  reconciliation persists pre-mount panes the moment the row is
  inserted; no follow-up mutation needed.
- Test-only helper imports: standardize the explanatory comment to one
  short line in both test files.

* chore: format two terminal-key-event-handler files (CI unblocker)

Auto-fixable lint issues introduced in #3968 (terminal key handler
share between v1/v2). Not related to this PR's pane-registry work,
but blocks CI; folding the trivial reformat into the rebase here so
PR3 can land. Should be a no-op once main resolves it.
Kitenite added a commit that referenced this pull request May 4, 2026
…3999)

* perf(workspace-fs): cap searchIndexCache + pathTypes for worktree scaling

Both maps previously had no eviction and grew monotonically with worktree
count (searchIndexCache) and file event count (pathTypes). Adds LRU(12) +
30-min idle TTL to searchIndexCache and LRU(10k) cap to per-watcher
pathTypes. Eviction in pathTypes loses only the directory-type hint; the
next event for that path falls back to stat() (existing slow path).

Measured (cache-and-paths-memory.bench.test.ts):
- searchIndexCache @ 130 worktrees: 6.87 MB → 2.02 MB (-71%)
- pathTypes @ 20k unique paths: 8.69 MB → 2.54 MB (-71%)
- searchIndexCache cap holds at 12 entries, pathTypes at 10000

Adds findings audit + fix plan + reproduction tests + benchmarks for the
broader v2 worktree-perf investigation. Notably, the host-service
syncWorkspaceBranches 30s polling (1542 ms/tick at N=20 worktrees, real git
subprocesses) is documented and reproduced but not fixed in this commit;
follow-up PR will subscribe the pull-requests runtime to GitWatcher.onChanged.

See plans/v2-paths-worktree-perf-findings.md for the full audit and
plans/v2-paths-worktree-perf-fix-plan.md for the remaining work.

* docs: expand fix plan with handoff checklist + Fix #1 implementation notes

Adds concrete pickup steps, app.ts wiring order, concurrency notes, and a
mapping of which existing tests/benchmarks change vs stay. The next session
(or fresh agent) implementing Fix #1 should be able to read the plan
top-to-bottom and execute without re-deriving context.

* perf(host-service): event-driven pull-requests sync via GitWatcher

Replaces the unconditional 30s `syncWorkspaceBranches` polling timer with a
`GitWatcher.onChanged` subscription so idle worktrees cost ~0 git
subprocesses regardless of N. Branch / HEAD / upstream changes are picked
up at ~430 ms p50 (was up to 30 s).

Lift `GitWatcher` to a standalone instance in `app.ts` so both `EventBus`
(broadcasts to clients) and `PullRequestRuntimeManager` (event-driven
branch sync) share one watcher.

`syncWorkspaceBranches` becomes the safety-net sweep: still O(N) per call,
but cadence drops from 30 s → 5 min. Project-level PR refresh interval
also drops from 20 s → 5 min — branch changes drive their own
`refreshProject` via `syncOneWorkspace`, so the polling is only there to
catch external PR opens.

Concurrency stays safe via the existing `inFlightProjects` guard. Workspace
deletion races no-op cleanly via a fresh row lookup in `syncOneWorkspace`.

Tests updated:
- Existing scaling unit + integration tests now describe the safety-net
  sweep (still pin the O(N) per-call shape).
- New integration test wires a real `GitWatcher` + `WorkspaceFilesystemManager`
  and asserts a `git commit` in 1/5 worktrees triggers exactly 4 git ops
  on 1 worktree.
- Bench replaces "ms per polling tick" with event-to-DB-update latency
  (427 ms measured) plus the long-cadence safety-net sweep cost.

Closes Fix #1 + Fix #4 in plans/v2-paths-worktree-perf-fix-plan.md.

* fix(perf): address PR review — TTL on cache hit, sync serialization, test timeout

- search.ts: enforce idle TTL on cache hits — previously a hot key was bumped
  forever and only sibling-key misses ran the TTL sweep, so a 30+ min idle
  entry would still be served stale on next access. Now the hit path checks
  freshness and rebuilds when expired.

- pull-requests.ts: serialize syncOneWorkspace per workspaceId via
  Map<workspaceId, Promise>. GitWatcher only debounces 300 ms; two bursts
  far enough apart could run concurrent git reads and let the slower write
  clobber the newer snapshot.

- pull-requests-scaling.integration.test.ts: fix `timeout` → `timeoutMs`
  in two waitFor calls. The wrong key was silently dropped, falling back
  to the 5 s default and risking flake on slow CI.

* fix(perf): coalesce per-workspace sync — running + rerun-pending flag

Replaces the linear promise chain with a "running + rerun pending"
flag so N events for the same workspace collapse into at most one
running sync + one queued rerun. Since each sync reads fresh state,
queuing additional redundant syncs adds no value — it just wastes
git subprocesses.

Bounded under sustained watcher noise (long interactive rebase,
bulk ref churn), where the previous chain could pile up dozens of
sequential no-op syncs.

* fix(workspace-fs): exempt directories from pathTypes LRU + de-flake cap test

Splits WatcherState.pathTypes into filePaths (LRU-capped at 10k) and
directoryPaths (uncapped Set). Pre-fix, the unified Map could LRU-evict
a directory hint, after which a delete event for that directory fell
back to isDirectory=false and patchSearchIndexesForRoot only pruned
the exact path — leaving descendant search-index entries stale until
the next full rebuild. Directory count per worktree is bounded by repo
structure (O(100s) even for huge repos), so tracking them uncapped is
fine; only the file-path stream grows unboundedly.

Also fixes the cap-eviction test which was polling on a 95% event-count
threshold (10_000 cap × 95% = 9,690 events, which can land before
eviction triggers and stall under coalesced delivery). Now polls on
the actual eviction outcome — `pathTypes.has(cap-0.tmp) === false` —
and asserts cap on filePaths.size directly via a new getFilePathsSize
helper. Bench predicate is similarly capped at min(target, FILE_PATHS_MAX)
to avoid spinning the deadline once size plateaus.

* fix(perf): route safety-net sweep through workspaceSyncState queue

The serialization queue added previously only covered the watcher-driven
path; `syncWorkspaceBranches` (initial startup sweep + 5-min safety net)
still called `syncWorkspaceRow` directly, so it could race a concurrent
watcher-triggered sync for the same workspace and clobber newer state.

The sweep now iterates ids and routes each through enqueueWorkspaceSync,
which coalesces — if a watcher sync is already running for a workspace,
the sweep just flips rerunPending and awaits the running promise.
Sequential per-workspace iteration matches the original sweep's
git-subprocess concurrency profile.

Test mocks override syncOneWorkspace to bypass the drizzle .where()
chain, since the sweep now performs a per-workspace row lookup that
doesn't compose cleanly with the existing chained mock structure.

* fix(perf): address remaining PR review nits

- watch.ts: serialize normalizeEvent calls in flushPendingEvents — replaces
  Promise.all with a sequential for-of loop so LRU mutations land in event
  order, not stat-completion order. Net code roughly unchanged but removes
  the concurrency hazard coderabbit flagged.

- watch-pathtypes-growth.test.ts: consolidate manager cleanup into afterEach.
  Tests register managers via createManager() and stop calling unsubscribe()
  + manager.close() inline. afterEach closes them all even if a test throws.
  Net code reduction (-16 lines).

- pull-requests.test.ts: tighten warn assertion to match the actual
  "Failed to sync workspace" prefix instead of accepting any console.warn.

- v2-paths-worktree-perf-fix-plan.md: align acceptance criteria wording with
  the tests that actually landed.

* refactor(workspace-fs): drop dead TTL sweep + inline one-shot bump helper

`evictStaleSearchIndexEntries` is redundant: per-hit TTL check on
getSearchIndex line 299-307 already discards stale entries on access,
and the hard LRU cap of 12 bounds memory regardless of TTL behavior.
The build-path sweep over all entries was duplicated work that did
nothing the LRU eviction wasn't already doing.

`bumpAndReturnCachedIndex` had one caller and was 4 lines of body —
inlined directly into the hit path. Net -23 lines.

* fix(ci): remove typecheck shim, exclude benches from default test run

CI typecheck failed because workspace-fs had a hand-rolled
src/bun-test.d.ts shim with a minimal `expect` (only `toContain` /
`toEqual` / `toHaveLength` / `toBeNull` / `toBeTruthy`) that shadowed
the real bun-types definitions. Adding bun-types as a devDependency
and dropping the shim restores the full matcher surface.

CI tests OOM'd on @superset/workspace-fs#test (exit 137). The
cache-and-paths-memory bench creates 130 worktrees × 200 files +
heap snapshots and was being picked up by default `bun test` because
of its `.bench.test.ts` suffix. Renamed both bench files to
`.bench.ts` (off the auto-discovery pattern) and added explicit
`bun run bench` scripts so they're still runnable on demand.

Also tightened search-cache-eviction.test.ts array typings: previous
`unknown[]` was fine under the shim's permissive `expect` but doesn't
typecheck against the real signature. Now uses
`Awaited<ReturnType<typeof getSearchIndex>>[]` with explicit guards
for noUncheckedIndexedAccess.

* fix(ci): slim integration test — drop scaling cases covered by mock units

CI host-service#test was OOMing (exit 137) because the integration
test created 4 scenarios with simple-git + WorkspaceFilesystemManager +
GitWatcher per scenario (15 worktrees + 15 parcel-watcher subscriptions
total). Two of those scenarios just re-asserted what the mock-based
unit test in test/pull-requests-scaling.test.ts already pins —
linearity of git-subprocess count and "safety-net walks all N".

Removes the duplicative integration scaling cases. Keeps only the
event-driven scenario, which is the unique integration coverage
(verifies a real `git commit` in one workspace triggers exactly one
single-workspace sync, with the others staying quiet). Reduced from
5 to 3 worktrees — enough to prove "only the target was touched".

Net: -142 lines, ~80% fewer worktrees spawned per test file run.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants