Ship diagnostics behind a launch switch - #123
Conversation
The input-latency fault is rare and unpredictable, so it has to be recorded on the run where it happens. Until now that meant running a dev build from a branch, which in practice means never having it on when it matters. Adds a "Record diagnostic log" preference, off by default, that starts and stops the recorder immediately rather than at next launch. It logs event-loop stalls with the PTY output volume around them, how long each keystroke waited between leaving the renderer and being serviced, clipboard write outcomes, and whole-transcript reads — enough to tell output saturation from a blocking call from input that never arrived. Two changes were needed before this could ship rather than stay on a branch. It no longer records what you type. A terminal receives passwords, tokens, and private conversation, and the log is meant to be attached to a bug report. Control and escape sequences are kept verbatim, because an arrow key is the whole point of a latency trace and carries no content; printable runs collapse to a count. It no longer grows without bound, rotating at 5 MB and keeping one previous file, so it can be left running for weeks waiting for a recurrence. Raw PTY capture stays behind --pty-debug and is deliberately not on this switch: it records every byte the terminal shows and remains a developer tool. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A setting in everyone's Preferences window is clutter for the people who will never use it, and the recorder exists for chasing a specific intermittent fault rather than as something to leave configured. Drop the preference and start on CODEHERD_DIAGNOSTICS=1 or --diagnostics instead. Kept separate from --pty-debug in both directions: raw capture records every byte the terminal shows and stays a developer tool, while the timeline is safe enough to leave running. Raw capture does still imply the timeline, since anyone debugging that deeply wants both. Documented in INSTALL.md, recommending the environment variable over a shortcut argument on Windows: our own Squirrel handler recreates the Start Menu shortcut on update, so arguments added to it are lost at the next upgrade. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Reworked — the Preferences checkbox is gone. You wanted the capability present in a shipped build, not exposed to everyone, and a setting for chasing one intermittent fault is clutter for everybody who will never touch it. Now: One caveat worth knowing before you edit your shortcut: prefer the environment variable on Windows. Kept from the earlier version, because both still matter for a log you might attach to an issue:
typecheck clean, 198 tests (+17), build clean. |
Moves the diagnostics work off the debug branch and into
main, behind a launch switch, so the recorder is available in a shipped build without being surfaced to everyone.Why a launch switch and not a setting
The recorder exists for chasing a specific intermittent fault. A checkbox for that in everyone's Preferences window is clutter for the people who will never touch it, and implies an ongoing choice where there isn't one.
So there is no UI, no preference, and nothing in
state.json. An ordinary launch records nothing at all.On Windows, prefer the environment variable over adding
--diagnosticsto the Start Menu shortcut.squirrel-startup.tsrecreates that shortcut on--squirrel-updated, so arguments added to it are wiped at the next upgrade — silently, and exactly when you have been waiting weeks for a fault to recur.setx CODEHERD_DIAGNOSTICS 1What it records
Written to
diagnostics.login the app-data folder (Help → Open Diagnostics Folder).event-loop stalled 1240ms (output this window: 512 chunks, 210000 bytes)— the stall together with the PTY volume around it, which is what separates output saturation from a blocking callinput tab=… <ESC>[B lag=4820ms— how long a keystroke waited between leaving the renderer and being serviced in main. A large lag means main was blocked; a near-zero lag next to an unresponsive prompt moves the search downstreamclipboard.write ok len=182 attempt=1/FAILED …— and the absence of a line after a copy is itself the signaltranscript.fullread 240000000 bytes in 4200ms— the synchronous whole-file reads that stall every tab in a windowheartbeat: … in last 60s— so "no stalls" can be read against real throughput rather than silenceIt never records what you type
The version on the debug branch logged the actual characters. That was fine for a personal branch and not fine for something shipped, because a terminal receives passwords, tokens, and private conversation, and this log is meant to be attached to an issue.
Control and escape sequences are kept verbatim — an arrow key is the entire point of a latency trace and carries no content — while printable runs collapse to a count:
\x1b[B(down arrow)<ESC>[Bhunter2<7 chars>\x1b[Bpassword\r<ESC>[B<8 chars><CR>\x1b[200~secret-token\x1b[201~<ESC>[200~<12 chars><ESC>[201~It can be left armed indefinitely
Rotates at 5 MB keeping one previous file (~10 MB ceiling). It writes only when something is worth seeing — a stall, an input event, a clipboard result, or the once-a-minute heartbeat — rather than a line per sampling window, which would both flood the file and add main-process work to the very thing being measured.
Kept separate from
--pty-debug--pty-debugcaptures every byte the terminal shows, including anything secret on screen, and stays a developer tool. Sharing one switch would have made the safe thing carry the unsafe thing's risk. Tested in both directions: neither flag turns on the other. Raw capture does still start the timeline, since anyone debugging that deeply wants both.Verification
npm run typecheckclean,npm test198 passed (+17),npm run buildclean.describeInputand both flag predicates are pure and tested — arrows, SS3, SGR mouse reports, bracketed paste, named and unnamed control characters, non-ASCII, empty input, and the flag independence above.Not covered by tests: the actual recording needs a live Electron run, so it is verified by inspection. Worth confirming once with the variable set that
diagnostics.logappears and a heartbeat line lands within a minute.docs/INSTALL.mddocuments turning it on and off, what it does and does not record, and the shortcut caveat.Earlier revisions of this PR added a Preferences checkbox. That was the wrong shape — the capability needed to be present in a shipped build, not exposed to every user — and has been removed.