Status: fix already written and committed locally on fix/pad-false-positive — deliberately NOT pushed yet. This issue exists so the known-bad behaviour on main is recorded and the work is recoverable.
The bug
PadRatioMonitor labels healthy recordings as compromised. Device-observed on the first real firing since the detector shipped (2026-08-11, "Intro Call - Hamilton Barnes", 30 min, speaker only, no device changes).
The user got:
Transcription Complete — capture anomalies
115944-Intro Call - Hamilton Barnes.json — 1 capture anomaly recorded; audio may be affected
The recording was fine. Measured on the system track:
| window |
zero-sample ratio |
| 0–30 s |
97.6% |
| 30–60 s |
18.2% |
| 60–300 s |
2.8% |
| whole file |
4.2% |
48 kHz throughout, no rate drift, no chipmunk. The call audio simply started ~60 s after recording began. The diagnostic recorded excessivePadding with ratio 0.911, padded 27s of 30s, judged at exactly t=30 s — on nothing but leading silence.
Root cause
A property of the Core Audio tap that ScreenCaptureKit does not share: the tap delivers no buffers at all while the output device is idle. SCK keeps delivering zero-filled buffers (the #86 liveness probe depends on that), which is why this was not anticipated. Before a call connects there is genuinely nothing to capture, so timelineSilencePad fabricates the whole span and every one of those frames counts toward the ratio.
The 15 s absolute floor added during review to prevent exactly this class of false positive does not help: leading silence trivially exceeds any absolute floor.
Starting a recording before joining the call is the ordinary way to use this app, so this fires on the common case — which is the failure mode that makes a warning worthless. See gotcha #62 on why a label that appears every time destroys the signal it carries.
The fix (written, on fix/pad-false-positive @ 5b738b4)
Not a bigger threshold — the right distinction: padding before a track has ever delivered a frame is a start offset, not a deficit. Nothing went missing because there was nothing to capture. PadRatioMonitor accumulates nothing until its first dataFrames > 0.
#58 detection is preserved exactly: in a genuine rate drift the device IS delivering (a call is connected, so silence still arrives as zero-filled buffers at the wrong rate), so the monitor starts when the frames do. Both cases are pinned by tests — leadingSilenceBeforeTheCallStartsIsNotCorruption and deficitAfterDataStartsStillFires, the latter also asserting the leading silence does not inflate the ratio.
Recorded as gotcha #65 on that branch.
Also on that branch (9745c66) — the error-message taxonomy
Same session surfaced a second reporting bug, unrelated to capture. A summary request timeout was reported as "Couldn't read the transcript or write the summary — check disk space and permissions", because MeetingSummarizer.runSummary's catch-all assumed any non-SummaryError was a file failure and URLError isn't one. That misdirection cost two separate debugging sessions (2026-08-04 and 2026-08-11) chasing a permissions problem that never existed.
The branch adds explicit URLError handling (timeout / unreachable / other), classifies 401-403 as SummaryError.authenticationFailed with a message that names the API key and deliberately omits the response body (some servers echo the offending credential back, and this text reaches a notification that can be on screen during a shared meeting), and raises the summary timeout to a configurable 600 s default (#173). Gotcha #66.
881 tests across 100 suites pass on the branch.
Impact while unfixed
main ships the detector as-is, so recordings started before the call connects — most of them — will be labelled "capture anomalies". The transcripts and audio are unaffected; it is purely a false warning. The risk is habituation: once the label is routine it stops being read, and the whole point of #58's surfacing work was that a compromised recording must not look like a clean one.
Related
#175 (the work that introduced this), #173 (timeout), #178 (summary reliability), #176 (other #175 follow-ups).
Status: fix already written and committed locally on
fix/pad-false-positive— deliberately NOT pushed yet. This issue exists so the known-bad behaviour onmainis recorded and the work is recoverable.The bug
PadRatioMonitorlabels healthy recordings as compromised. Device-observed on the first real firing since the detector shipped (2026-08-11, "Intro Call - Hamilton Barnes", 30 min, speaker only, no device changes).The user got:
The recording was fine. Measured on the system track:
48 kHz throughout, no rate drift, no chipmunk. The call audio simply started ~60 s after recording began. The diagnostic recorded
excessivePaddingwithratio 0.911, padded 27s of 30s, judged at exactly t=30 s — on nothing but leading silence.Root cause
A property of the Core Audio tap that ScreenCaptureKit does not share: the tap delivers no buffers at all while the output device is idle. SCK keeps delivering zero-filled buffers (the #86 liveness probe depends on that), which is why this was not anticipated. Before a call connects there is genuinely nothing to capture, so
timelineSilencePadfabricates the whole span and every one of those frames counts toward the ratio.The 15 s absolute floor added during review to prevent exactly this class of false positive does not help: leading silence trivially exceeds any absolute floor.
Starting a recording before joining the call is the ordinary way to use this app, so this fires on the common case — which is the failure mode that makes a warning worthless. See gotcha #62 on why a label that appears every time destroys the signal it carries.
The fix (written, on
fix/pad-false-positive@5b738b4)Not a bigger threshold — the right distinction: padding before a track has ever delivered a frame is a start offset, not a deficit. Nothing went missing because there was nothing to capture.
PadRatioMonitoraccumulates nothing until its firstdataFrames > 0.#58 detection is preserved exactly: in a genuine rate drift the device IS delivering (a call is connected, so silence still arrives as zero-filled buffers at the wrong rate), so the monitor starts when the frames do. Both cases are pinned by tests —
leadingSilenceBeforeTheCallStartsIsNotCorruptionanddeficitAfterDataStartsStillFires, the latter also asserting the leading silence does not inflate the ratio.Recorded as gotcha #65 on that branch.
Also on that branch (
9745c66) — the error-message taxonomySame session surfaced a second reporting bug, unrelated to capture. A summary request timeout was reported as "Couldn't read the transcript or write the summary — check disk space and permissions", because
MeetingSummarizer.runSummary's catch-all assumed any non-SummaryErrorwas a file failure andURLErrorisn't one. That misdirection cost two separate debugging sessions (2026-08-04 and 2026-08-11) chasing a permissions problem that never existed.The branch adds explicit
URLErrorhandling (timeout / unreachable / other), classifies 401-403 asSummaryError.authenticationFailedwith a message that names the API key and deliberately omits the response body (some servers echo the offending credential back, and this text reaches a notification that can be on screen during a shared meeting), and raises the summary timeout to a configurable 600 s default (#173). Gotcha #66.881 tests across 100 suites pass on the branch.
Impact while unfixed
mainships the detector as-is, so recordings started before the call connects — most of them — will be labelled "capture anomalies". The transcripts and audio are unaffected; it is purely a false warning. The risk is habituation: once the label is routine it stops being read, and the whole point of #58's surfacing work was that a compromised recording must not look like a clean one.Related
#175 (the work that introduced this), #173 (timeout), #178 (summary reliability), #176 (other #175 follow-ups).