Repository navigation
release: 1.38.0 — reliable session close, terminal drawing, Apply update, performance - #46
Merged
Merged
Conversation
- removeMachine no longer resurrects the machine: closing the link fired onClose -> scheduleRetry before the entry was deleted, so it redialed, re-saved the known host and kept delivering sessions. Entries are now marked removed and every callback checks it is still current. - A notify may only run `send` methods (it was running invoke methods such as cockpit:open with no rate limit); non-input notifies share a token bucket, async rejections are caught. - Only the session channels a host is allowed to push reach the renderer; a host can no longer forge `link:sessions` for another machine (or for 'local'). - Pre-auth limits: at most 8 unauthenticated connections, 64 KiB frames until ready, TLS handshake timeout, attach/detach only from a paired device; the frame decoder no longer copies O(n^2). - Audit rows from refusals and strangers are throttled so they cannot evict the history. - A hello->ready deadline so a host that stalls after TLS cannot wedge the dial forever. - An exited session is dropped from every connection's attached set (it kept hasRemoteViewers true forever, blocking idle shutdown); attach announces only when something changed. - Port-mapping renewals stop when the server they were started for is gone. - An already-paired device sending `pair` keeps its permissions; evicting the oldest device past the cap is audited. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YTT8PjCsRNq19Rc53mSSta (cherry picked from commit f924b93970bf80bd5b29c704714ede01d234778b)
…lling work on a crash
- Usage: two scans resuming the same grown transcript folded into the CACHED digest's objects,
so concurrent scans (deck + usage view, or a remote viewer) added the new lines twice, for good
(200,100 -> 400,100 tokens until restart). Folds go into copies and are serialized per file;
the same fix for the per-session meta reader.
- Windows: closing a session now kills its process tree (taskkill /T, then node-pty's kill).
Measured: node-pty's console-only kill left a hidden child (the shape of an MCP server or a
background dev server) running; the tree kill ends it.
- A renderer crash no longer kills every terminal: the reload re-adopts the running sessions, so
only DevDeck's own login shells are reaped. Measured: both sessions survive a forced renderer
crash and come back as the same two tiles.
- git runs with GIT_OPTIONAL_LOCKS=0 / GIT_TERMINAL_PROMPT=0, so the 30-45 s status polls stop
taking index.lock out from under the agent's own git commands.
- AI summaries: no shell:true (it corrupted `--mcp-config '{"mcpServers":{}}'` on Windows), a
timed-out summarizer's whole tree is killed, failures are reported.
- A resume id that fails validation is reported as null instead of naming a conversation the
`-c` fallback never opened.
- state.json rename retries on EPERM/EBUSY/EACCES; a failed shutdown.exe returns the scheduler to
disarmed instead of counting down to nothing; at most 32 project terminals at once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YTT8PjCsRNq19Rc53mSSta
(cherry picked from commit d5c47a164d90ad42c62c42d07559c49124470ef6)
…ls correctly; apply agent updates Close, measured on two paired instances before and after (qa/close.mjs, 22 checks): - Closing at one machine left an "exited" tile on the OTHER one, which was saved, came back as a previous session, and was re-run by an update restart. The exit now says it was a close (and from where), and every deck showing the session follows it. - A bulk close brought all but one of the closed sessions back as dead tiles: each close makes the machine announce its sessions, and the first announcement still listed the rest, which adoption read as "started elsewhere". Closed ids are tombstoned until the machine stops listing them. - A remote close was a fire-and-forget notify. Offline, it vanished and the tile was adopted back on reconnect with the agent still running; without the `control` permission it looked like it worked. A new `cockpit:closeSession` answers: offline closes are kept and delivered on reconnect, refusals put the tile back and say why. Older hosts get the old notify. - Update restarts and the restart counter skip tiles whose terminal already ended. - Sessions another machine starts no longer take the selection and the keyboard; "+ New session" on a remote tile opens on that machine; reopening a project only dedupes against tiles on the machine being opened; Enter on a focused Cancel cancels; a failed restart keeps the tile as a saved entry; a restart waits for the old agent to exit and starts a never-written conversation fresh under the same id instead of failing to resume it. Rendering: tiles draw with xterm's DOM renderer again. Under the WebGL addon Claude Code's screen built up stale cells - old status lines stacked down the screen, earlier text showing through where new text had spaces - while the buffer was right: minimize/restore, which only makes xterm redraw every row (measured: no pty resize at all), cleared it every time. WebGL had left the GPU process's cost unchanged when it was measured. Agent updates: Claude Code shows "Update installed · Restart to apply", and its in-session /restart exists only on macOS and Linux (gated on platform), so on Windows the update waited for a manual /exit. The tile header now offers "Apply update": the restart keeps the conversation, and while a turn is running it waits for the turn to end. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YTT8PjCsRNq19Rc53mSSta (cherry picked from commit d0162e01cf381057033255e66b97237a4ecf5c08)
…e; add qa:close `npm audit --audit-level=moderate` (the CI gate) failed at HEAD on advisories published since the last release: Electron 43.3 (sandbox inheritance, protocol-handler CORS, preload cache poisoning; fixed in 43.4.1/43.5.0) and, through electron-builder's @electron/get, global-agent 3 -> roarr -> sprintf-js (no fixed sprintf-js exists; global-agent 4 no longer depends on it), pinned by override. Now 0 vulnerabilities. Verified on 43.8: unit tests, qa, qa:close, qa:link, and a packaged win-unpacked build whose embedded terminal opens, streams and closes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YTT8PjCsRNq19Rc53mSSta (cherry picked from commit 340d2bbb06506caef6e9bf34ccf66af532dbb7d6)
…ine-scoped project state - Measured (12 sessions, 6 working off screen): any running spinner makes Chromium redraw the window every step, ~6% of a core whether one spinner turns or six. Spinners now tick 4x/s instead of 10x and pause while the window is unfocused (all-frozen measured 6.8% vs 13.1%). - qa/perf.mjs: its own rAF frame counter ran through every process-CPU window (idle read 6.8%, real 0.5%); it now runs only while a JS profile is taken. - Task board / project memory Open went to THIS machine for a remote project; project activity, badges, providers and sidebar project signals are now per machine. - A size change's ConPTY repaint no longer counts as agent activity (it flipped idle sessions to working); background refreshes no longer rebuild the header rename editor mid-typing; find bar follows a language switch; view mode is not reverted by a slow reload; tray overdue count stays this machine's; due dates save on blur/Enter; adding a single-project folder as a location no longer turns it into a scan root; the login terminal is not shadowed by the usage modal's keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YTT8PjCsRNq19Rc53mSSta (cherry picked from commit 39354df1770c5a0f100be2d6c906759693da7287)
…write and launch gaps Performance (measured on synthetic stores at the user's scale: 100 projects, ~520k transcript lines, 5,000 Codex rollouts; "block" = longest main-thread stall): - Codex session re-resolution walked all 5,000 rollouts per project every TTL: 18.8 s -> 277 ms for 100 projects (the deck pass's head cache is reused). Per-tile drift stat pass ~230 ms -> 32 ms. - codexUsageScan re-read, deep-copied and rewrote its 4.5 MB index synchronously on every scan: block 84-99 ms -> 6-11 ms (kept in memory, written async only when it changed). - Tail search for the last user message: an O(n^2) 8 MB sync read, 43.6 ms block -> 1 ms cached, 6 ms on a miss (async, incremental, keyed by file size+mtime). - Active time is an incremental tally instead of copy+sort of every timestamp on every scan: warm usage scan 57-90 ms -> 23-30 ms, heap 40 MB -> 21 MB; session meta tick 3-5 ms -> ~1 ms. - `where` for the Codex CLI cached (44-56 ms sync -> 0.1 ms); session file listing async; `projects:list` shares the build already running (git once for N concurrent callers). Fixes: - `cockpit:liveSessionId` (observe permission) could rewrite which conversation any terminal here announces; only this machine's window may record, on a terminal of the asked project/provider. - Notes: versioned `project:saveNote` (old `setNote` kept for older peers, and it bumps the revision) so a note saved on another device is no longer silently overwritten. - Paired machines may write only to projects this machine actually scanned (the allowlist is a string check); pasted images from them are capped per hour; their usage scans run one at a time. - Session counts were the length of a list capped at 5; counted from the store now. - Open in VS Code expanded %VAR% inside the folder name (shell:true) - escaped and verbatim now, verified byte-exact through a real .cmd shim; wt args escape `;`; openFolder opens folders only; `git init` is bounded; the in-progress task is read from the newest 50 task files, not the oldest. - Tray menu and tooltip take their words from the window, in the app's language. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YTT8PjCsRNq19Rc53mSSta (cherry picked from commit 8826a51a4da6f418f2519bdab11ca1769f3ebc10)
…d notes; i18n and a11y - A running CSS animation keeps Chromium producing frames whether or not a step is due. A/B in one app run, 12 sessions with 6 working: CSS spinner at 10 steps/s 16.0% of a core, 4 steps/s 12.3%, 1 step/s 9.0%; a 250 ms JS tick setting a custom property 5.5%, nothing turning 4.6%. The spinner now turns on that tick (paused when unfocused, hidden or reduced-motion); the shipped build measured 3.6% in the same scenario. - Note editor saves against the revision it read; on a conflict both notes are shown to reconcile, never one erased. Max length shown on the box. - Owed remote closes survive an app restart. - Hard-coded English (project menu, checkboxes, chart title) localized; shutdown history dates use the app language; per-project checkbox names, aria-pressed toggles, calendar day labels, keyboard edit of task text. - qa: the update-button scenario retries Enter on the trust prompt; the spinner check verifies the spinner actually turns rather than that a CSS animation exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YTT8PjCsRNq19Rc53mSSta (cherry picked from commit 5c45a1b1ee0ff71273260052329f6ae349c37e96)
Release notes, README highlight and compatibility, state-consistency contract for notes and session close, backlog (note concurrency done), qa:close documented as a supervised harness. Gates on this commit's tree: tsc 0, vitest 1420 passed, check:docs, qa:maintenance, qa:resilience, qa:session-sync, qa:audit (0 violations), qa (0 console/page errors); supervised qa:close (22/22) and qa:link (0 failing checks). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YTT8PjCsRNq19Rc53mSSta
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
User-visible change
See docs/releases/1.38.0.md. In short:
exitedtile on the other. That tile was persisted and re-run by update auto-restore.controlpermission was silently dropped, and the session was re-adopted on reconnect.cockpit:closeSession), owed closes are delivered on reconnect (and survive restart), closed ids are tombstoned against re-adoption, and exits carryclosed/closedBy./restartis macOS/Linux-only. DevDeck now detects "Update installed · Restart to apply" and restarts the session on the new version with the same conversation.%VAR%/;safe launches: verified byte-exact through a real.cmdshim.npm auditreports 0.Verification
Windows 11, Node 22.16 (local), Electron 43.7.11, on this branch's tree:
tsc --noEmit: 0 errors.vitest: 1420 passed, 1 skipped.check:docs,qa:maintenance,qa:resilience,qa:session-sync,qa:audit(0 violations),qa(0 console/page errors): all pass.qa:close(new, two apps, 22 checks). Failed on the baseline for every close scenario above.qa:link: 0 failing checks.qa:perf -- 4 --allow-live-agents: PASS.win-unpackedbuild opened, streamed and closed a real PTY.Compatibility and privacy
cockpit:closeSession: an older host answers "no such method" and the viewer falls back to the old notify.project:saveNote: an older host falls back tosetNote, which also bumps the revision on a new host.noteRevisionfield per project, and owed remote closes in renderer localStorage.Documentation and remaining work
docs/state-consistency.md,docs/backlog.md(note concurrency done), anddocs/quality.md(qa:close).🤖 Generated with Claude Code
https://claude.ai/code/session_01YTT8PjCsRNq19Rc53mSSta