Harden recording durability and raise capture quality#13
Merged
Conversation
Durability (no recorded bytes lost, ever): - Never rename over an existing final recording: finalize and orphan recovery uniquify to recording-<takeId>-<suffix>-2.webm, -3, ... - fsync failures at finalize surface a durability warning through IPC instead of being swallowed; rename still proceeds - Periodic fsync (default 5s) during recording bounds data loss on OS crash; failures reported once, never kill the recording - before-quit backstop fsyncs and closes all open handles, leaving .part files recoverable - Quit guard: window close is intercepted mid-recording, user confirms, recording finalizes to disk before the window closes - Capture failures surface immediately in the recording UI; screen/mic failures auto-stop once to save what's on disk, camera failures keep recording for partial success; system-audio fallback shows a notice Quality: - VP9 preferred over VP8, warn when falling back to browser default - Camera up to 4K with bitrate scaled to actual captured resolution (12/20/30 Mbps); frame rate stays capped at 30 to protect sync - Explicit mic constraints: 48kHz stereo, no AEC/NS/AGC, with a deviceId-only retry so constrained mics still open Also updates AGENTS.md test-first guidance to be layer-calibrated, and fixes two pre-existing check failures (waveform-sources typecheck, dead invalidateTakeWaveformCache). Tests written first per AGENTS.md; full pnpm run check passes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
What changed
Implements every finding from a full recording-pipeline audit: no recorded bytes should ever be lost, and capture should run at the highest sane quality.
Durability
finalizeRecordingandrecoverOrphanRecordingnever overwrite an existing final file — they uniquify torecording-<takeId>-<suffix>-2.webm,-3, … (previously a crash → recover → re-record cycle with the same takeId silently destroyed the earlier file)..part) but now returns a durabilitywarningcarried through IPC and logged prominently in the renderer.closeAllRecordingHandlesForShutdown()onbefore-quitfsyncs + closes every open fd, leaving.partfiles intact for the existing orphan-recovery overlay.src/main/app/close-guard.ts, renderer decision helper inclose-request.ts; anti-wedge fallbacks so a broken renderer can never block quit.recorder.onerrornow show a status-line warning the moment they happen. Screen/mic failures auto-stop exactly once (re-entry-guarded) to finalize what's already on disk; camera failures warn and keep recording so partial success still saves the screen file. Silent system-audio fallback now shows a notice.Quality
computeCameraVideoBitsPerSecond(≥4K: 30 Mbps, ≥1440p: 20 Mbps, else 12 Mbps — up from a hardcoded 10). Frame rate stays capped at 30 deliberately: camera WebM is VFR and this repo has documented camera/audio sync-drift history.echoCancellation/noiseSuppression/autoGainControloff for raw capture, with a one-shot deviceId-only retry so constrained devices still open. Screen constraints and the 30 Mbps screen bitrate are untouched; start-epoch/timeslice logic untouched.Also in this PR
AGENTS.mdtest-first guidance rewritten to be layer-calibrated (tests-first for pure/main-process logic; honest e2e-or-manual verification for OS/device/MediaRecorder behavior; behavior-level checks for UI glue).pnpm run checkfailures fixed (they reproduce on cleanmain): thewaveform-sources.ts:42typecheck error (widenedgetTakePlaybackSourcesparam, behavior unchanged) and deadinvalidateTakeWaveformCachelint error.docs/production/feature-inventory.mdupdated for the quit guard.Testing
Tests were written first per AGENTS.md and confirmed failing before implementation. ~40 new tests across
tests/integration/recording-service.test.ts(rename uniquification, fsync warning, periodic fsync with fake clock, shutdown handle closing) andtests/unit/(close-guard, close-request, create-window close interception, register-handlers, preload bridge,classifyRecorderFailurepolicy, bitrate tiers, mime ordering, mic constraints). Fullpnpm run checkpasses.Reviewer notes
🤖 Generated with Claude Code