Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 10
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
Expand Down Expand Up @@ -114,6 +114,22 @@ jobs:
- name: Verify host and two viewers agree on committed session titles
if: runner.os == 'Windows'
run: node qa/session-sync.mjs
- name: Performance gate (fixture agent; animations, DOM churn, main-process stalls)
if: runner.os == 'Windows'
run: node qa/perf-gate.mjs
# One step per harness: a multi-line pwsh `run` fails only on its LAST command's exit code.
- name: Session images and attach
if: runner.os == 'Windows'
run: node qa/images.mjs
- name: Session images from a paired host
if: runner.os == 'Windows'
run: node qa/images-remote.mjs
- name: Update all Claude sessions
if: runner.os == 'Windows'
run: node qa/claude-update.mjs
- name: Restored agents start one at a time
if: runner.os == 'Windows'
run: node qa/restore-pacing.mjs
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: always()
with:
Expand All @@ -122,3 +138,7 @@ jobs:
qa/shots/resilience/
qa/shots/maintenance/
qa/shots/session-sync/
qa/shots/perf-gate/
qa/shots/images/
qa/shots/images-remote/
qa/shots/claude-update/
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ See what needs attention, resume the right conversation, and work on a paired PC

</div>

> **1.38.0** makes closing a session reliable across paired PCs, fixes stale terminal drawing, adds **Apply update** for Claude Code on Windows, protects notes from being overwritten by another device, and cuts background CPU use ([release notes](docs/releases/1.38.0.md)). Use 1.38.0 on both machines for confirmed remote close and versioned notes; 1.37.9 added confirmed session renames and task conflict protection ([notes](docs/releases/1.37.9.md)). See [the maintenance review](docs/quality-review-2026-09-08.md) and [session synchronization review](docs/session-sync-review-2026-09-08.md).
> **1.39.0** shows the screenshots and images a Claude session worked with — Claude Desktop shows them inline; DevDeck puts them in a strip beside the terminal, also for sessions on another PC — adds 📎 / drag-and-drop attachments (images travel to a remote PC), **Update all** for Claude Code on Windows, staggered session restores that no longer stall a PC, and ends the daily false "Login expired" ([release notes](docs/releases/1.39.0.md)). 1.38.0 made closing a session reliable across paired PCs ([notes](docs/releases/1.38.0.md)). See [the maintenance review](docs/quality-review-2026-09-08.md) and [session synchronization review](docs/session-sync-review-2026-09-08.md).

