返回 DeepSeek-Reasonix
DOCK_FILE_NAVIGATION.md
根目录 / docs / DOCK_FILE_NAVIGATION.md
1 # Dock file navigation
2
3 How the right dock opens a file, who owns the result, and why a click can no
4 longer start a render loop. Read this before changing
5 `WorkspaceDockRegion`, `WorkspacePanel`, `RemotePanel` or anything under
6 `lib/fileNavigation*`.
7
8 ## Contract
9
10 Opening a file is a command, not a render.
11
12 ```
13 file row / markdown link / verified answer reference / file tree / preview control
14 → bound open command (FileNavigationOwner)
15 → resolve the resource and re-check the command is still current
16 → pick the target dock and preview position
17 → commit a navigation record
18 → the dock subscribes and renders what was committed
19 ```
20
21 - **A render never navigates.** Panels read `owner.getSnapshot(key)` through
22 `useSyncExternalStore`; nothing is published while React renders.
23 - **Resource identity, access context and navigation parameters are separate.**
24 Preview identity is resource space + backend-canonical path. Session tab,
25 source and optional `toolCallId` belong to `FileAccessContext`; `preview`,
26 `source` and `reveal-tree` are parameters. Switching a parameter reuses the
27 preview tab, while switching sessions can rebind its access without changing
28 which file it represents.
29 - **Access context travels with the command.** Workspace, presented and
30 host-verified answer references keep distinct readers. Reopening a path from
31 another entry point uses that command's credentials, never an earlier
32 presentation's. An answer reference is resolved and authorized again by the
33 host for every read or direct action.
34 - **Records are keyed by dock tab.** A record holds the dock
35 instance identity, its lifecycle generation, the entries with their access
36 contexts, the last navigation intent, a display revision, a content revision
37 and the lifetime signal.
38 - **Only an explicit command advances a revision.** An equivalent command keeps
39 the entry objects and the entry list identical, so the preview does not
40 restart a read that is still valid.
41
42 ## Harness design → Reasonix implementation
43
44 | Harness design | Reasonix implementation |
45 | --- | --- |
46 | Command-driven navigation: opening is an event, rendering reads the result | `FileNavigationOwner` (`lib/fileNavigationOwner.ts`) commits records; `WorkspaceDockRegion` no longer builds a request during render; `useFileNavigationRecord` (`app-shell/useFileNavigation.ts`) only reads |
47 | Stable resource identity: a canonical file coordinate, not an object reference | `FileResourceRef` / `FileAccessContext` / `ResolvedFileResource` (`lib/fileResource.ts`); backend-resolved `identityPath` collapses relative/absolute aliases, while access context separately keeps workspace, presented and verified-reference readers distinct |
48 | Separate navigation parameters from identity | `FileNavigationParams` — `action` (`preview`/`source`/`reveal-tree`) and `view` (`files`/`changed`) never change what the resource is |
49 | Independent navigation instance per dock | One `FileNavigationOwner` per running app instance (`useFileNavigationRuntime`), one record per dock tab, `generation` per lifecycle |
50 | Command results are reported, not thrown | `FileNavigationOutcome` — `opened` / `cancelled` (superseded, closed, disposed) / `failed`; a cancelled command shows no error |
51 | Resource URL ownership on a cancelled browser preview | `openBrowserPreview` releases exactly one URL: the tab owns it once handed over, otherwise the command revokes it |
52 | No global cancellation | `fileNavigationLifetime.ts` is gone; a new command supersedes the record's pending one, and `retain`/`bindScope` end a record's lifetime |
53 | Tab restore without replay | The record survives a dock collapse; a remount reads the retained selection without re-applying the last intent or re-running the open command |
54
55 Not ported: split view, floating preview windows, layout undo history, the
56 Cordis plugin framework, and the `dsh-resource://` scheme (resource ids stay
57 internal to navigation).
58
59 ## Lifecycle and cancellation
60
61 | Event | Effect |
62 | --- | --- |
63 | New command for the same dock | Aborts the record's pending operation; the older outcome is `cancelled` |
64 | Another command to a different dock | Nothing: panels never cancel each other |
65 | Dock tab closed or removed from the active workspace | `retain` drops the record and aborts its lifetime |
66 | Session changes inside the same project | `bindScope` keeps entries and selection, then replaces their access contexts with current-session workspace access |
67 | Project/remote-host resource space changes | `bindScope` rebuilds the record: empty entries, advanced generation, previous lifetime aborted |
68 | Dock collapsed (`workspacePanelOpen` false) | Nothing: the record is what re-expanding restores |
69 | Runtime unmounted | `dispose` aborts every record and every one-shot operation |
70 | Dock tab id reused after close | A new generation; remembered paths come back with workspace access only |
71
72 ## Persistence
73
74 Only paths are persisted (`workspaceViewMemory`, the dock tab list). Lifecycle
75 generations, cancellation signals and access contexts live in memory and are
76 never written to `localStorage`. Storage written by older versions is read as
77 paths and revalidated against the current workspace, which is why a restored
78 preview never regains a presented tool scope.
79
80 No Go/Electron bridge payload, session log, tool schema, standing instruction or
81 provider request byte changes, so prompt caching is unaffected.
82
83 ## Verification
84
85 | Area | Test |
86 | --- | --- |
87 | Navigation instance | `file-navigation-owner.test.ts` |
88 | Command ordering and cancellation | `file-navigation-races.test.ts` |
89 | Verified answer-reference reader and source toggle | `workspace-reference-reader.test.tsx` |
90 | Dock chain (local and remote) | `dock-file-navigation.test.tsx` |
91 | Preview tabs, cap, source mode, reveal, generations | `file-navigation-dock.test.tsx` |
92 | Render-loop defect, StrictMode, re-renders, remount | `file-navigation-lifecycle.test.tsx` |
93 | Remote reads, save isolation, disconnect | `remote-file-navigation-races.test.tsx` |
94 | Dock request delivery | `dock-navigation.test.ts`, `dock-view-requests.test.tsx` |
95 | Real DOM and Electron | `bench/dock-file-navigation.mjs` (`test:app-browser`, `test:dock-electron`) |
96 | Answer-reference click into the running owner | `bench/chat-file-reference.mjs` (`test:chat-file-browser`) |
97
97 lines MARKDOWN