fix(worktrees): bound ownership refreshes and lazily cache inventories - #708
Conversation
Keep the native event stream responsive when the shared daemon emits events from many unrelated directories. Sequential ownership misses previously replaced the inventory and its negative cache for every new directory, repeatedly running full worktree discovery and blocking the serial event bridge. Allow one miss-triggered refresh per inventory cache lifetime. Further foreign directories share that refreshed snapshot, while TTL expiry and explicit worktree invalidation still discover external changes. Preserve concurrent singleflight loads and canonical path ownership checks. Add a regression for twenty distinct foreign directories, negative-result reuse, explicit invalidation, and TTL expiry. It reproduces 21 inventory loads before the fix and requires two afterward. Validated the 63 targeted routing, workspace-manager, and directory tests, server typecheck, and emitted server build; measured the installed application's event backlog before and after a graceful relaunch.
|
Follow-up live measurement (45 seconds, after restart): the multi-minute backlog is reduced, but this is not a complete startup/latency fix. Session event lag in the renderer had a median of 851 ms and p95 of 12.2 s, with overall spikes up to 15.5 s. Direct native events had session median 52 ms and p95 339 ms. Startup resource timing also shows a 9.6 s worktree request and an 18.5 s project metadata request. Investigating those remaining inventory/hydration costs separately from this bounded miss-refresh correction. |
|
The remaining inventory cost is now isolated on the real repository: native location.get 22 ms, worktree.refresh 282 ms, worktree.list 5 ms for 165 entries; complete listNativeWorktrees took 24,067 ms and returned 165 owned worktrees. The implementation launches three Git commands per inspected checkout (495 for those entries, plus root checks), at up to eight concurrent checkouts. The dominant remaining cost is the local validation/annotation pass rather than the native list API. This PR bounds repetition of that pass; it does not eliminate its cold-start cost. |
Retain successful native worktree inventories rather than only sharing in-flight requests. Serve warm display snapshots immediately and refresh stale data on demand in one shared background scan, without polling. Fence invalidated or disposed scans and retain the last display snapshot with retry backoff after failures. Keep directory authorization on validated snapshots and force fresh family transaction inventories. Publish completed inventory changes to existing UI consumers and serialize their reload behind older HTTP responses. Open the worktree selector without waiting for I/O and suppress duplicate selection notifications so reconciliation cannot close it or trigger a family move. Cover cache expiry, concurrency, mutation races, failures and disposal; real native rename/cache behavior with an isolated OpenCode 2.0.7 daemon; and Chromium selector updates, blocked refresh dismissal and pointer/keyboard/touch gestures. Passed 110 targeted server tests, five UI store tests, four browser tests, server/UI typechecks and builds. Cold scan cost and authoritative event-routing stalls remain separate from warm display caching.
Require post-mutation inventory validation before returning display reads, propagate ownership misses through the lower inventory cache, and reject directory resolutions invalidated while loading. Preserve lazy display snapshots for passive refreshes while keeping create/remove read-your-writes semantics. Coalesce UI refresh bursts into one in-flight read and a trailing refresh, retaining the last successful snapshot on reload errors. Keep selector options and inline actions focused through background updates and dismiss explicit reselection without moving the session. Document refresh conventions against existing catalogue, Git and virtualization behavior. Add layered-cache and invalidation race regressions, native isolated manager create/remove coverage, and browser tests for stale HTTP responses, refresh bursts, keyboard focus and menu gestures. Synchronize the Git singleflight test mock with native ESM imports. Validation: 110 server tests, 7 UI store tests, 7 Chromium tests, isolated OpenCode 2.0.7 fixture, server/UI typechecks and UI build.
|
PR builds are available as GitHub Actions artifacts: https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/35339219496 Artifacts expire in 7 days.
|
Summary
Bound miss-triggered ownership refreshes and retain completed worktree inventories with demand-driven stale-while-revalidate reads for display consumers.
The shared native event bridge awaits directory ownership sequentially. Previously, each new foreign directory discarded the inventory and its negative cache, triggering another complete native/Git scan. Twenty foreign directories caused 21 inventory loads; the ownership regression now requires two and covers explicit invalidation and expiry.
The manager also used to retain only an in-flight inventory promise. Sequential UI reads therefore repeated the complete scan.
WorktreeInventorynow retains the last successful snapshot:workspace.worktreesChanged; existing UI consumers queue a reload behind older HTTP responses. Unchanged scans do not create notification feedback loops.Measurements and scope
Validation
Installed state
Only the earlier compiled ownership-cache module (
70af9c84) has been installed locally. The new inventory cache and selector changes are built and tested in the existing worktree but have not been deployed to the running installation. No shared daemon or user profile was restarted or modified for these new tests.