From 2428087fbb792c231fbfc5fb8a20f1e723746fca Mon Sep 17 00:00:00 2001 From: Victor Vogel Date: Tue, 20 Oct 2026 20:28:22 -0500 Subject: [PATCH 1/8] fix(web): preserve literal backslashes in changed-file paths Checkpoint paths come from Git numstat, which separates directories with "/" on every platform. The changed-files tree also split on "\", so a POSIX file named back\slash.txt showed under a false "back" folder and shared the identity of back/slash.txt, opening the wrong diff. Split only on "/" and keep the card's folder check in line. Co-Authored-By: Claude Opus 5.5 --- .../src/components/chat/ChangedFilesTree.tsx | 2 +- apps/web/src/lib/turnDiffTree.test.ts | 30 +++++++++++++++++++ apps/web/src/lib/turnDiffTree.ts | 7 ++--- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/chat/ChangedFilesTree.tsx b/apps/web/src/components/chat/ChangedFilesTree.tsx index 1e648495a00f2..da51f1929ea6a 100644 --- a/apps/web/src/components/chat/ChangedFilesTree.tsx +++ b/apps/web/src/components/chat/ChangedFilesTree.tsx @@ -47,7 +47,7 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: { onFileContextMenu, } = props; const summaryStat = summarizeTurnDiffStats(files); - const hasDirectories = files.some((file) => /[/\\]/.test(file.path)); + const hasDirectories = files.some((file) => file.path.includes("/")); return (
{ }, ]); }); + + it("keeps literal backslashes in file names apart from directory paths", () => { + const tree = buildTurnDiffTree([ + { path: "back\\slash.txt", kind: "modified", additions: 1, deletions: 0 }, + { path: "back/slash.txt", kind: "modified", additions: 2, deletions: 0 }, + ]); + + expect(tree).toEqual([ + { + kind: "directory", + name: "back", + path: "back", + stat: { additions: 2, deletions: 0 }, + children: [ + { + kind: "file", + name: "slash.txt", + path: "back/slash.txt", + stat: { additions: 2, deletions: 0 }, + }, + ], + }, + { + kind: "file", + name: "back\\slash.txt", + path: "back\\slash.txt", + stat: { additions: 1, deletions: 0 }, + }, + ]); + }); }); diff --git a/apps/web/src/lib/turnDiffTree.ts b/apps/web/src/lib/turnDiffTree.ts index cd9bfc831fbb3..2fabb20147361 100644 --- a/apps/web/src/lib/turnDiffTree.ts +++ b/apps/web/src/lib/turnDiffTree.ts @@ -32,11 +32,10 @@ interface MutableDirectoryNode { const SORT_LOCALE_OPTIONS: Intl.CollatorOptions = { numeric: true, sensitivity: "base" }; +// Checkpoint paths come from Git, which separates directories with "/" on every +// platform. A backslash is part of a POSIX file name, not a separator. function normalizePathSegments(pathValue: string): string[] { - return pathValue - .replaceAll("\\", "/") - .split("/") - .filter((segment) => segment.length > 0); + return pathValue.split("/").filter((segment) => segment.length > 0); } function compareByName(a: { name: string }, b: { name: string }): number { From ecee07de86e301d0d17bf24046dd49cd29c3b9b8 Mon Sep 17 00:00:00 2001 From: Victor Vogel Date: Sun, 8 Nov 2026 09:40:20 +0000 Subject: [PATCH 2/8] fix(web): Open and Reveal keep literal backslashes in diff paths The changed-files tree now keeps back\slash.txt apart from back/slash.txt, but resolveDiffPathForWorkspace still turned every backslash into "/", so Open, the context-menu Open and Reveal landed on back/slash.txt. Split Git paths on "/" only and leave the host separator to the path join. Co-Authored-By: Claude Opus 5.5 --- apps/web/src/diffFileActions.test.ts | 51 +++++++++++++++++----------- apps/web/src/diffFileActions.ts | 10 +++--- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/apps/web/src/diffFileActions.test.ts b/apps/web/src/diffFileActions.test.ts index b4fdb71f8d89a..4495962f2b16e 100644 --- a/apps/web/src/diffFileActions.test.ts +++ b/apps/web/src/diffFileActions.test.ts @@ -45,16 +45,26 @@ describe("openDiffFilePrimaryAction", () => { ).toBe("frontend/Dockerfile"); }); - it("handles Windows roots and mixed diff separators", () => { + it("handles Windows roots", () => { expect( resolveDiffPathForWorkspace({ - filePath: "Frontend/src\\index.ts", + filePath: "Frontend/src/index.ts", workspaceRoot: "C:\\repo\\frontend", repositoryRoot: "C:\\repo", }), ).toBe("src/index.ts"); }); + it("keeps a literal backslash in a Git file name", () => { + expect( + resolveDiffPathForWorkspace({ + filePath: "frontend/back\\slash.txt", + workspaceRoot: "/repo/frontend", + repositoryRoot: "/repo", + }), + ).toBe("back\\slash.txt"); + }); + // regression: #6174 opening diff files broke for repositories at a filesystem root it.each([ { workspaceRoot: "/frontend", repositoryRoot: "/" }, @@ -69,23 +79,26 @@ describe("openDiffFilePrimaryAction", () => { ).toBe("index.ts"); }); - it.each(["backend/server.ts", "frontend2/app.ts", "frontend/../secret.ts", "C:secret.ts"])( - "does not open an out-of-project diff path: %s", - (filePath) => { - const openInEditor = vi.fn(); + it.each([ + "backend/server.ts", + "frontend2/app.ts", + "frontend/../secret.ts", + "frontend/..\\secret.ts", + "C:secret.ts", + ])("does not open an out-of-project diff path: %s", (filePath) => { + const openInEditor = vi.fn(); - openDiffFilePrimaryAction({ - threadRef: THREAD_REF, - filePath, - activeCwd: "/repo/frontend", - repositoryRoot: "/repo", - openInEditor, - }); + openDiffFilePrimaryAction({ + threadRef: THREAD_REF, + filePath, + activeCwd: "/repo/frontend", + repositoryRoot: "/repo", + openInEditor, + }); - expect( - selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, THREAD_REF), - ).toMatchObject({ isOpen: false }); - expect(openInEditor).not.toHaveBeenCalled(); - }, - ); + expect( + selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, THREAD_REF), + ).toMatchObject({ isOpen: false }); + expect(openInEditor).not.toHaveBeenCalled(); + }); }); diff --git a/apps/web/src/diffFileActions.ts b/apps/web/src/diffFileActions.ts index 99baf13c94160..828734aff0832 100644 --- a/apps/web/src/diffFileActions.ts +++ b/apps/web/src/diffFileActions.ts @@ -17,11 +17,11 @@ function normalizedRelativePathSegments(filePath: string): ReadonlyArray return null; } - const segments = filePath - .replaceAll("\\", "/") - .split("/") - .filter((segment) => segment.length > 0 && segment !== "."); - if (segments.length === 0 || segments.includes("..")) return null; + // Git separates directories with "/" on every platform, so a backslash is part of + // a file name. Windows paths still treat it as a separator, so reject ".." either way. + if (filePath.split(/[\\/]/).includes("..")) return null; + const segments = filePath.split("/").filter((segment) => segment.length > 0 && segment !== "."); + if (segments.length === 0) return null; return segments; } From a1a2d3cce372f18ef9da97a45bbf5669132c1b4c Mon Sep 17 00:00:00 2001 From: Victor Vogel Date: Sun, 8 Nov 2026 10:47:16 +0000 Subject: [PATCH 3/8] fix(web): open diff files from a workspace whose path has a backslash A POSIX workspace such as /repo/back\slash was split on "\" too, so its segments never matched Git's back\slash/a.ts and Open did nothing. Split the workspace suffix on the repository root's own separator: "\" for Windows roots, "/" otherwise. Co-Authored-By: Claude Opus 5.5 --- apps/web/src/diffFileActions.test.ts | 52 ++++++++++++++++++---------- apps/web/src/diffFileActions.ts | 7 ++-- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/apps/web/src/diffFileActions.test.ts b/apps/web/src/diffFileActions.test.ts index 4495962f2b16e..55b46307933c6 100644 --- a/apps/web/src/diffFileActions.test.ts +++ b/apps/web/src/diffFileActions.test.ts @@ -45,25 +45,39 @@ describe("openDiffFilePrimaryAction", () => { ).toBe("frontend/Dockerfile"); }); - it("handles Windows roots", () => { - expect( - resolveDiffPathForWorkspace({ - filePath: "Frontend/src/index.ts", - workspaceRoot: "C:\\repo\\frontend", - repositoryRoot: "C:\\repo", - }), - ).toBe("src/index.ts"); - }); - - it("keeps a literal backslash in a Git file name", () => { - expect( - resolveDiffPathForWorkspace({ - filePath: "frontend/back\\slash.txt", - workspaceRoot: "/repo/frontend", - repositoryRoot: "/repo", - }), - ).toBe("back\\slash.txt"); - }); + it.each([ + { + filePath: "Frontend/src/index.ts", + workspaceRoot: "C:\\repo\\frontend", + repositoryRoot: "C:\\repo", + expected: "src/index.ts", + }, + { + filePath: "Frontend/Src/index.ts", + workspaceRoot: "C:\\repo\\frontend\\src", + repositoryRoot: "C:\\repo", + expected: "index.ts", + }, + { + filePath: "frontend/back\\slash.txt", + workspaceRoot: "/repo/frontend", + repositoryRoot: "/repo", + expected: "back\\slash.txt", + }, + { + filePath: "back\\slash/a.ts", + workspaceRoot: "/repo/back\\slash", + repositoryRoot: "/repo", + expected: "a.ts", + }, + ])( + "resolves $filePath in $workspaceRoot", + ({ filePath, workspaceRoot, repositoryRoot, expected }) => { + expect(resolveDiffPathForWorkspace({ filePath, workspaceRoot, repositoryRoot })).toBe( + expected, + ); + }, + ); // regression: #6174 opening diff files broke for repositories at a filesystem root it.each([ diff --git a/apps/web/src/diffFileActions.ts b/apps/web/src/diffFileActions.ts index 828734aff0832..bd4ab4d8d1b6c 100644 --- a/apps/web/src/diffFileActions.ts +++ b/apps/web/src/diffFileActions.ts @@ -35,16 +35,13 @@ function repositoryRelativeWorkspaceSegments( const normalizedRepositoryRoot = normalizeProjectPathForComparison(repositoryRoot); if (normalizedWorkspaceRoot === normalizedRepositoryRoot) return []; - const separator = normalizedRepositoryRoot.includes("\\") ? "\\" : "/"; + const separator = isWindowsAbsolutePath(normalizedRepositoryRoot) ? "\\" : "/"; const repositoryPrefix = normalizedRepositoryRoot.endsWith(separator) ? normalizedRepositoryRoot : `${normalizedRepositoryRoot}${separator}`; if (!normalizedWorkspaceRoot.startsWith(repositoryPrefix)) return null; - return normalizedWorkspaceRoot - .slice(repositoryPrefix.length) - .split(/[\\/]+/) - .filter(Boolean); + return normalizedWorkspaceRoot.slice(repositoryPrefix.length).split(separator).filter(Boolean); } export function resolveDiffPathForWorkspace(input: { From a0373688ab236c65e310ab6519d36034739b3453 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Tue, 10 Nov 2026 13:42:05 +0000 Subject: [PATCH 4/8] fix(web): resolve nested diff files without a remote --- .../src/components/DiffPanel.browser.test.tsx | 32 +++++++++++++++++++ apps/web/src/components/DiffPanel.tsx | 6 ++-- .../components/chat/TimelineAssistantRows.tsx | 19 +++++++++-- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/DiffPanel.browser.test.tsx b/apps/web/src/components/DiffPanel.browser.test.tsx index 874118daf306f..a5acd431be541 100644 --- a/apps/web/src/components/DiffPanel.browser.test.tsx +++ b/apps/web/src/components/DiffPanel.browser.test.tsx @@ -91,6 +91,7 @@ vi.mock("./DiffFilePathCopyButton", () => ({ DiffFilePathCopyButton: () => null import DiffPanel from "./DiffPanel"; import { useDiffPanelSessionStore, useDiffPanelStore } from "~/diffPanelStore"; +import { selectThreadRightPanelState, useRightPanelStore } from "~/rightPanelStore"; const cwd = "/project-worktree"; const errorMessage = "Could not find a common commit between 'origin/orphan' and HEAD."; @@ -124,6 +125,7 @@ function setup(failedBaseRef: string | null) { keepSelectionOnOpenThreadKey: null, }); useDiffPanelSessionStore.setState({ collapsedFileKeysByScopeKey: {} }); + useRightPanelStore.setState({ byThreadKey: {}, threadPanelVisibilityByThreadKey: {} }); }; reset(); state.diffFilesCollapsed = false; @@ -229,6 +231,36 @@ it.each([ }, ); +it("opens a repository-relative file from a nested project without a remote", async () => { + using context = setup("origin/orphan"); + state.thread = { ...state.thread!, worktreePath: null }; + const path = "project/back\\slash.txt"; + const metadata = preview(); + const source = { + ...metadata.sources[0]!, + files: [{ path, previousPath: null, additions: 1, deletions: 0 }], + }; + state.previews.set("main", Atom.make(AsyncResult.success({ ...metadata, sources: [source] }))); + state.status = Atom.make( + AsyncResult.success({ + ...AsyncResult.getOrElse(context.registry.get(state.status), () => { + throw new Error("Missing status"); + }), + workingTree: { rootPath: "/", files: [], insertions: 0, deletions: 0 }, + }), + ); + useDiffPanelStore.getState().selectBranchBaseRef(context.threadRef, "main"); + await using screen = await render( + + + , + ); + await userEvent.click(screen.getByText(path, { exact: true })); + expect( + selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, context.threadRef), + ).toMatchObject({ activeSurfaceId: "file:back\\slash.txt" }); +}); + it.each([false, true])( "expands an unloaded file on the first click with default collapse %s", async (collapsed) => { diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx index e7e6d614ef77f..0343dbccca399 100644 --- a/apps/web/src/components/DiffPanel.tsx +++ b/apps/web/src/components/DiffPanel.tsx @@ -257,9 +257,6 @@ export default function DiffPanel({ : null, ); const activeCwd = activeThread?.worktreePath ?? activeProject?.workspaceRoot; - const activeRepositoryRoot = activeThread?.worktreePath - ? undefined - : activeProject?.repositoryIdentity?.rootPath; const serverConfig = useAtomValue( serverEnvironment.configValueAtom(activeThread?.environmentId ?? null), ); @@ -277,6 +274,9 @@ export default function DiffPanel({ }) : null, ); + const activeRepositoryRoot = + gitStatusQuery.data?.workingTree.rootPath ?? + (activeThread?.worktreePath ? undefined : activeProject?.repositoryIdentity?.rootPath); const diffSelection = useDiffPanelStore((state) => selectThreadDiffPanelSelection(state.byThreadKey, routeThreadRef), ); diff --git a/apps/web/src/components/chat/TimelineAssistantRows.tsx b/apps/web/src/components/chat/TimelineAssistantRows.tsx index 458d84b9e982b..3ce911b1ba3f3 100644 --- a/apps/web/src/components/chat/TimelineAssistantRows.tsx +++ b/apps/web/src/components/chat/TimelineAssistantRows.tsx @@ -23,6 +23,11 @@ import { formatChatTimestampTooltip, formatDayAwareTimestamp } from "../../times import { useV2ItemSupport } from "../../state/v2ItemSupport"; import { TimelineRowCtx, TimelineRowActivityCtx, type TimelineRow } from "./timelineRowContext"; import { MESSAGE_HEADING_LEVEL, MessageAuthorHeading } from "./TimelineRowParts"; +import { useAtomValue } from "@effect/atom-react"; +import { AsyncResult, Atom } from "effect/reactivity"; +import { vcsEnvironment } from "../../state/vcs"; + +const NO_VCS_STATUS = Atom.make(AsyncResult.initial()); export function AssistantTimelineRow({ row }: { row: Extract }) { const ctx = use(TimelineRowCtx); @@ -349,6 +354,15 @@ function AssistantChangedFilesSectionInner({ : null, ); const onFileContextMenu = useFileContextMenuHandler(ctx.activeThreadEnvironmentId); + const repositoryRoot = useAtomValue( + ctx.workspaceRoot + ? vcsEnvironment.status({ + environmentId: ctx.activeThreadEnvironmentId, + input: { cwd: ctx.workspaceRoot }, + }) + : NO_VCS_STATUS, + (status) => AsyncResult.getOrElse(status, () => null)?.workingTree.rootPath, + ); return ( Date: Thu, 12 Nov 2026 10:50:16 +0000 Subject: [PATCH 5/8] revert(web): leave the Changes card tree as on main The Changes card tree fix is on main with the same change and test. Co-Authored-By: Claude Opus 5.5 --- apps/web/src/lib/turnDiffTree.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/web/src/lib/turnDiffTree.ts b/apps/web/src/lib/turnDiffTree.ts index ee8129894aabd..7cceba0137847 100644 --- a/apps/web/src/lib/turnDiffTree.ts +++ b/apps/web/src/lib/turnDiffTree.ts @@ -32,8 +32,6 @@ interface MutableDirectoryNode { let sortCollator: Intl.Collator | undefined; -// Checkpoint paths come from Git, which separates directories with "/" on every -// platform. A backslash is part of a POSIX file name, not a separator. function normalizePathSegments(pathValue: string): string[] { return pathValue.split("/").filter((segment) => segment.length > 0); } From 68c83bac45c81dd9428ee6879ac9ce91060ae1b9 Mon Sep 17 00:00:00 2001 From: Victor Vogel Date: Thu, 12 Nov 2026 10:50:16 +0000 Subject: [PATCH 6/8] revert(web): keep the Changes card's folder check as on main It only decides whether the expand-all control shows, not which file opens. Co-Authored-By: Claude Opus 5.5 --- apps/web/src/components/chat/ChangedFilesTree.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/components/chat/ChangedFilesTree.tsx b/apps/web/src/components/chat/ChangedFilesTree.tsx index 2972e7c8b3086..5023da3a7823f 100644 --- a/apps/web/src/components/chat/ChangedFilesTree.tsx +++ b/apps/web/src/components/chat/ChangedFilesTree.tsx @@ -53,7 +53,7 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: { } = props; const syncTimelineLayout = useSyncLayout(); const hiddenFileCount = totals.count - files.length; - const hasDirectories = files.some((file) => file.path.includes("/")); + const hasDirectories = files.some((file) => /[/\\]/.test(file.path)); return (
Date: Thu, 12 Nov 2026 11:17:35 +0000 Subject: [PATCH 7/8] fix(web): diff paths under a Windows root still split on backslashes Tool-call patches can name files with backslash separators; only POSIX roots keep a backslash as part of a file name. Co-Authored-By: Claude Opus 5.5 --- apps/web/src/diffFileActions.test.ts | 12 ++++++++++++ apps/web/src/diffFileActions.ts | 24 +++++++++++++++++------- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/apps/web/src/diffFileActions.test.ts b/apps/web/src/diffFileActions.test.ts index 39b86aa7f0d75..7d57107216b35 100644 --- a/apps/web/src/diffFileActions.test.ts +++ b/apps/web/src/diffFileActions.test.ts @@ -52,6 +52,18 @@ describe("openDiffFilePrimaryAction", () => { repositoryRoot: "C:\\repo", expected: "src/index.ts", }, + { + filePath: "Frontend/src\\index.ts", + workspaceRoot: "C:\\repo\\frontend", + repositoryRoot: "C:\\repo", + expected: "src/index.ts", + }, + { + filePath: "Frontend\\src\\index.ts", + workspaceRoot: "C:\\repo\\frontend", + repositoryRoot: "C:\\repo", + expected: "src/index.ts", + }, { filePath: "Frontend/Src/index.ts", workspaceRoot: "C:\\repo\\frontend\\src", diff --git a/apps/web/src/diffFileActions.ts b/apps/web/src/diffFileActions.ts index 2dc7fa5e3c853..046baa4ce5b64 100644 --- a/apps/web/src/diffFileActions.ts +++ b/apps/web/src/diffFileActions.ts @@ -17,15 +17,21 @@ interface OpenDiffFilePrimaryActionInput { readonly openInEditor: (targetPath: string) => void; } -function normalizedRelativePathSegments(filePath: string): ReadonlyArray | null { +function normalizedRelativePathSegments( + filePath: string, + windowsRoot: boolean, +): ReadonlyArray | null { if (filePath.startsWith("/") || isWindowsAbsolutePath(filePath) || /^[a-zA-Z]:/.test(filePath)) { return null; } - // Git separates directories with "/" on every platform, so a backslash is part of - // a file name. Windows paths still treat it as a separator, so reject ".." either way. - if (filePath.split(/[\\/]/).includes("..")) return null; - const segments = filePath.split("/").filter((segment) => segment.length > 0 && segment !== "."); + // On POSIX a backslash is part of a file name; Windows treats it as a separator + // (tool-call patches can name files with either), so reject ".." either way. + const parts = filePath.split(/[\\/]/); + if (parts.includes("..")) return null; + const segments = (windowsRoot ? parts : filePath.split("/")).filter( + (segment) => segment.length > 0 && segment !== ".", + ); if (segments.length === 0) return null; return segments; } @@ -54,14 +60,18 @@ export function resolveDiffPathForWorkspace(input: { readonly workspaceRoot: string | undefined; readonly repositoryRoot: string | undefined; }): string | null { + const windowsRoot = [input.workspaceRoot, input.repositoryRoot].some( + (root) => root !== undefined && isWindowsAbsolutePath(root), + ); // Claude and ACP tool-call diffs name files by absolute path. if (isAbsolutePath(input.filePath)) { const relativePath = workspaceRelativeFilePath(input.filePath, input.workspaceRoot); - const segments = relativePath === null ? null : normalizedRelativePathSegments(relativePath); + const segments = + relativePath === null ? null : normalizedRelativePathSegments(relativePath, windowsRoot); return segments ? segments.join("/") : null; } - const fileSegments = normalizedRelativePathSegments(input.filePath); + const fileSegments = normalizedRelativePathSegments(input.filePath, windowsRoot); if (!fileSegments) return null; const workspaceSegments = repositoryRelativeWorkspaceSegments( From 0020a00632ef4f7d2f3506a62c1d1b9658661d5b Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Sat, 14 Nov 2026 21:27:26 +0000 Subject: [PATCH 8/8] test(web): keep only diff path rows that fail on main Co-Authored-By: Claude Opus 5.5 --- apps/web/src/diffFileActions.test.ts | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/apps/web/src/diffFileActions.test.ts b/apps/web/src/diffFileActions.test.ts index 7d57107216b35..8f7b2bc597b79 100644 --- a/apps/web/src/diffFileActions.test.ts +++ b/apps/web/src/diffFileActions.test.ts @@ -46,12 +46,6 @@ describe("openDiffFilePrimaryAction", () => { }); it.each([ - { - filePath: "Frontend/src/index.ts", - workspaceRoot: "C:\\repo\\frontend", - repositoryRoot: "C:\\repo", - expected: "src/index.ts", - }, { filePath: "Frontend/src\\index.ts", workspaceRoot: "C:\\repo\\frontend", @@ -64,12 +58,6 @@ describe("openDiffFilePrimaryAction", () => { repositoryRoot: "C:\\repo", expected: "src/index.ts", }, - { - filePath: "Frontend/Src/index.ts", - workspaceRoot: "C:\\repo\\frontend\\src", - repositoryRoot: "C:\\repo", - expected: "index.ts", - }, { filePath: "frontend/back\\slash.txt", workspaceRoot: "/repo/frontend", @@ -77,10 +65,10 @@ describe("openDiffFilePrimaryAction", () => { expected: "back\\slash.txt", }, { - filePath: "back\\slash/a.ts", + filePath: "back/slash/a.ts", workspaceRoot: "/repo/back\\slash", repositoryRoot: "/repo", - expected: "a.ts", + expected: null, }, ])( "resolves $filePath in $workspaceRoot",