Repository navigation
feat(dictation): per-session debug-dump JSONL - #68
Merged
Merged
Conversation
Add a FAT per-press JSONL writer at <userData>/dictation-debug/<id>.dictation.jsonl, mirroring the ghostJournal shape (100ms batched appends, registry flushed on before-quit, 14-day startup prune). Wires the dictation:debug-event IPC channel and instruments every main-side site ([dictation:trace] handlers, batch upload start/end, OUTCOME success/no-speech/error/cancel) with paired CHUNK:main:received events carrying sha8 fingerprints for cross-process correlation against the renderer. Renderer instrumentation lands in the follow-up commit. Plan doc included for future readers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mint a debugSessionId at recorder construction and route every
existing debug() call site through the per-session journal in
addition to console.debug. Each call site is tagged with a
DictationDebugLayer so the JSONL can be filtered by RECORDER /
CHUNK / IPC / DEVICE / ERROR.
New emission sites:
- DEVICE: enumerate:audioinput + select:built-in / fallback
- CHUNK:renderer:produced with sha8 (pairs against main's
CHUNK:main:received for cross-process correlation — the
primary lever for chunk-drop / chunk-reorder investigations)
- AUDIO_LEVEL meter:start (track labels + muted + readyState),
sample at ~10Hz (peak + per-band levels), meter:stop
- TRANSCRIPT preview:interim / preview:final / committed
Thread debugSessionId through startDictationStream so main can
key its events to the same file. Microtask-clear the ref in
cleanup so late callbacks (transcript IPC, OUTCOME from main)
still resolve a journal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 tasks
Juliusolsson05
added a commit
that referenced
this pull request
May 11, 2026
…irks docs (#71) * feat(paste): event-driven submit + per-paste debug dump + headless quirks docs Bundles three coordinated changes addressing the long-standing "first Enter on a multi-line paste does nothing; second Enter submits" intermittent in Claude composers. Track A — documentation in claude-code-headless submodule: - Thick WHY comment above HeadlessTerminal.scheduleFlush explaining that the 'screen' event silently stops firing under Claude's synchronized-output protocol (pendingWrites stays > 0 with @xterm/headless under sync-out pressure). Consumers needing correctness should poll snapshotPlain() directly. - Stronger warning on the activity-event debouncer that the SPINNER_VERB_RE walk produces false negatives during tool-result rows and transient headers, and the 'activity' event must NOT be used as a binary "did this submit?" verdict. The paste-submit harness lost 2/10 iterations on a spurious spinner read before we figured this out. Track B — per-paste debug dump: Mirror of the dictation-debug subsystem from PR #68. Every Enter in a Claude composer mints a pasteId and emits RENDER/IPC/PTY/SCREEN/OUTCOME events into <userData>/paste-debug/<pasteId>.paste.jsonl. Renderer and main both write into the same file keyed by pasteId; payloads are sha8-fingerprinted so the IPC and PTY events can be paired across the renderer↔main boundary. 100 ms batched, 14-day prune. This is the diagnostic that will finally answer "why does the PTY-isolated harness pass 10/10 with the production 125 ms timer but the user still hits the bug live?" Hypotheses include IPC variance shrinking the effective gap, a double-fire on Enter where a second \r races the first paste's payload, or a React state mutation between paste and submit — the dump shape lets us pick the right one from a real failing session. Track C — event-driven paste-submit: Replaces the unconditional 125 ms wall-clock wait between bracketed paste and \r with a poll for Claude's `[Pasted text #N]` placeholder. The PTY-isolated harness shows event-driven is 10/10 reliable with an average wait of ~58 ms vs. the timer's 125 ms unconditional floor. Falls back to the timer after a 2 s safety bound if the placeholder never materializes (future-Claude rename insurance). Gated by settings.eventDrivenPasteSubmit, default ON. Setting also exposed in Settings → Workspace. Submodule bump: packages/claude-code-headless → feat/paste-submit-headless-fix e59235a — adds ClaudeCodeHeadless.awaitPastePlaceholder and the two doc comments above. Will need to be merged to submodule main before this cc-shell PR can merge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: bump claude-code-headless to main (PR #7 merged) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Sep 27, 2026
Merged
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.
Summary
Every dictation press now writes a FAT append-only JSONL under
<userData>/dictation-debug/<id>.dictation.jsonlcapturing thewhole pipeline:
labels/muted/readyState of the granted track
renderer:produced(with sha8) andmain:received(with sha8) — match bychunkIndexto detectdrops, reorders, or corruption across IPC
peak—the data behind the sine-wave indicator
wire when streaming returns)
Mirrors the
ghostJournalshape exactly: 100ms batched writes,registry flushed on
before-quit, 14-day startup prune. Privacycontract honored — never logs raw audio bytes or API keys; transcript
text is logged because the file is local user-private.
The goal is to diagnose the two symptoms reported: silent sine-wave
indicator and missing live-streaming preview, both happening while
the final batch transcript still comes back fine. The file lets us
distinguish "AnalyserNode reading silence" vs "meter never started"
vs "live-preview path dead (batch fallback)" without needing to
repro on a debugger.
Two commits:
main-side instrumentation, plan doc)
Test plan
<uuid>.dictation.jsonlappears under~/Library/Application Support/cc-shell/dictation-debug/.CHUNK:renderer:producedsha8values match main'sCHUNK:main:receivedsha8values for the samechunkIndex.AUDIO_LEVEL:sampleevents emit at ~10Hz withpeak > 0while speaking.
OUTCOME:successlands with the committed transcript text(truncated to 4 KB) at the end of a successful press.
OUTCOME:cancelis theterminal event for that session's journal.
🤖 Generated with Claude Code