0.9.3 — a suite that could never fail now can, Windows gets its status line back, and a dozen races close
Everything here came out of auditing the app subsystem by subsystem, so it is
mostly defects that were quiet rather than features. The two that matter most
are about the checks themselves.
The colour suite could not fail. verify-color.mts counted its failures,
printed FAIL on every failing line, and never set an exit code — so npm run check and the release gate both went green whatever it found. It is the only
automated check for the contrast maths, the theme ladder's floors and the
accent-ink derivation. And the CI job's hand-written list of suites had drifted
out of step with npm run check for the second time, so the theme generator,
file-drop quoting and the phone link were enforced locally and gated no release.
That list is derived from the check chain now, so it cannot drift again.
The statusLine shim never ran on Windows machines with Git for Windows
installed — which is the CLI's own preferred configuration. A PowerShell call
operator was being prepended unconditionally, and a leading & is a syntax
error in a POSIX shell. Those machines had no context ring, no plan-limit
figures, no activity dot, no notifications, and lost their own configured status
line too. Stoke detects the interpreter now, mirroring the CLI's own locator.
Phone access. The WebSocket had none of the size discipline the HTTP paths
were deliberately given, and a resize frame could hand node-pty a nine-digit
dimension. The bearer key stayed in the phone's URL bar and history forever
instead of being stripped once the cookie was set. And "Require Cloudflare
Access" claimed in three places to admit only tunnel traffic when it checks that
the headers are present and cannot verify their signature — it says what it does
now. Every screen also paints the desktop's real theme, rather than only the
terminal.
Races and leaks. Two Claude Code settings written at once silently dropped
one, with both reported as saved. Resume and Start again lacked the guard the
relaunch pill got in 0.9.0 — and "Resume all" followed by "Close them" would
drop every tab and then have each one reappear with a live process behind it.
"Resume all" also stole the selection to whichever session started last. A
manual "Update now" could run a second claude update beside the automatic one.
The docked browser was never destroyed with its window, and the six-hourly
update timer was never cleared, so both accumulated across close-and-reopen on
macOS. A crashed browser tab now recovers instead of sitting there dead.
Settings. Dragging any of seven sliders wrote the whole settings file
synchronously on the main thread on every tick, blocking every session's output
with it. Closing the sheet with Escape threw away whatever you were typing in an
SSH or profile field, because unmount fires no blur. And the Interface scale box
snapped the whole UI to its minimum on the first keypress, because an empty
number field reads as zero.
Colour and the meter. Hovering a primary button dropped its label below the
contrast floor on 28 of 108 theme-and-accent combinations, including one shipped
theme's own accent. The theme editor had two controls labelled "Accent colour"
writing different state, and its contrast report was silent on five of the
seventeen tokens it invites you to override. The context ring was not counting
the last reply's tokens, so it understated how full the window was between
turns — the one direction this codebase treats as dangerous.
The worklog. Rejecting a proposal is the only thing that stops the scan
offering it again, and the button for it had been left behind by an earlier
refactor: the IPC worked and nothing in the app called it. Proposals piled up
with no way to clear them while the title bar counted them and opened an
unrelated report. Reject is back, "Review all" shows everything pending, and the
copy names what it actually opens.
-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgRMU791FgMe8r+DfqyzVBFQ6kaz
8vnoPpPI8Ui7gGH3MAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
AAAAQCCE/H6B8w3TPZMqt1dZ0UHu5WULluopmvTjVgch3nRtOCytd/VQMjjbiIG/ohC3MI
xA3KtsWhVqpbtufKFsggQ=
-----END SSH SIGNATURE-----