Pin the system-audio stop tail handoff in tests - #1749
Conversation
The recording-end tail drop in the Core Audio tap change is fixed in 4765e68: stop arms the attempt's tail admission before the recording generation advances, finishes and drains the backend instead of cancelling it, and AudioStopCleanup closes the system writer on the same serial file queue behind the drained writes. Nothing pinned that wiring. CoreAudioSystemAudioCaptureTests covers the backend drain and the admission window in isolation, but no test exercised Audio.stop's ordering, and no CI job records real audio, so moving either line would silently truncate the end of every saved meeting. Add SystemAudioStopTailHandoffTests: an end-to-end pass through the real AudioStopCleanup scheduler asserting a queued ring tail reaches the WAV before the writer closes, the cancellation counterpart asserting a superseded attempt still discards its tail, and a source-order contract for the two orderings inside Audio.stop that no unit test can observe. Also correct the stale "Deferred P2" bullet in the probe README, which still described the tail drop as unresolved. No production code changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NwYbJHm2aXJogYcYKEps2s
|
CI note on The job aborted with signal 6 out of the XCTest stall detector, in Why it isn't mine:
No fix exists to port: the abort is a main-queue stall under load in a time-based test, not an assertion failure, and I can't reproduce it — these sessions have no Swift toolchain. I'd rather not push a speculative change to an unrelated test I can't run. I don't have permission to re-run the job ( Final state of this commit: Generated by Claude Code |
macOS verification follow-up — September 21Pushed test-only hardening in
Fresh hosted CI is running on the pushed head. Hardware skips are not passes. Fresh TCC, signed upgrade, real routes/calls and final release packaging remain separate gates. Release-health/product-task checks above are deterministic fixtures, not live fleet evidence. No merge or release performed. |
|
Updated this test-only branch with its permission-branch base via normal merge facd0bf (no force push). The PR remains test/doc-only relative to its updated base. All eight focused stop-tail and metering tests pass locally on the new head; fresh hosted checks are pending. This does not establish hardware or release readiness. |
(cherry picked from commit ca818b2290a0ca79cc4a912a125c6390c9255bb3)
(cherry picked from commit a2011b0)
|
Reconciled this companion PR with the updated audio-permission base at d73e93f using a normal merge (4fab65f). The remaining PR diff is still the stop-tail tests and their documentation. Focused Core Audio plus stop-tail tests pass: 27/27. The standalone base permission suite also passes 287/287. New exact-head hosted CI is pending; fresh permission, denial/re-enable and old-grant upgrade tests remain open. No merge or release performed. |
Requested by Justin · project thread
Why
Before: #1747's release-hold checklist still lists the recording-end tail drop as an open bug — "PCM queued in the new backend ring can be dropped when host writer admission closes at stop" — and
experiments/audio-only-probe/README.mdstill carries the matching "Deferred P2" bullet. A reader of either concludes the last slice of every meeting is still being lost.After: the checklist item's first half is done and the docs say so. The bug was fixed in 4765e68 on that same branch; what was missing is anything pinning the fix, and the independent review the checklist also asks for.
I traced the whole stop path before writing anything, because the checklist wording was the only description I had. The tail survives because three things line up in
Audio.stop():finishingCapture?.beginFinishing()runs beforebeginRecordingSessionGeneration(). After the generation advances, every buffer the backend's 10 ms consumer timer delivers arrives at the host with a stale generation, and the finishing handoff is the only path that still writes them. If admission were armed later — insidestopSystem, which is dispatched asynchronously — everything delivered in that window would be dropped with no diagnostic.stopSystemcalls the attempt'sfinishAndDrain(), notcancel(). Cancellation deliberately discards queued PCM, which is correct for a superseded attempt and wrong for a normal stop.AudioStopCleanupcloses the system writer on the same serial file queue the tail writes were enqueued on, afterstopSystemreturns. Reverse those and the drained tail is written to a closed file.Each is a one- or two-line ordering inside a 3.5k-line file.
CoreAudioSystemAudioCaptureTestscovers the backend drain and the admission window in isolation, but nothing covered the wiring, and no CI job records real audio, so a reorder would silently truncate every saved meeting.Product Impact
meetingsmeeting reliabilityWhat changed
Tests/TranscriptedCoreTests/AudioTests/SystemAudioStopTailHandoffTests.swift:testStopCleanupWritesTheDrainedRingTailBeforeClosingTheWriter— drives a realSystemAudioCaptureStartAttemptover a hookedCoreAudioSystemAudioCapture, with a callback shaped likeAudioFileManager's, through the realAudioStopCleanup.schedule. Two buffers sit in the ring when Stop is pressed; both must be in the WAV once cleanup completes.testCancelledAttemptDiscardsItsRingTail— the counterpart, so a future "fix" for the tail can't start letting a superseded attempt append to a file a successor has moved on from.testStopArmsTheTailHandoffBeforeAdvancingTheRecordingGeneration— a source-order contract overAudio.stop(), since no unit test below that level can observe the order of statements in it.experiments/audio-only-probe/README.mdand noted the new pin in the hardening section.No production code changes.
How I checked it
This is the part that needs saying plainly: none of the Swift checks ran. These sessions are Linux containers with no Swift toolchain, and this repo is macOS 26 + Apple Silicon only, so the new test has never been compiled anywhere. CI on
macos-26is the first real compile — thespm-testsjob runsswift test, which picks the new file up automatically (AudioTestsis a directory-based SPM target).scripts/dev/agent-preflight.shpython3 scripts/dev/check-build-source-lists.py— passedbash build.sh --no-open— cannot run herebash run-tests.sh— cannot run herebash run-integration-smoke.sh— cannot run hereswift test— cannot run here; this is the job that matters, left to CIRisk Review
.agent-review/visuals/evidence — no UI changesNotes
Targets
codex/system-audio-only-permissionrather than adding a commit to it, so #1747's own history stays yours.Two adjacent gaps I found while tracing and deliberately did not touch, since the brief was to keep this narrow:
micAudioWriteBackpressure.close(...)runs at the top ofAudio.stop(), but the mic tap is torn down later on a global queue. Mic buffers arriving in that window hitcase .closed:inAudioFileManagerandreturn— no finishing handoff, no diagnostic. This is pre-existing onmain, not introduced by Use audio-only system capture and add Meetings plus menu #1747, and the window is however long the dispatch hop takes. Worth its own issue.cleanupAbandonedSetup()can race a normal stop. If Stop lands immediately afterstartIfNotCancelledreturns, the setup path'ssessionIsCurrent()check fails and callscaptureAttempt.cancel(), which closes tail admission andstopSync()s the backend — discarding the ring thatAudio.stop()had just armed for draining. Only reachable in the first moments of a recording, so the tail at stake is tiny, but it is a real ordering hole.Neither blocks the remaining checklist items, which are the fresh-install, old-grant-upgrade, denial/recovery, route/sleep-wake and packaging checks that need a human at a Mac.
Agent handoff
COORD_DONE: BRIEF | https://github.com/r3dbars/transcripted/pull/1747 | tail-drop confirmed already fixed in 4765e68, added a host-wiring regression pin + source-order contract, corrected stale docs | none | whether to file the two adjacent gaps above as issues | agent-preflight + check-build-source-lists only; no Swift toolchain in session, swift test left to CI | read CI on this branch, then take the remaining #1747 checklist items to a Mac🤖 Generated with Claude Code
https://claude.ai/code/session_01NwYbJHm2aXJogYcYKEps2s
Generated by Claude Code