Skip to content

TT-7621 fix: offer Record once the reference audio stops - #529

Open
nabalone wants to merge 5 commits into
developfrom
TT-7621_pbt-record-after-playback-stops
Open

TT-7621 fix: offer Record once the reference audio stops#529
nabalone wants to merge 5 commits into
developfrom
TT-7621_pbt-record-after-playback-stops

Conversation

@nabalone

Copy link
Copy Markdown
Collaborator

Fix 3 of the PBT series. Into #527.

The fault

Pausing the reference playback part way through left Record disabled with no way forward but replaying the whole clause.

allowRecord needs currentClausePlayed and phase 'recordReady', and handleRegionPlayEnd was the only thing that set them — so the step depended on a region-out that does not always come. A user pause has none by definition.

The fix

The step now treats the reference audio stopping as the signal that the clause has been heard — which is the thing the user actually did. A stop reported within 250ms of playback starting is the seek that started it rather than the audio, so those are ignored; auto-segmenting never produces a clause anywhere near that short.

Also fixes the waveform selection and the segment label disagreeing after a segment is selected, which came from the same stranded state.

Deliberately not fixed here

Record stays operable while a clicked segment plays is still red. I tried it on this branch and backed it out: the cause is a spurious park (wavesurfer emits the clicked clause's region-out during the seek, ~10ms in, so the step parks on a segment that is about to play). Suppressing that park does fix Record, but it then strands the click flow entirely — playback started by clicking a segment is not region-bounded and reports neither a region-out nor a stop, so nothing ever re-enables Record. Shipping that would trade a wrong-enabled button for a dead end.

The honest fix is at the player level: a segment click should start region-bounded playback that reports its end like playCurrentClause does. Worth its own ticket — it also means clicking a segment currently plays past it into the following audio.

Verification

  • defects spec: pause test now green
  • selection spec: 5 of 7 green (remaining: swallowed adjacent click — fixed in TT-7621 fix: act on a segment click instead of swallowing it #528 — and Record-during-playback, above)
  • behaviour specs: 13 + 16 green
  • jest PassageDetailCarefulSpeech: 24 green (this touches shared guided-record code)

🤖 Generated with Claude Code

@nabalone

Copy link
Copy Markdown
Collaborator Author

Claude opened this but I don't think we actually want this change in behavior

@nabalone nabalone closed this Aug 24, 2026
@nabalone

Copy link
Copy Markdown
Collaborator Author

Actually, asking the team

@nabalone nabalone reopened this Aug 24, 2026
@nabalone

Copy link
Copy Markdown
Collaborator Author

We decided that for now we do want to make this change and allow recording after a pause. And then when we raise it to the product owners, if they object we can back it back out

@nabalone
nabalone marked this pull request as ready for review August 24, 2026 21:05
nabalone and others added 2 commits August 24, 2026 17:55
Mounts the real Phrase Back Translate step - real wavesurfer, MediaRecord,
Orbit memory source, UnsavedProvider and upload action chain - so UI-level
faults show up: record button enable/disable, playhead position, waveform
region colours, and which segment a take is actually filed under.

Three things stand in for the real world:
- the microphone, via the existing installRecordingMocks
- the source audio, a generated WAV with silence around each segment boundary
  so region-out fires as it does on speech and auto-segment has gaps to find
- the server, via cy.intercept on the mediafile POST, the audio PUT and
  fileurl. Every upload's source-segments is captured, so "which segment did
  this take land on" is an assertion rather than a guess.

Because the server is ours, putDelayMs / fileurlDelayMs / rowDataLagMs make
the save- and load-in-flight races deterministic instead of flaky.

33 tests pass and pin down current good behaviour. 8 fail on purpose, each a
repro for a defect found while writing them, kept in their own specs so a
green run of the others still means something:
- navigating while a take loads hangs the recorder ("Loading..." sticks and
  Record is disabled on every later segment)
- clearing a take mid-upload brings the take back
- pausing the reference playback leaves Record disabled with no way out
- Fewer Segments can produce more segments
- navigation is offered while a take is still unsaved (intermittent)
- a segment click is only half applied: the waveform selection and playhead
  move but the step does not follow, so the first click on the next segment is
  ignored, the selection and label disagree, Record is operable during
  playback, and a take can be filed under the previously selected segment

Also: Cypress now launches Chrome with --mute-audio (these specs play real
audio), component specs get the node tasks so cy.task('log') works, the
harness's deps are pre-bundled to stop a mid-run re-optimize leaving two
copies of React in the AUT, and CT gets its own vite cacheDir so it and the
app dev server stop invalidating each other.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The cypress-tests job runs the whole component suite, so the deliberately-red
repro tests turned the check red on this PR. A red check that is expected to be
red teaches everyone to ignore the check.

Each known-defect test is now tagged @known-defect (per test, not per describe,
so a test does not stay excluded once its defect is fixed). cy:run-ct - what CI
runs - excludes that tag. Added cy:run-ct-known-defects to run only the repros
and cy:run-ct-all to run everything.

CI-equivalent run of the four PBT specs: 33 passing, 8 skipped, none failing.

When a fix lands it must drop the tag from the test it fixes, or that test stops
being run. The fix branches off this one already rename those tests, so their
merge conflicts resolve as "take the fix branch's version, without the tag".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nabalone
nabalone force-pushed the TT-7621_pbt-segment-selection-and-recorder-state branch from 5bef41e to 3f3ee57 Compare August 24, 2026 21:56
nabalone and others added 3 commits August 24, 2026 17:57
Pausing the reference playback part way through left Record disabled with no way
forward but replaying the whole clause.

Record needs currentClausePlayed and phase 'recordReady', and handleRegionPlayEnd
was the only thing that set them - so the step depended on a region-out that does
not always come. A user pause has none by definition. The step now treats the
reference audio stopping as the signal that the clause has been heard, which is
the thing the user actually did.

A stop reported within 250ms of playback starting is the seek that started it
rather than the audio, so those are ignored; auto-segmenting never produces a
clause anywhere near that short.

Also fixes the waveform selection and the segment label disagreeing after a
segment is selected, which came from the same stranded state. Both tests are
renamed to what they now assert.

Not fixed here, and still red: Record stays operable while a clicked segment
plays. Suppressing the spurious park that causes it turns out to strand the
click flow instead, because playback started by clicking a segment is not
region-bounded and reports neither a region-out nor a stop - so the honest fix
is at the player level, and it is not this commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Devin flagged that clausePlaybackStartedAtRef was only set by playCurrentClause,
so playback started any other way - the user pressing Play, which goes through
handleBeforeSourcePlay - left the timestamp stale. The 250ms seek-suppression
window then compared against an old time, and a stop caused by that start would
fall outside the window and mark the clause played before it had been. Correct,
and exactly the failure the window exists to prevent.

The timestamp is now re-based wherever playback begins: in handleBeforeSourcePlay
for a user-initiated play, and on the play-status change for the real start
however it was triggered, as well as in playCurrentClause for the seek.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comments and one behaviour-preserving split, no logic change.

The seek-suppression window needed explaining rather than naming: starting a
clause seeks the playhead to its start, and that seek reports a stop of its own
before anything has been heard, so elapsed playback time is what separates it
from a real stop such as a user pause.

Split `if (playingNow || !recordingPassStartedRef.current) return;` into its two
unrelated halves - one is "this is a start, not a stop", the other is "the listen
pass has nothing to record" - and said why the recording/saving guard reads refs
rather than state.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nabalone
nabalone force-pushed the TT-7621_pbt-record-after-playback-stops branch from 4751e03 to 3c433f6 Compare August 24, 2026 21:57
Base automatically changed from TT-7621_pbt-segment-selection-and-recorder-state to develop August 24, 2026 22:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant