Commit graph

3 commits

Author SHA1 Message Date
Pascal André
c05f7956d6
fix(events): prevent relay timeout on worktree discovery waits (#837)
## Summary

Follow-up to merged #835: fix the demonstrated dependency behind real
`Instance event relay routing timed out` disconnects, not duplicate
connection notifications.

Existing diagnostics captured relay ownership blocked in `getWorktrees
-> WorktreeInventory -> POST /api/worktree/refresh`, including a refresh
pending beyond 150 seconds and a separate ~220-second completed call.
That exceeds the unchanged 60-second relay job deadline, so the shared
relay reconnects and Usage correctly clears for all projects.

Event authorization now uses a separate **registered-only** native
worktree inventory and never starts or joins native strategy discovery.
Native project/checkout checks, physical Git membership, clone
exclusion, configured roots, nested-folder and WSL projection remain in
the shared catalogue implementation. Native/local invalidation and
workspace disposal fence both inventories and their distinct
directory-cache namespaces. Ordinary request authorization and explicit
worktree discovery retain refresh and read-your-writes behavior.
Full-location checks receive the connection attempt's abort signal.

No timeout increase, swallowed reconnect, stale authorization fallback
or quota-specific workaround. FIFO lanes, ownership retries,
generation/workspace fences and retained-work budgets are unchanged.

## Validation

- Baseline-red production-manager + relay reproduction: hold discovery
unresolved and require the linked-worktree event to route before
releasing it.
- All **248 workspace tests pass**, including event order/reconnect,
ownership, no-Git, WSL, configured Git roots and inventory invalidation.
- Server TypeScript check passes.
- Isolated OpenCode **2.0.22** location/worktree/event fixture passes,
including real pre-refresh native registration and ordered real session
events during a held fixture-manager discovery request.
- Isolated no-Git native prompt/recovery fixture passes.
- `git diff --check` passes.

## Scope / limitations

The native refresh's internal delay is not claimed fixed; the relay no
longer depends on that unnecessary discovery operation. Actual transport
failures and other stuck native reads still trigger bounded
reconnect/reconciliation. Compaction and window-resize jank are not
established causes or fixed by this PR.

The installed application, shared daemon, user settings and independent
#824 worktree are untouched. This conversation remains attached to
`D:\CodeNomad`.

Existing oversized source touched:
`packages/server/src/workspaces/manager.ts`, approximately 1,260 lines.
No unrelated size-only refactor.

Independent Gatekeeper review will be published against the exact PR
head, with findings resolved and fresh passes until zero findings before
the requested administrator merge.
2026-10-03 04:04:03 +02:00
Pascal André
96ef38c67a
fix(worktrees): bound ownership refreshes and lazily cache inventories (#708)
## 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.
`WorktreeInventory` now retains the last successful snapshot:
- Display reads reuse it immediately. Expiry (10 seconds) or
invalidation only schedules one background refresh when a consumer next
reads it; there is no polling timer.
- Concurrent callers share the scan, failures retain display data with
demand-driven retry backoff, and invalidation fences obsolete results
with serialized trailing validation.
- Directory authorization uses validated reads, which wait if the
snapshot is stale. Family create/move/delete checks force a fresh scan.
Workspace disposal fences pending results.
- A changed completed snapshot publishes `workspace.worktreesChanged`;
existing UI consumers queue a reload behind older HTTP responses.
Unchanged scans do not create notification feedback loops.
- The selector opens before the reload completes. Duplicate selection
notifications during inventory reconciliation no longer dismiss an open
menu or produce a family move, and a delayed response cannot reopen a
dismissed menu.

## Measurements and scope
- Before the initial miss-refresh correction, the installed renderer
lagged native events by 331-336 seconds, versus 42-84 ms on a direct
read-only native subscription.
- The first post-restart sample caught up to 340-699 ms, but a
subsequent 45-second sample still showed session median 851 ms, p95 12.2
seconds, and overall spikes of 15.5 seconds. The corresponding direct
native session median was 52 ms and p95 339 ms. This is **not a complete
startup/event-latency fix**.
- A real 165-worktree inventory measured location.get 22 ms, native
refresh 282 ms, and native list 5 ms, versus 24,067 ms for the whole
CodeNomad inventory. The local validation/annotation pass launches three
Git commands per inspected checkout.
- This memory cache removes repeated display scans and lets warm display
reads continue while stale data refreshes. It does not persist across
backend restarts or reduce the cost of a cold or required authoritative
scan. The serial event bridge can still await such validation.

## Validation
- 110 targeted server tests passed: inventory cache (6), ownership
directory cache, event bridge, workspace lifecycle/routes, worktree
routes, family transactions and evacuation.
- Five existing UI worktree store tests passed with browser conditions.
- Four Chromium tests passed: pointer/keyboard/touch actions, background
completion racing an older HTTP response, immediate cached opening and
Escape dismissal during a blocked refresh, and creation/selection by
stable ID.
- The isolated native location/worktree fixture passed against OpenCode
2.0.7, including real branch rename, cached reuse, lazy invalidation and
forced validation with native events connected.
- Server/UI typechecks, emitted server TypeScript, UI production build
and diff check passed.

## 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.
2026-09-18 13:58:35 +02:00
Pascal André
83a4124b37
feat(v2): manage session families across worktrees (#649)
## Summary

Manage complete session families across the opened local repository's
native OpenCode worktrees. This supersedes #642 with the V2
implementation and addresses #319.

- OpenCode owns `worktree.refresh/list/create/remove`; CodeNomad
supplies the default directory, named-branch policy, local repository
scope and verified family transactions.
- A project is the opened local checkout and its registered worktrees,
identified through the physical Git common directory. Independent clones
sharing an OpenCode project ID stay separate.
- Session rows, composer selection, search and the Git panel follow
native `SessionInfo.location.directory`. Durable metadata is preserved,
but old `worktreeSlug` metadata does not override native placement.
- Creation uses the selected checkout's revision and the main local
checkout's `.codenomad/worktrees` parent. Uncommitted edits stay in the
source checkout. External registered worktrees remain discoverable.
- Keep copy/open/delete actions in the accessible selector, remove the
redundant action bar, hide root-workspace badges, and explain “Create
and use” in all ten locales.

## Current base and native contract

This branch includes merged `dev@e47e01c6`, including #697's separately
reviewed discovery/status compatibility fix. UI/server pin
`@opencode/client@2.0.4`; the independently managed daemon has no
exact-version startup gate.

- Complete bounded inventories use opaque native cursors; continuations
send only the cursor.
- Moves are serialized across complete families, block active sessions,
verify authoritative native locations and roll back partial failures.
- Native move events refresh placement and the catalogue without
speculative cross-project transfers.
- Opaque worktree identifiers are separate from mutable branch labels.
The main checkout cannot be removed.
- Native detached creation is followed by ordinary Git branch selection,
preserving Git's refusal to reuse a checked-out branch;
branch-attachment failure cleans up through the native API.
- Agent placement instructions distinguish creating a checkout, one-off
commands and moving the conversation's execution context. Explicit
attachment choices are preserved.

## Deletion safety

- Physical checkout reservations cover published and in-flight logical
workspaces.
- Project serialization and deletion fences cover all family
source/destination checkouts.
- Complete inventories and cross-project checks run inside the deletion
fence, including nested workspaces and owned Shell/PTY resources.
- Canonical path, filesystem identity, registration, branch, HEAD and
active sessions are rechecked before removal. Replacement checkouts fail
closed; rollback requires the original identity.

## Recovery and Git-panel corrections

Validation on the real repository exposed serial enumeration of 162
worktrees and concurrent ownership misses launching overlapping
inventories. Git annotations now run with bounded concurrency, pending
catalogue reads are shared, and negative ownership results are
snapshot-cached. A regression exercises 40 concurrent misses.

The Git panel now treats its complete local staged/unstaged inventory as
authoritative instead of resurrecting native-only stale rows. It also
rechecks the selected worktree/request after waiting for native VCS
status.

## Validation

Latest integration validation:

- Isolated official **OpenCode 2.0.0 and 2.0.7**: production
discovery/status, native worktree discovery/create/remove, selected
HEAD/default parent/named branches, independent-clone scope, stable IDs,
nested paths, checked-out/dirty guards, complete family moves and
rollback.
- Real Solid selector browser tests: mouse, keyboard, touch, actions
without session movement, creation source and returned stable ID.
- Targeted server ownership/cache/manager/proxy/route/location tests; UI
session projection/request-authority/lifecycle tests; family
evacuation/rollback and Git model regressions.
- UI/server typechecks and production server build including the UI.
- Installed Windows Tauri application on the actual repository:
dedicated session creation/use, native location and row/composer badges,
Git marker visibility, move back, and agent `session_move` reflected in
both badges.
- Rebuilt UI/server resources installed; full Developer Mode restart
restored the original conversation. Disposable sessions, marker,
worktrees and branches cleaned up. The original conversation remained
attached to `pr649-final`.
- `git diff --check`.

An immediate post-restart desktop run exceeded a 30-second
refresh-button wait; the subsequent completed run passed. These checks
validate the workflows, not a startup latency bound or exhaustive
compatibility of every OpenCode payload.

Earlier branch validation also covered Electron/Tauri native actions,
packaging and desktop parity; those results remain in the PR's
historical validation comments. Detailed contract and latest evidence:
`dev-docs/WORKTREE_SESSION_PLACEMENT.md`.

## Residual limit

OpenCode and Git expose no shared snapshot/CAS transaction. CodeNomad
narrows races with reservations, locks, fences and repeated
authoritative checks; unrelated processes can still mutate state outside
those controls.
2026-09-17 22:57:37 +02:00