Build out a Playwright-based regression-detection harness covering the compat-matrix surfaces (KDE-W, KDE-X, GNOME, Sway, i3, Niri, packaging formats). Adds: - Planning + decision docs under docs/testing/ — README, matrix, runbook, automation, cases/ (11 case files), quick-entry-closeout - Playwright scaffolding (config, tsconfig) - 78 spec runners under tools/test-harness/src/runners/ — T## case- doc runners and S## distribution/smoke runners - Substrate primitives in tools/test-harness/src/lib/: AX-tree loader (snapshotAx + waitForAxNode + axTreeToSnapshot), focus- shifter, eipc-registry, niri-native bridge, drag-drop bridge, electron-mocks, claudeai page-objects, inspector client S03 (DEB Depends declared) and S04 (RPM Requires declared) ship marked test.fail() — they're regression detectors for the case-doc gap (deb.sh emits no Depends:, rpm.sh sets AutoReqProv: no), and the expected-failure shape lets them report green on every host until upstream packaging starts declaring runtime deps. 127 files, no runtime changes; harness is opt-in via 'cd tools/test-harness && npx playwright test'. Co-authored-by: Claude <claude@anthropic.com>
9.9 KiB
Code Tab — Foundations
Tests covering Code-tab availability on Linux (officially unsupported per upstream docs), sign-in flow, folder picker, drag-and-drop, and the basic editing surfaces (terminal, file pane). See ../matrix.md for status.
T15 — Sign-in completes in the embedded webview
Drift in build 1.5354.0 — Sign-in is an in-app
mainView.webContents.loadURLflow, not anxdg-openbrowser handoff. Claude.ai/login renders inside the embedded BrowserView; the resultingsessionKeycookie is then exchanged at${apiHost}/v1/oauth/${org}/authorizewith redirect URIhttps://claude.ai/desktop/callback. No system browser is involved.
Severity: Smoke Surface: Auth / embedded webview Applies to: All rows Issues: —
Steps:
- Launch a fresh app instance (signed-out state).
- Click Sign in. Observe claude.ai/login rendering inside the app.
- Authenticate. Observe the in-app navigation completing back to the workspace.
Expected: Sign-in stays inside the embedded webview (will-navigate
handler Ihr keeps /login/ paths in-app). After auth the
sessionKey cookie is captured and silently exchanged for an OAuth
token via the desktop/callback redirect. Account dropdown populates;
no auth banner remains.
Diagnostics on failure: DevTools console for the mainView
BrowserView, network captures of the /v1/oauth/{org}/authorize and
/v1/oauth/token calls, launcher log, cookie jar inspection
(sessionKey on .claude.ai).
References: Code tab auth troubleshooting
Code anchors:
build-reference/app-extracted/.vite/build/index.js:141996— desktop OAuth redirect URIhttps://claude.ai/desktop/callbackbuild-reference/app-extracted/.vite/build/index.js:142431— POST to${apiHost}/v1/oauth/${org}/authorizewithBearer ${sessionKey}build-reference/app-extracted/.vite/build/index.js:216565—Ihrtreats/login/paths as in-app (not external)build-reference/app-extracted/.vite/build/index.js:141316—mainView.webContents.loadURL(...)drives the embedded sign-in
T16 — Code tab loads
Severity: Smoke Surface: Code tab — top-level UI Applies to: All rows Issues: —
Steps:
- After sign-in, click the Code tab at the top center.
- Wait a few seconds.
Expected: Code tab renders the session UI (sidebar, prompt area, environment dropdown). Per upstream docs the Code tab is "not supported" on Linux — the patched build under this project should render the UI normally or surface a clear, actionable message. Not a blank screen, infinite spinner, or Error 403: Forbidden.
Diagnostics on failure: Screenshot, DevTools console, network captures (auth/feature-flag responses), launcher log, the active patch set in scripts/patches/.
References: Use Claude Code Desktop, Get started with the desktop app
Code anchors:
build-reference/app-extracted/.vite/build/index.js:525066—sidebarMode === "code"rewrites the BrowserView path to/epitaxybuild-reference/app-extracted/.vite/build/index.js:496066— Code deeplinks (claude://code?...) navigate to/epitaxy?...build-reference/app-extracted/.vite/build/index.js:105273—IHirecognises/epitaxyand/epitaxy/...as the Code-tab pathbuild-reference/app-extracted/.vite/build/index.js:105346—sidebarModeenum contains"code"
Inventory anchor: …tablist.tab-by-name.code (role tab, label
Code) — confirms the Code tab is reachable from the new-chat tablist
in the captured idle state.
T17 — Folder picker opens
Severity: Smoke
Surface: Code tab → Environment selection
Applies to: All rows
Issues: —
Runner: tools/test-harness/src/runners/T17_folder_picker.spec.ts — runtime-attach via SIGUSR1 + main-process dialog.showOpenDialog mock + webContents.executeJavaScript to drive the renderer. Click chain to reach the folder-picker button awaits selector tuning
Steps:
- In the Code tab, click the environment pill → Local → Select folder.
- Choose a project directory.
Expected: Native file chooser opens. On Wayland sessions the chooser is xdg-desktop-portal-backed (verify with busctl --user tree org.freedesktop.portal.Desktop). On X11 sessions the GTK/Qt native picker fires. Selected path appears in the env pill.
Diagnostics on failure: systemctl --user status xdg-desktop-portal, XDG_SESSION_TYPE, the portal backend in use (xdg-desktop-portal-kde, xdg-desktop-portal-gnome, xdg-desktop-portal-wlr), launcher log.
References: Local sessions
Code anchors:
build-reference/app-extracted/.vite/build/index.js:66403— IPC channelclaude.web_FileSystem_browseFolder(renderer → main)build-reference/app-extracted/.vite/build/index.js:509188—browseFolderimpl callsdialog.showOpenDialogwithproperties: ["openDirectory", "createDirectory"]build-reference/app-extracted/.vite/build/index.js:450534—grantViaPicker(Operon host-access folder grant) uses the same["openDirectory"]shapetools/test-harness/src/lib/claudeai.ts:122—installOpenDialogMockintercepts both(opts)and(window, opts)arities, matching the call sites at index.js:509196 and :450534
Inventory anchor: root.main.region.button-by-name.select-folder
(role button, label Select folder…) — the persistent button the
T17 runner clicks before the dialog mock fires.
T18 — Drag-and-drop files into prompt
Severity: Critical Surface: Code tab → Prompt area Applies to: All rows Issues: —
Steps:
- Open a Code-tab session.
- From the system file manager, drag one or more files into the prompt area.
- Repeat with multiple files at once.
Expected: Files attach to the prompt. The renderer resolves dropped
File objects to absolute paths via the preload-bridged
claudeAppSettings.filePickers.getPathForFile (Electron's
webUtils.getPathForFile). Multi-file drops attach each file. Works on
both Wayland and X11.
Diagnostics on failure: Screen recording, wl-paste --list-types (Wayland) or xclip -selection clipboard -t TARGETS -o (X11) during drag, DevTools console, launcher log.
References: Add files and context
Code anchors:
build-reference/app-extracted/.vite/build/mainView.js:9267—filePickers.getPathForFilewrapswebUtils.getPathForFilebuild-reference/app-extracted/.vite/build/mainView.js:9552— exposed to the renderer aswindow.claudeAppSettings
T19 — Integrated terminal
Severity: Critical Surface: Code tab → Terminal pane Applies to: All rows Issues: —
Steps:
- In a Code-tab session, press
Ctrl+`(or open via the Views menu). - Confirm the terminal opens in the session's working directory.
- Run
git status,npm --version,gh auth status.
Expected: Terminal pane opens in the session's working directory, inherits the same PATH Claude sees. Standard commands run cleanly. Terminal pane is local-session-only per docs.
Diagnostics on failure: Terminal pane content, echo $PATH from inside the pane, pwd, the shell binary in use, launcher log.
References: Run commands in the terminal
Code anchors:
build-reference/app-extracted/.vite/build/index.js:69135— IPC channelclaude.web_LocalSessions_startShellPty(alsoresizeShellPty,writeShellPtyat :69184, :69210)build-reference/app-extracted/.vite/build/index.js:486438—startShellPtybody: spawnsnode-ptyinn.worktreePath ?? n.cwdwithTERM=xterm-256colorbuild-reference/app-extracted/.vite/build/index.js:486463—node-ptydynamic import (optional dep,package.jsonline 100)build-reference/app-extracted/.vite/build/index.js:259306—shell-path-worker/shellPathWorker.jsresolves the user's interactive PATH;FX()(line 259311) returns it for the spawned PTY env
T20 — File pane opens and saves
Severity: Critical Surface: Code tab → File pane Applies to: All rows Issues: —
Steps:
- In a Code-tab session, click a file path in chat or diff to open it in the file pane.
- Make a small edit. Click Save.
- Modify the file externally (e.g.
echo >> file). Re-edit in the pane. Observe the on-disk-changed warning.
Expected: File opens in the editor pane. Edits write back to disk on Save. If the file changed on disk since opening, the pane shows the on-disk-changed warning and offers override or discard. (The conflict check is sha256-based, not mtime-based — writeSessionFile reads the current bytes, hashes them, and rejects with Conflict if the renderer-supplied expectedHash doesn't match.)
Diagnostics on failure: sha256sum <file> output (and stat mtime for cross-checking), launcher log, DevTools console, screen recording of the warning state.
References: Open and edit files
Code anchors:
build-reference/app-extracted/.vite/build/index.js:68922— IPC channelclaude.web_LocalSessions_readSessionFilebuild-reference/app-extracted/.vite/build/index.js:69003— IPC channelclaude.web_LocalSessions_writeSessionFilewithexpectedHashargument at position 3build-reference/app-extracted/.vite/build/index.js:492874—readSessionFileimplbuild-reference/app-extracted/.vite/build/index.js:492954—writeSessionFileimpl: sha256-hashes current on-disk bytes, returns{ status: nW.Conflict, currentHash }whenexpectedHashmismatches