fix(sync): settle a guessed session directory once its owner is known
Selecting a session whose directory this client has not indexed yet routes it through the active directory. That is a deliberate, documented guess: it keeps routing usable while the owning store bootstraps, and it is excluded from both the resolver and persistence. Nothing settled the guess afterwards. `setSessionDirectory` performs exactly that promotion, but only confirmed destinations call it — a completed move or a worktree this client created. A session whose directory the client learned about later, such as one in a worktree created outside this client, kept the guess forever: every message fetch was addressed to the parent repository, which does not own the session. Captured for such a session before this change, with the session already indexed and its owning store known: routedDirectory .../worktree/feature currentSessionDirectory /repo <- guess, never settled opencodeClientDirectory /repo conflict selected -> /repo and after: routedDirectory .../worktree/feature currentSessionDirectory .../worktree/feature opencodeClientDirectory .../worktree/feature conflict null Directory bootstrap completion is the moment the authoritative directory first becomes readable, so the promotion runs there. It only ever promotes a guess: a confirmed selection and a selection that has since moved on are both left alone, and tests cover both directions. This removes a real routing split-brain. It does not by itself fix the reported symptom of a session created mid-session never rendering; that remains open.
This commit is contained in:
@@ -352,6 +352,12 @@ export type SessionUIState = {
|
||||
debugSessionMessages: (sessionId: string) => Promise<void>
|
||||
pollForTokenUpdates: () => void
|
||||
setSessionDirectory: (sessionId: string, directory: string | null) => void
|
||||
/**
|
||||
* Replace a guessed selection directory with the authoritative one once sync
|
||||
* has indexed the session. Safe to call at any time: it only ever promotes a
|
||||
* guess, never overrides a confirmed selection.
|
||||
*/
|
||||
adoptAuthoritativeSessionDirectory: (sessionId?: string) => void
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -1739,6 +1745,26 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
|
||||
// Handled by sync system's SSE stream
|
||||
},
|
||||
|
||||
adoptAuthoritativeSessionDirectory: (sessionId) => {
|
||||
const target = sessionId ?? get().currentSessionId
|
||||
// Only a guess is promoted. A confirmed selection outranks anything sync
|
||||
// learns later, and a selection that has since moved on must not be
|
||||
// rewritten by a directory that finished bootstrapping in the background.
|
||||
if (!target || target !== guessedSelectionSessionId) return
|
||||
if (target !== get().currentSessionId) return
|
||||
|
||||
const authoritative = getAuthoritativeSessionDirectory(target)
|
||||
if (!authoritative) return
|
||||
|
||||
// The selection stops being a guess even when the directory is unchanged:
|
||||
// the value has now been confirmed by the store that owns the session.
|
||||
guessedSelectionSessionId = null
|
||||
if (authoritative !== get().currentSessionDirectory) {
|
||||
set({ currentSessionDirectory: authoritative })
|
||||
}
|
||||
writeRuntimeSessionMemory(runtimeMemoryKey(), { sessionId: target, directory: authoritative })
|
||||
},
|
||||
|
||||
setSessionDirectory: (sessionId, directory) => {
|
||||
const normalized = normalizePath(directory)
|
||||
// Callers set this from a confirmed destination (a completed move, a
|
||||
|
||||
Reference in New Issue
Block a user