<div align="center">
<img src="docs/screenshots/projects.png" width="820" alt="DevDeck project overview with repository and session navigation" />
Expand All @@ -32,6 +32,8 @@ DevDeck is a local-first desktop command center for **Claude Code, Codex, and An
| --- | --- |
| Find work that needs attention | Shared sidebar for working/waiting sessions; Quick Open with `Ctrl+Shift+P`; recent projects |
| Resume without losing context | Provider-aware Open, existing terminal focus, previous-session restore, local Project Memory |
| See what the agent looked at | Image strip of a Claude session's screenshots, pasted images and written image files, with a full-size viewer — local or remote |
| Hand the agent a file | 📎 or drag-and-drop onto the terminal; images are sent to a remote PC and typed as its path |
| Track a repository | Branch, dirty files, unpushed commits, activity, notes, project pin/hide and external actions |
| Plan and inspect usage | Cross-project tasks and calendar; local token/API-equivalent cost estimates; supported live provider limits |
| Use another PC | Opt-in DevDeck Link: the agent runs on the host; the viewer sends input and receives terminal output |
Expand Down
6 changes: 4 additions & 2 deletions docs/quality.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ Results and screenshots are written to `qa/shots/maintenance/` and `qa/shots/res

## Manual harnesses

`qa:link` is a supervised two-app harness that may start installed provider CLIs. `qa:close` is a supervised two-app harness for the session close lifecycle (remote, offline, refused and bulk close, adoption focus, restart, Apply update); it starts the installed `claude` CLI in a throwaway repository and sends no prompts. `npm run qa:perf -- 4 --allow-live-agents` is a manual Windows benchmark and explicitly opts into real-agent startup. Performance QA refuses missing terminals, invisible workload, incomplete streaming/repaint activity and missing CPU samples instead of treating low CPU as success. It has not become a deterministic CI performance gate; that conversion remains a separate task.
`qa:link` is a supervised two-app harness that may start installed provider CLIs. `qa:close` is a supervised two-app harness for the session close lifecycle (remote, offline, refused and bulk close, adoption focus, restart, Apply update); it starts the installed `claude` CLI in a throwaway repository and sends no prompts. `npm run qa:perf -- 4 --allow-live-agents` is a manual Windows benchmark and explicitly opts into real-agent startup. Performance QA refuses missing terminals, invisible workload, incomplete streaming/repaint activity and missing CPU samples instead of treating low CPU as success. Its CPU percentages are for a person to read, not for CI.

`qa:images` (Windows) drives the cockpit image strip, full-size viewer and 📎 attach against an isolated home holding a transcript with a tool screenshot, a pasted image and a written image file; its fixture `claude` echoes what it is sent, so an attached path is read back off the terminal. `qa:update` (Windows) checks "Update all": a fixture `claude` prints Claude Code's failed-update footer, and the harness asserts that `claude update` ran with no session process alive and that every conversation was resumed by id. `qa:images-remote` (Windows) pairs a host and a viewer over real TLS and checks the same from the viewer: thumbnails and the full view cross the link, an attached JPEG lands on the host as PNG with the host path typed, and a non-image named `.png` or an unindexed id is refused over the link. None of these starts a real agent.
The CI performance gate is `qa:perf-gate` (Windows, every push): a fixture agent instead of a real one, and units that do not depend on the runner's speed — running CSS animations and DOM mutation records on an idle deck (both must be ~0), DOM mutations outside the terminals while two tiles repaint at 10fps (a per-chunk sidebar rebuild is ~8,000 against a limit of 400), and the main process's longest event-loop stall while working and while 20,000 lines stream. Each limit was checked by injecting the regression it guards against: an infinite CSS animation, a sidebar render per PTY chunk, and a 700ms synchronous block in the main process all fail it. Results go to `qa/shots/perf-gate/results.json`.

`qa:images` (Windows) drives the cockpit image strip, full-size viewer and 📎 attach against an isolated home holding a transcript with a tool screenshot, a pasted image and a written image file; its fixture `claude` echoes what it is sent, so an attached path is read back off the terminal. `qa:update` (Windows) checks "Update all": a fixture `claude` prints Claude Code's failed-update footer, and the harness asserts that `claude update` ran with no session process alive and that every conversation was resumed by id. `qa:images-remote` (Windows) pairs a host and a viewer over real TLS and checks the same from the viewer: thumbnails and the full view cross the link, an attached JPEG lands on the host as PNG with the host path typed, and a non-image named `.png` or an unindexed id is refused over the link. `qa:restore-pacing` (Windows) checks that restored agents start one at a time: a fixture that draws for 2s while booting must not see the next one launched before it finished (measured before the fix: 26–49ms apart). None of these starts a real agent.

Do not run these against a live development conversation. Do not equate a short loopback test with sleep/resume, VPN changes or multi-day field validation.

Expand Down
38 changes: 38 additions & 0 deletions docs/releases/1.39.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# DevDeck 1.39.0

Includes 1.38.1, which was tagged but not published separately.

## See what a session looked at

- **Image strip.** The 🖼 button in a Claude session's header shows the screenshots its tools took (Playwright, Windows MCP, Blender and others), the images you pasted, and the image files it wrote — newest first, between the header and the terminal. Click one for a full-size view: ←/→ step through them, Esc closes, Save keeps a copy. Images that arrived while the strip was closed show as **+N** on the button.
- It reads the session's own transcript, where those screenshots exist and nowhere else, and only what was added since the last look (a 133 MB transcript: 100 ms the first time, about 1 ms after).
- Works for a session on another PC: thumbnails are made on that PC, so a 4K screenshot crosses the connection as a ~30 KB preview.
- Clicking an image path in a remote session's terminal now shows the image instead of "that file is on another PC".

## Attach files

- **📎** in the session header, or drag files onto the terminal. On this PC the file's own path is typed into the prompt.
- For a session on another PC, images (PNG, JPEG, GIF, WebP, BMP) are sent there as PNG — scaled down if needed — and the path on that PC is typed. Anything that could not be attached is reported.

## Claude Code updates on Windows

- On Windows, Claude Code cannot update itself while any session is running it, so with several sessions open its background update keeps failing. When a session shows "Auto-update failed", or "Update installed" while other sessions are open, the header offers **Update all (N)**: it waits for running turns (and permission prompts) to finish, stops every Claude session on this PC, runs `claude update`, and reopens each conversation. Sessions are saved first, so quitting DevDeck in the middle loses nothing.

## Fewer freezes when sessions come back

- Restored sessions now start one at a time, each after the previous one has finished starting. Before, a reboot, an update or **Restore all** started every agent at once. One `claude --resume` of a large conversation was measured at 1.6 GB and ~10 CPU-seconds while starting, four at once at 6.6 GB and 88 CPU-seconds within five seconds — enough to stall a PC for minutes.

## Usage footer

- No more daily "Login expired". Claude's access token lasts about eight hours and is renewed by Claude Code on its next reply; while the 30-day refresh token is still valid the footer now says **Awaiting refresh** and keeps the last numbers, with no sign-in prompt. Sign-in is offered only when the refresh token is gone or rejected. *(1.38.1)*

## Security

- A paired device with only "observe" permission can read just the images a session's own record lists. Image files are checked by their contents, must really be inside a project folder (symlinks and junctions are resolved), and full-size images are kept small enough that they can never drop the connection.

## Under the hood

- New automated checks run on every change: a performance gate (no animation or DOM churn on an idle deck, no main-process stalls while streaming — each limit verified by injecting the regression it guards against), the image strip locally and across two paired PCs, attach, Update all, and staggered restores.
- TypeScript 7, Vitest 5, Playwright 1.64; `@types/node` now matches Electron's Node 24.

The image strip, attach and remote image viewing need 1.39.0 on the PC running the session; with an older host the strip stays empty and remote image paths keep the old message.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@
"qa:close": "npm run build && node qa/close.mjs",
"qa:resilience": "npm run build && node qa/resilience.mjs",
"qa:perf": "npm run build && node qa/perf.mjs",
"qa:perf-gate": "npm run build && node qa/perf-gate.mjs",
"qa:maintenance": "npm run build && node qa/maintenance.mjs",
"check:docs": "node scripts/check-docs.mjs",
"qa:session-sync": "npm run build && node qa/session-sync.mjs",
"qa:images": "npm run build && node qa/images.mjs",
"qa:images-remote": "npm run build && node qa/images-remote.mjs",
"qa:update": "npm run build && node qa/claude-update.mjs"
"qa:update": "npm run build && node qa/claude-update.mjs",
"qa:restore-pacing": "npm run build && node qa/restore-pacing.mjs"
},
"build": {
"appId": "com.soursea.devdeck",
Expand Down
Loading
Loading