fix(18): stop ends everything — pills clear, pump/prep failures roll the session back

Creator ruling 2026-09-01 (stuck REC pill after the failed first recording):
- StopStream clears RecordPillOn + OnAirPillOn — intent resets with reality.
- OnFramePumpFailed: dispatcher-marshalled FULL rollback via StopStream (was:
  toast + Error-status only when live — record-only sessions zombied with
  IsRecording=true over a dead encoder; the 20:34 attempt proved it). Toast copy
  says 'Recording stopped' vs 'stream pipeline stopped'.
- Same zombie class in the three go-live prep failure branches: StreamStatus.Error
  limbo replaced by StopStream() (audio loop + recording + pills unwind; a created
  broadcast still gets the close-out).
- AudioMixer.StopLive made explicitly idempotent (Stop() twice, rollback paths that
  never reached StartLive).
- Seams: OnFramePumpFailed + IsRecording setter internal (InternalsVisibleTo; test
  pattern mirrors LayoutPathOverride).
ONE integration test: SessionTeardownTests (real window + temp DB: pump death with
lit pill -> no zombie, no End button, Offline). AudioPipelineTests confirmed to hang
STANDALONE (pre-existing, the declared-known audio class) — ai.md test-count
paragraph corrected to stop claiming a suite total that cannot currently be measured.
This commit is contained in:
2026-09-01 21:15:57 -07:00
parent 688682d5b5
commit 5a1a3c566a
5 changed files with 123 additions and 18 deletions
+4 -2
View File
@@ -160,11 +160,13 @@ public sealed class AudioMixer : IDisposable
}
/// <summary>Ends the live mix loop and closes the audio pipe — ffmpeg sees
/// EOF on the audio input. Safe when not live.</summary>
/// EOF on the audio input. Safe when not live: the writer's Stop() is idempotent
/// (StopStream now calls this on every rollback path).</summary>
public void StopLive()
{
_liveCts?.Cancel();
var cts = _liveCts;
_liveCts = null;
cts?.Cancel();
_pipe.Stop();
}