Skip to content

Don't let a half-read batch transcript bury a whole live one - #471

Open
Optic00 wants to merge 1 commit into
stenolabs:mainfrom
Optic00:fix/transcript-coverage-gate
Open

Don't let a half-read batch transcript bury a whole live one#471
Optic00 wants to merge 1 commit into
stenolabs:mainfrom
Optic00:fix/transcript-coverage-gate

Conversation

@Optic00

@Optic00 Optic00 commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Description

The onnx backend skips a window whose recognize() raises, on purpose: one bad window shouldn't fail a whole meeting. But the transcript then covers less audio than the recording holds, and nothing said so.

The live-transcript rescue (#207) only fires when the batch failed or came back as the exact silence sentinel. A half-read batch is neither, so it passed the gate and silently replaced a complete live transcript with a full-of-holes one. No error, just less text.

This reports how much audio the surviving windows covered, rolls it up across channels, and lets the rescue fire when it falls under half.

Measured in seconds of audio, not windows. Windows overlap and the last one is short: on 61 s they are [0,60) and [45,61), so losing the first leaves 16 seconds while a 1-of-2 count would report half. A window counts only once it has contributed — counting at recognize() time called a window later discarded for unusable timing a success. Channels emptied by bleed handling don't vote, since what their transcription missed says nothing about the transcript built from the other one.

Keyed on coverage, not length: the length threshold this gate used to have was removed because it replaced correct-but-short transcripts.

Platform scope: only the onnx path (Windows/Linux) can lose a window quietly. parakeet-mlx has no per-chunk except, so on macOS a bad window fails the whole call loudly and is already handled. A backend that does no windowing reports None, and None stays "unknown" rather than counting as complete.

Type of Change

  • Bug fix

Testing

  • 526 backend unit tests passing (python -m unittest discover tests), 12 of them new: the duration-based metric (losing the first window is not the same loss as losing the last), a window dropped for mismatched token/timestamp lengths not counting as read, the worst-channel roll-up ignoring channels that report nothing, and the gate from both sides.
  • ruff check clean on every file touched, no new findings against main.
  • The existing live-fallback test restated the production condition instead of calling it, so it could stay green while process_streaming changed underneath. The decision now lives in _unusable_batch_reason and the test calls it.

Additional Notes

The 0.5 threshold is a judgment value, not a measured one, and deliberately low — a meeting that lost a window or two is still better than the streaming text. Happy to move it.

Not covered here: when coverage lands between the threshold and 1.0, the batch still wins silently. Filling only the gaps from the live transcript would be better, but needs timestamps in the live snapshot.


Summary by cubic

Prevents a partially read ONNX batch transcript from overwriting a complete live transcript. We now compute window coverage and fall back to the live transcript when coverage is below 50% (measured in seconds of audio).

  • Bug Fixes
    • Compute window_coverage in ONNX windowed runs using overlap-aware covered seconds; include in result dicts and roll up across channels by the worst reported value. Channels with no surviving segments don’t vote.
    • Add _unusable_batch_reason to gate the live fallback on: batch failure, exact silence sentinel, or low coverage; None coverage is treated as unknown (e.g., whisper.cpp, parakeet-mlx) and doesn’t penalize.
    • Log coverage shortfalls; propagate coverage through transcriber.py and simple_recorder.py; remove length-based fallback to avoid replacing correct-but-short transcripts.
    • Tests cover coverage math, discarded-window handling, non-windowed backends reporting None, worst-channel roll-up, and the new fallback predicate.

Written for commit 3cb0070. Summary will update on new commits.

Review in cubic

…ript

The onnx backend skips a window whose recognize() raises, on purpose: one
bad window shouldn't fail a meeting. But the transcript then covers less
audio than the recording holds, and nothing said so. The live-transcript
rescue (stenolabs#207) only fires when the batch FAILED or came back as the
silence sentinel, and a half-read batch is neither -- so it passed the
gate and silently replaced a complete live transcript with a
full-of-holes one. No error, just less text.

Report how much audio the surviving windows actually covered, roll it up
across channels, and let the rescue fire when it falls under half.

Measured in seconds of audio, not in windows: windows overlap and the
last one is short, so on 61 s the two windows are [0,60) and [45,61) and
losing the first leaves 16 seconds while 1-of-2 would report half. A
window also only counts once it has contributed -- counting at
recognize() time called a window discarded for unusable timing a success.
Channels emptied by bleed handling don't vote, since what their
transcription missed says nothing about the transcript built from the
other one.

Keyed on coverage, not length: the length threshold this gate used to
have was removed because it replaced correct-but-short transcripts.
A backend that does no windowing reports None, and None stays "unknown"
rather than counting as complete -- only the onnx path can lose a window
quietly, since parakeet-mlx has no per-chunk except and fails loudly.
@Optic00
Optic00 requested a review from ruzin as a code owner August 3, 2026 20:59
@cubic-dev-ai

cubic-dev-ai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

We've triggered an ultrareview automatically — This PR modifies the core decision logic for transcript selection and introduces a new window-coverage metric involving complex overlap calculations across channels, posing a risk to data integrity.. I'll post findings when complete.

An ultrareview is cubic's deepest review, catching hard-to-find bugs in the most critical PRs. It runs a longer, multi-pass analysis using cubic's most capable review models, and typically takes around 30 minutes. It consumes your team's reviewed-lines allowance at 3× the standard rate.

Automated ultrareviews are disabled by default. We triggered this run as part of your trial. Want cubic to do this for every high-risk PR? Enable auto-ultrareview in your settings.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultrareview completed in 10m 41s

1 issue found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/_parakeet_onnx.py">

<violation number="1" location="src/_parakeet_onnx.py:585">
P3: The new coverage warning can report an inflated “windows usable” count. The code logs `windows_recognized`, but that counter is incremented before the token/timestamp length check that can discard a window, so discarded windows still appear as “usable” in the message. Using wording that matches the counter (or tracking a true usable counter) would keep diagnostics aligned with actual behavior.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread src/_parakeet_onnx.py
covered_seconds = covered_samples / _SAMPLE_RATE
if covered_seconds < total_seconds:
logger.warning(
"ONNX transcription read %.0fs of %.0fs (%d of %d windows usable) — "

@cubic-dev-ai cubic-dev-ai Bot Aug 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The new coverage warning can report an inflated “windows usable” count. The code logs windows_recognized, but that counter is incremented before the token/timestamp length check that can discard a window, so discarded windows still appear as “usable” in the message. Using wording that matches the counter (or tracking a true usable counter) would keep diagnostics aligned with actual behavior.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/_parakeet_onnx.py, line 585:

<comment>The new coverage warning can report an inflated “windows usable” count. The code logs `windows_recognized`, but that counter is incremented before the token/timestamp length check that can discard a window, so discarded windows still appear as “usable” in the message. Using wording that matches the counter (or tracking a true usable counter) would keep diagnostics aligned with actual behavior.</comment>

<file context>
@@ -539,4 +578,20 @@ def _transcribe_windows(ts_model: Any, samples) -> _SimpleResult:
+    covered_seconds = covered_samples / _SAMPLE_RATE
+    if covered_seconds < total_seconds:
+        logger.warning(
+            "ONNX transcription read %.0fs of %.0fs (%d of %d windows usable) — "
+            "the transcript is missing roughly %.0fs of audio",
+            covered_seconds, total_seconds,
</file context>
Suggested change
"ONNX transcription read %.0fs of %.0fs (%d of %d windows usable) — "
"ONNX transcription read %.0fs of %.0fs (%d of %d windows recognized) — "
Fix with cubic

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