Fix flaky voice spec: take the silence meter's first reading synchronously - #194
Merged
Conversation
The "doesn't send a recording it heard nothing in" system spec was flaky in CI: the meter's first reading happened inside a requestAnimationFrame callback, so a take that ended before the browser painted a frame was judged by a meter that "never ran" — and the when-in-doubt fallback sent the silent recording to the transcriber, tripping the spec's expectation that Ai.transcribe is never called. Sampling once at setup makes the verdict independent of frame timing: reproduced deterministically by stubbing rAF to never fire (old code fails exactly like CI, fixed code passes). The suspended-AudioContext path is unchanged — a suspended context still gets no vote and the take is still sent. 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
One change in
voice_controller.js: the silence meter takes its first reading synchronously when metering is set up, instead of waiting for the firstrequestAnimationFramecallback.Why
The system spec "recording for the server to transcribe doesn't send a recording it heard nothing in" was flaky in CI (example failure:
CoPlan::Ai.transcribecalled 1 time, expected 0).The spec clicks start/stop back-to-back. The meter's
meterLivelatch was only set inside a rAF callback, so on a loaded CI box where no frame rendered between the two clicks, the take ended with a meter that "never ran". The controller deliberately treats that as "when in doubt, send it" (the suspended-AudioContext safeguard), so the silent recording went to the transcriber anyway. Any slow frame reproduced it — the failing commit's diff was unrelated.Sampling once at setup removes frame timing from the verdict entirely. In production the only behavioral change is that a take shorter than one frame is now judged by a real (silent) reading rather than sent on the benefit of the doubt — a sub-frame take contains no speech, so "didn't hear anything" is the right answer for it. The suspended-context path is untouched: a context that never ran still gets no vote, and the take is still sent (its own regression spec confirms).
How this was verified
requestAnimationFramestubbed to never fire in the spec's recorder stub, the old code fails with the exact CI message (raised fromdictations_controller.rb:109); the fixed code passes under the same condition.voice_commenting_spec.rb(20 examples) andvoice_hotkey_setting_spec.rb(3 examples) green on an isolated Postgres DB mirroring thetest-postgresjob'sdb:migratepath; the formerly flaky example green 5 consecutive runs.🤖 Generated with Claude Code