Skip to content

feat(code): route file/diff opens to the Code surface (cave-ohcj)#3735

Merged
BunsDev merged 1 commit into
mainfrom
feat/code-phase2b
Jul 24, 2026
Merged

feat(code): route file/diff opens to the Code surface (cave-ohcj)#3735
BunsDev merged 1 commit into
mainfrom
feat/code-phase2b

Conversation

@BunsDev

@BunsDev BunsDev commented Jul 24, 2026

Copy link
Copy Markdown
Member

First half of cave-ohcj (phase 2b of the Code surface series; follows #3732).

What

File-open events — cave:open-project-file, cave:open-file-diff, cave:browse-project-files — now land on the Code surface instead of Chat's inline code rail, from every mode including chat itself. Clicking a file chip or diff link in a transcript jumps to that session's workbench with the file focused in the Files tab (or its diff expanded in the Diff tab).

How

  • pending-code-rail-open.tspending-code-open.ts: PendingCodeOpen carries the raising chat session (sessionId).
  • workspace.tsx: bridge enqueues unconditionally (no chat-mode skip), attaches activeChatSessionId via ref mirror, switches to code mode; pending open handed to CodeView.
  • code-view.tsx: consumes pendingOpen — selects the raising session, or a browse root's newest session (codeSessionWorkRoot match); degrades to the surface with no stale focus when nothing matches. Manual rail switches drop pending focus so it can't replay into an unrelated workbench.
  • code-workbench.tsx: openTarget lands on Diff/Files tab; focusPath/focusNonce forwarded to SessionChangesInner (existing contract) and CodeWorkbenchFiles (new props).
  • use-workspace-rail-controller.ts: chat rail no longer listens for global open events; keeps surface-internal cave:changes-open. The rail itself stays — slimming is the second half of cave-ohcj.
  • Pins flipped: code-surface-mode, workspace-chat-handoff, tool-target-file.

Verification

  • pnpm typecheck ✓, eslint ✓
  • 892 app + 239 api test files ✓
  • e2e: code-surface, code-rail, mobile drill-in + sheet — 8 passed (2 local-load flakies passed on retry)

File-open events (cave:open-project-file, cave:open-file-diff,
cave:browse-project-files) now land on the Code surface instead of
Chat's inline code rail — from every mode, including chat itself.

- pending-code-rail-open.ts → pending-code-open.ts: PendingCodeOpen
  carries the raising chat session (sessionId) so CodeView can select
  that session's workbench.
- workspace.tsx: the bridge enqueues unconditionally (no more chat-mode
  skip), attaches activeChatSessionId, and switches to code mode; the
  pending open is handed to CodeView, not ChatSurface.
- code-view.tsx: consumes pendingOpen — picks the raising session (or a
  browse root's newest session; degrades gracefully when none matches),
  lands on the Sessions tab, and forwards the target to the workbench.
  A manual rail switch drops any pending focus so it can't replay into
  an unrelated workbench.
- code-workbench.tsx: openTarget lands on the Diff or Files tab with
  the path focused (SessionChangesInner focusPath; new focusPath/
  focusNonce on CodeWorkbenchFiles).
- use-workspace-rail-controller.ts: the chat rail no longer listens for
  the global open events; it keeps only its surface-internal
  cave:changes-open affordance. The rail itself stays (slimming is the
  follow-up half of cave-ohcj).
- Pins flipped: code-surface-mode, workspace-chat-handoff,
  tool-target-file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 24, 2026 00:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Routes file/diff/browse open events (cave:open-project-file, cave:open-file-diff, cave:browse-project-files) to the dedicated Code surface so opens land in the correct session workbench (instead of Chat’s inline code rail), and updates the contract tests/pins to enforce the new behavior.

Changes:

  • Introduces PendingCodeOpen (with optional raising sessionId) and updates Workspace to enqueue opens and switch into mode="code".
  • Adds Code surface consumption: CodeView resolves the target session (by sessionId or browse root), and CodeWorkbench/Files tab focus the appropriate diff/file via focusPath + nonce.
  • Removes the chat rail controller’s global file/diff/browse event listeners; retains only surface-internal cave:changes-open.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/lib/use-workspace-rail-controller.ts Stops consuming global file/diff/browse open events; keeps only cave:changes-open handling for the rail.
src/lib/tool-target-file.test.ts Updates pin assertions to require Code-surface routing and to forbid rail controller global listeners.
src/lib/pending-code-open.ts Defines PendingCodeOpen and adds optional sessionId for session-aware routing.
src/components/workspace.tsx Bridges open events into pendingCodeOpen and switches to mode="code"; passes pending open into CodeView.
src/components/workspace-rail.tsx Updates rail focus type import to PendingCodeOpen.
src/components/workspace-chat-handoff.test.ts Updates contract tests to enforce Code-surface routing and CodeView/Workbench handling.
src/components/code-workbench.tsx Accepts openTarget and routes to Diff/Files tab + forwards focus props.
src/components/code-workbench-files.tsx Adds focusPath/focusNonce and applies external focus by selecting/opening the path.
src/components/code-view.tsx Consumes pendingOpen, resolves target session (by sessionId/root), and passes a scoped openTarget to the resolved workbench.
src/components/code-surface-mode.test.ts Updates pin to assert file-open events route to Code surface (and adjusts Diff tab mount assertion).
src/components/chat-surface.tsx Removes pending code-rail open props/effect; Chat no longer participates in global file/diff routing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +118 to +136
useEffect(() => {
if (!pendingOpen) return;
const byId = pendingOpen.sessionId
? groups.flatMap((g) => g.sessions).find((row) => row.id === pendingOpen.sessionId)
: undefined;
const root = pendingOpen.kind === "files" ? pendingOpen.root : undefined;
const trim = (p: string) => p.replace(/\/+$/, "");
const byRoot =
!byId && root
? groups.flatMap((g) => g.sessions).find((row) => trim(codeSessionWorkRoot(row)) === trim(root))
: undefined;
const target = byId ?? byRoot;
setTopTab("sessions");
if (target) setSelectedId(target.id);
// Root browse with no matching session: there is no workbench to focus —
// land on the surface and leave the rail/selection as-is.
setWorkbenchTarget(root && !target ? null : { open: pendingOpen, sessionId: target?.id ?? null });
onPendingOpenHandled?.();
}, [groups, onPendingOpenHandled, pendingOpen]);
@BunsDev
BunsDev merged commit 0f78e6f into main Jul 24, 2026
24 of 25 checks passed
@BunsDev
BunsDev deleted the feat/code-phase2b branch July 24, 2026 05:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants