fix(ui): refresh + auto-switch worktrees on session idle#516
Open
omercnet wants to merge 2 commits into
Open
Conversation
Eliminate opencode's "reply for unknown request" warning caused by orphaned/stale question replies in the CodeNomad UI. Previously the question request lifecycle lacked the idempotency and stale-guard layer that permissions already had, so a question popup answered after a reconnect, restart, or rapid double-submit could POST a reply for a request the backend no longer tracked. User-visible behavior change: - Answering a question prompt that has already expired no longer fails silently or mis-routes; the UI now surfaces a localized "this prompt expired, send your answer as a message instead" notice (new i18n key toolCall.question.errors.expired added to all 7 locales). - Rapid double-submit (Enter + button) can no longer fire two replies. - Reconnect/rehydrate no longer re-surfaces an already-answered question. Implementation: - New stores/question-replies.ts ledger (markQuestionReplied / hasRepliedQuestion / pruneRepliedQuestions / clearRepliedQuestions), a structural mirror of permission-replies.ts (no shared abstraction, per YAGNI; prune semantics may diverge). - sendQuestionReply/sendQuestionReject reconcile against question.list() before POSTing (no error-string parsing); on absent requestID they take the expired-prompt path instead of POSTing to a "root" fallback, and resolve the owning worktree when the stored slug is missing. markQuestionReplied is recorded on success before removeQuestionFromQueue. - syncPendingQuestions adopts the full timestamp-based prune+filter pattern so replied questions are never rehydrated. - The question ledger is cleared only on instance removal, never on rehydrate, so in-flight stale replies cannot slip through. - session-events stale guards on handleQuestionAsked/handleQuestionAnswered. - tool-call.tsx synchronous double-submit guard before the await boundary. Also fixes two pre-existing module-load crashes uncovered while making the UI test suite green (ownership policy): client-identity.ts dereferenced window storage before its typeof-window guard, and server-events.ts opened an EventSource at module load without an EventSource guard. Both fixes are minimal and production-safe. Validation: UI suite 49/49 pass (bun test --conditions browser), UI typecheck clean (tsc --noEmit). Evidence packet under evidences/058-question-reply-idempotency/.
The opencode agent can run `git worktree add` mid-session. CodeNomad cached its worktree list with no invalidation trigger for out-of-band git changes, so the files view stayed bound to a stale list and the session remained mapped to its old worktree slug. Wire a debounced refresh into the existing onSessionIdle path (sse-manager -> handleSessionIdle) via a new refreshWorktreesOnIdle in the worktrees store. On idle we reload the live server enumeration (no server cache added) and apply a constrained auto-switch: - Per-instance trailing debounce (~600ms) keyed by instanceId, deduped on the parent session id, so idle storms coalesce into one fetch. - Add a reloadLoads in-flight guard to reloadWorktrees so it no longer overlaps with rehydrateInstance's reload. - Diff the slug set before/after reload. Auto-switch only when exactly one worktree was added AND the idle session is the active session AND the parent session's slug has not changed since the snapshot (user has not manually switched). Zero or multiple additions refresh only. - The switch persists via setWorktreeSlugForParentSession, which writes through OpenCode session metadata (per NeuralNomadsAI#514) so it survives reload. Delete/prune paths are untouched. Adds co-located tests covering idle reload, debounce coalescing, the in-flight guard, single-new auto-switch, zero/multi-new no-switch, and the active-session guard.
|
PR builds are available as GitHub Actions artifacts: https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/26755126669 Artifacts expire in 7 days.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The opencode agent creates worktrees via
git worktree addmid-session (outside CodeNomad's managed flow). CodeNomad cached its worktree list and never invalidated it for out-of-band git changes, so the files view stayed bound to a stale tree until a manual reload or restart.Fix
packages/uionly — no server cache (server enumeration viagit worktree list --porcelainis already live per request):refreshWorktreesOnIdle(instanceId)inworktrees.ts, triggered from the existinghandleSessionIdlepath (no new sse-manager hook). Per-instance ~600ms trailing debounce, deduped on parent session id. Adds areloadLoadsin-flight guard toreloadWorktrees(it had none) to avoid overlap withrehydrateInstance.setWorktreeSlugForParentSession(metadata-backed, per Migrate worktree mappings to session metadata #514). Guarded: switch only when the idle session is the active session AND the user hasn't manually switched in the meantime. Zero or multiple new worktrees → refresh list only, no switch (ambiguous attribution is not reliable without a per-session signal).Why the constraint
CodeNomad cannot reliably attribute a newly-created worktree to a specific session (no per-session cwd signal). The single-new-worktree heuristic covers the common case (agent creates one worktree mid-turn) safely without yanking the user's view.
Validation
bun test --conditions browser) — 49 existing + 7 newtsc --noEmit), build OK