Skip to content

Enforce the transcription cap where cloud STT is dispatched (flag off) - #21866

Merged
Git-on-my-level merged 28 commits into
mainfrom
lane/sub-cap-dispatch
Oct 16, 2026
Merged

Git-on-my-level merged 28 commits into
mainfrom
lane/sub-cap-dispatch

Conversation

@Git-on-my-level

@Git-on-my-level Git-on-my-level commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

The allowance was computed but Omi-funded STT dispatch did not enforce it. This adds STT_CAP_DISPATCH_ENFORCEMENT, default false, to pause new funded provider work at exhaustion while preserving capture and retaining sync audio for recovery. Activation is David's product decision; this PR adds no runtime binding or flag flip.

Failure-Class: new

The violated contract is the cap being computed but not enforced at dispatch. FC-stt-allowance-not-enforced-at-dispatch records provider, queue, custody and recovery regression guards.

UI-Evidence: none -- Client changes are regenerated optional response DTOs; no UI, interactions or rendering change.

Behavior behind the flag

  • Live shares one funded admission state across provider creation, ordinary sends, multichannel, replay, recovery and replacement providers. Accepted finite audio debits it locally; ordinary usage flushes refresh it. Policy pauses keep receiving and existing storage paths active, preserve the threshold event, and drain accepted work. Resumed registry sockets retain one debit boundary.
  • Deepgram cloud actually using a customer key is customer funded. Omi-funded alternatives reauthorize without the BYOK quota or desktop-paywall exemption. Custom/on-device sessions continue. Current sync provider policy serves Omi-funded Modulate/Parakeet; a Deepgram key does not exempt them.
  • Sync v1 intake / v2 execution hold decoded source before VAD and cloud STT. V2 first applies ordinary WAL overlap coverage. The segment processor rechecks each STT invocation/retry. Initial holds coordinate database checks/manifest writes and storage uploads as async leaves, without occupying the sync pool. Transient admission/configuration holds do not claim credits are exhausted. Late holds appear in the result and their quantities wait for recovery settlement. Existing root meter definitions and unit projections remain intact.
  • Encrypted audio uses bounded streaming chunks in the existing owner-scoped private bucket. Failed storage cannot produce a retained-audio acknowledgment. V2 transfers its fenced content ledger into held, without a TTL or completed-transcription proof. Duplicate intake acknowledges custody without another provider call; only all referenced holds settling can complete the ledger.
  • The flagged API sync router registers recovery through the existing locked_content_recovery authenticated-request path, independently of the old monthly unlock receipt/reset flag. Recovery replays WAL coverage/lineage and restores geolocation and speaker context. Before STT or ingestion, a token/lease-fenced dispatched receipt records funding, provider/model, dispatch time and attempt. Piece bytes and lineage freeze in encrypted owner-scoped artifacts before dispatch. An explicit provider_called signal clears known non-calls under the same fence. Conversation ingestion atomically commits a proof bound to that hold/piece/lineage. After 30 minutes, ambiguous work uses the proof without STT, or gets one admission-checked second dispatch (attempt=2). A second unresolved attempt becomes failed_reconciliation and retains audio. Recovery owns the retry, so checkpointed recovery does not perform the ordinary local empty-ASR retry. Provider-internal SDK retries are unchanged. Successful siblings settle/finalize independently with checkpointed progress; failed pieces never publish all-success ledger completion. Reconciled ingests retain raw audio because interrupted playback storage may be incomplete. Terminal retained rows cannot dispatch, and settlement failures remain retryable. Unacknowledged preparing/failed source jobs retain their retry ownership.
  • Batch authorizes compute and rechecks immediately before ASR microbatches and the newly merged full-capture empty-ASR retry. Recovery reauthorization runs only when that adapter will dispatch; its actual synthetic padding is included in accepted-audio provenance. An oversized retry returns None/HeldWork only on the enforced callback path and does not call fail(). The flag-off adapter keeps current main's [] result and ordinary failure behavior. Held work preserves raw artifacts and refunds the provider-failure attempt. Quarantine recovery rechecks before fetch and its sole provider attempt.
  • Missing/invalid launch configuration logs/counts an error and closes funded admission only with enforcement on. A valid explicit future launch remains intentionally unlaunched.

What users see when the flag is on — David's activation decision

  • Mobile, on-device overflow flag off: capture continues as main permits; exhausted managed sessions stop producing cloud transcripts. Existing Plus threshold handling remains. Offline uploads wait durably, with no new held-content card or automatic on-device processing of that backlog.
  • Mobile, on-device overflow flag on: eligible clients keep the existing on-device offer/mode switch. Custom/on-device sessions continue. Server-held uploads still await reset/upgrade recovery.
  • macOS: the existing threshold handler can stop capture. If the client continues, the backend stops new funded sends. Accepted work drains; this adds no macOS on-device overflow flow.
  • Web: funded live transcripts pause at the cap; supported sync uploads wait for recovery. No new on-device offer or waiting-audio view is added.

Existing over-cap paid users would receive held audio instead of transcripts that become locked. Live retention follows existing consent/storage paths; the sync hold is a processing checkpoint. Recovery is bounded and request-driven: WS-only/inactive accounts wait for an authenticated API request. Rollback pauses new hold recovery while retaining its audio. Enabling later must cover listen, sync/backfill, policy and GPU runtimes. No deployment, catalog/price, Stripe or ladder-v1 change is included.

Additive measurement

omi_stt_cap_provider_audio_seconds_total{path,outcome,provider,allowance} records accepted Omi-funded provider-adapter audio versus suppressed live input / held sync quantities. path=live|sync, outcome=accepted|suppressed|held, and allowance=exhausted|other isolate genuine post-cap acceptance from available/untrusted observations. Provider labels are bounded. GPU results persist cap_dispatch.omi_funded_accepted_provider_audio_seconds and compute admission origin; the counter is also exported by the worker metrics registry. omi_stt_cap_launch_config_errors_total{reason=missing|invalid} distinguishes configuration errors. omi_stt_cap_recovery_reconciliation_total{outcome} counts cleared_noncall, reconciled_ingested, redispatched, and failed; structured logs carry hold id and piece name, with no ids in metric labels. No new provenance emits with the flag off. Held queue attempts can repeat; these are not unique-content counts or an exact vendor invoice ledger.

Gate review map

Boundary Code
Default-off flag backend/config/stt_cap_dispatch.py:6
Shared live balance / actual sends backend/utils/stt_cap_dispatch.py:79
Provider construction, replacement and funded fallback backend/utils/stt_cap_dispatch.py:207
Live startup / receiving while paused backend/routers/listen/receiver.py:1246
Ordinary live buffers backend/routers/listen/receiver.py:1861
Multichannel backend/routers/listen/receiver.py:2030
Replay / idle replay backend/utils/stt/live_session.py:1054
Recovery without closing capture backend/routers/listen/receiver.py:2010
Existing threshold event / allowance refresh backend/routers/listen/runtime.py:802
Sync v1 intake / late-held result backend/routers/sync.py:661; backend/routers/sync.py:839
Sync v2 post-coverage hold / last pre-STT gate backend/utils/sync/pipeline.py:2434; backend/utils/sync/pipeline.py:1353
Batch authorization backend/utils/batch_pipeline/admission.py:14
GPU microbatch recheck backend/diarizer/batch_pipeline.py:366
GPU full-capture retry dispatch / padded provenance backend/diarizer/batch_pipeline_models.py:86
Quarantine recovery backend/utils/batch_pipeline/quarantine_recovery.py:93
Encrypted audio / custody ledger backend/utils/sync/cap_hold.py:124; backend/database/sync_cap_holds.py:24
Reset / upgrade recovery and piece checkpoint backend/utils/sync/cap_hold_recovery.py:85
Atomic held-piece ingest proof backend/database/sync_cap_recovery.py:13
Frozen recovery piece bytes / lineage backend/utils/sync/cap_recovery_plan.py:24
Recovery WAL coverage / lineage backend/utils/sync/cap_recovery_audio.py:15
BYOK bound to actual funding backend/utils/stt_cap_dispatch.py:19
Launch configuration backend/utils/subscription.py:1593
Additive metrics backend/utils/observability/stt_cap_dispatch.py:7

Validation

Backend tests run serially under nice -n 10 through backend/test.sh only, with BACKEND_PYTEST_WORKERS=1. The final delta run passes 1,276 cases in 50 suites, including the recovery state machine, real provider-call signals, atomic ingest hooks, immutable audio/lineage plans, metric/log outcomes, lifecycle/assignment contracts, both flag states, all earlier dispatch gates and root-meter parity. No timing budget, workflow, marker or skip hatch is weakened.

Flag-off evidence: denied-provider matrices delegate to the original constructors/sends with no new allowance IO; the real FastAPI v1 route returns the original exact JSON bytes; the nullable v2 field is omitted when absent. Without held audio, legacy DG settlement receives its original duration argument without new arithmetic; the real partial-batch coordinator regression covers that path. Existing metering suites and canonical OpenAPI / generated Dart, TypeScript and Swift freshness checks pass. Client DTO regeneration adds only the optional held-result field. Current-main calendar fixtures pass unchanged. The parity fixture isolates its synthetic transcript from auxiliary language-model loading. No timing budget, marker, workflow or skip hatch is weakened.

Exact-head CI remains the merge authority. Local validation is synthetic API/provider/persistence-boundary evidence; no production/GCP/provider requests or client/device acceptance was performed. The dispatch/custody contract and limits are in backend/docs/stt-cap-dispatch.md.

Bounded reconciliation state machine (delta review)

The coordinator policy prefers retaining audio over avoiding one duplicate transcription. An ambiguous piece is not permanently sealed: dispatched(attempt=1) waits thirty minutes, then becomes transcribed from the atomic ingest proof or reauthorizes a single dispatched(attempt=2). After a second ambiguous cooldown, proof still wins; absent proof, the piece becomes failed_reconciliation, retaining raw audio and frozen pieces without an application TTL. Known non-calls clear/revert the intent without spending the additional dispatch. Completed siblings finalize while another piece waits or fails. Usage keeps the same cap-hold-{hold}-{piece} idempotency key.

Regression guards cover signed-URL/keyterm/provider-select non-calls, pre-provider and late cap denial, atomic ingest proof with lost/expired/replaced leases, proof-based finalization without STT, exactly one successful or ambiguous redispatch, terminal audio retention, sibling progress, storage/plan failure, immutable piece bytes/lineage, retryable ledger settlement, and all four metric/log outcomes. Real segment-processor and ordinary lifecycle call-shape tests cover flag-off parity. Oversized-retry parity and the duration-parse guard remain intact.

The runtime parity sweep covers live constructors/sends/multichannel/replay/failover/death handling; sync v1/v2 response bytes, null omission and root-meter arguments; GPU retries/failure accounting; queue/quarantine; BYOK; launch configuration; storage/ledger rollback custody; and provider selection. The new proof hooks, immutable plans, provider-call signal and reconciliation emissions are reached only by enforced recovery. No ladder-v1 code, price, activation, deployment or workflow changes are added.

Follow-ups — #21488

No ladder-v1 paths change here. Findings #6 (budget-aware live refresh when remaining_seconds=None, including remaining admission/reservation races) and #7 (usage-unit projection coverage gaps) remain for lane/sub-ladder-cutover / #21488. These checks are not distributed quota reservations: already admitted concurrent work can drain across the cap.

Line-Count-Exception: backend/routers/listen/receiver.py | 2564 -> 2618 | Integrate the default-off gate at the owning boundary; shared admission, custody and recovery live in small dedicated modules.

Line-Count-Exception: backend/routers/sync.py | 2795 -> 2849 | Integrate the default-off gate at the owning boundary; shared admission, custody and recovery live in small dedicated modules.

Line-Count-Exception: backend/utils/stt/streaming.py | 2401 -> 2405 | Integrate the default-off gate at the owning boundary; shared admission, custody and recovery live in small dedicated modules.

Line-Count-Exception: backend/utils/subscription.py | 1962 -> 1978 | Integrate the default-off gate at the owning boundary; shared admission, custody and recovery live in small dedicated modules.

Line-Count-Exception: backend/utils/sync/pipeline.py | 3213 -> 3385 | Integrate the default-off gate at the owning boundary; shared admission, custody and recovery live in small dedicated modules.

Line-Count-Exception: backend/diarizer/batch_pipeline.py | 484 -> 529 | Integrate the default-off gate at the owning boundary; shared admission, custody and recovery live in small dedicated modules.

Line-Count-Exception: backend/utils/stt/pre_recorded.py | 1285 -> 1293 | Integrate the default-off gate at the owning boundary; shared admission, custody and recovery live in small dedicated modules.

Line-Count-Exception: backend/utils/conversations/lifecycle.py | 980 -> 987 | Integrate the default-off gate at the owning boundary; shared admission, custody and recovery live in small dedicated modules.

🤖 Generated with Codex

@mintlify

mintlify Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
omi 🟢 Ready View Preview Oct 16, 2026, 9:46 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-16T21:54:48.491869Z c47bc2f New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e26e11ce28

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/utils/sync/pipeline.py Outdated
Comment on lines +1255 to +1258
if stt_cap_dispatch_enabled():
if not sync_allowed(uid):
if deferred_outcome is not None:
deferred_outcome['cap_held'] = True

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Propagate late holds into sync results

When allowance passes the pre-VAD check but becomes unavailable before this per-segment check, process_segment returns False without adding an error and only sets deferred_outcome['cap_held']. Neither coordinator consumes that flag: v1 counts the segment as successful and bills the full speech duration, while v2 reports success without held_audio and marks the content ledger completed even though its content is still pending recovery. Return or aggregate a distinct held outcome so both paths publish the held state and avoid settling it as completed content.

AGENTS.md reference: backend/AGENTS.md:L145-L145

Useful? React with 👍 / 👎.

Comment thread backend/utils/sync/pipeline.py Outdated
Comment on lines +2325 to +2327
if cap_held:
await run_blocking(storage_executor, _cleanup_files, wav_paths)
wav_paths = []

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Apply WAL coverage before recovering held captures

For an exhausted v2 upload that overlaps audio already committed by live capture or an earlier WAL, this clears the decoded paths before apply_sync_wal_audio_coverage and apply_safety_wal_coverage run. Recovery later performs only VAD plus process_segment, so the held copy bypasses the existing overlap and lineage protections and can create duplicate transcript/conversation content after the allowance resets. Preserve and replay the coverage evidence, or run the coverage stages before retaining the recoverable audio.

AGENTS.md reference: backend/AGENTS.md:L145-L145

Useful? React with 👍 / 👎.

Comment thread backend/utils/sync/pipeline.py Outdated
Comment on lines +2321 to +2324
target_conversation_id=target_conversation_id,
client_device_id=client_device_id,
client_platform=client_platform,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve geolocation in held-audio metadata

When a v2 sync request includes conversation geolocation and is held at the cap, the already-resolved geolocation value is not stored here. cap_hold_recovery._recover_row subsequently calls process_segment without geolocation, so every conversation created from that retained upload permanently loses the location that the ordinary v2 path would persist.

Useful? React with 👍 / 👎.

Comment on lines +113 to +120
async def resume(self):
if self.raw is None and not self.closed and self.admission.allowed(self.provider):
raw = await self.factory()
if raw is None:
raise RuntimeError('held provider constructor returned no socket')
self.raw = raw
for callback in self._open_callbacks:
callback(raw)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid nesting cap sockets when resuming Modulate

When a Modulate target is held during a mid-session cap transition and later resumed, this factory can return another CapSocket: connect_modulate is decorated itself, and its default-target branch calls the separately decorated streaming.process_audio_modulate. Unlike the initial decorator path, resume does not unwrap that result, so every subsequent send passes through LiveAdmission.send twice, double-counting provider metrics and consuming the restored allowance at twice the audio rate.

Useful? React with 👍 / 👎.

Comment thread backend/utils/sync/cap_hold.py Outdated
Comment on lines +43 to +44
def hold_segment(uid: str, path: str, metadata: dict[str, Any]) -> float:
audio = Path(path).read_bytes()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Stream large held WAVs instead of buffering them

For a valid large compressed sync upload, the decoded WAV can be hundreds of megabytes or more, and read_bytes() loads it entirely before encrypt_audio_chunk creates another full-size ciphertext buffer for upload. An exhausted request can therefore exhaust a backend-sync instance's memory while trying to create the hold, repeatedly failing the same upload and potentially disrupting concurrent jobs; encrypt and upload bounded chunks instead of materializing the complete WAV and ciphertext in memory.

AGENTS.md reference: backend/AGENTS.md:L202-L205

Useful? React with 👍 / 👎.

Comment thread backend/utils/sync/cap_hold_recovery.py Outdated
Comment on lines +99 to +109
await run_blocking(
db_executor,
record_usage,
uid,
transcription_seconds=int(seconds),
speech_seconds=int(seconds),
idempotency_key=f'cap-hold-{snapshot.id}-{name}',
usage_metadata={'funding': 'omi', 'provider': 'unknown', 'source': 'sync_v2'},
)
await run_blocking(
db_executor, snapshot.reference.update, {'processed_paths': firestore.ArrayUnion([name])}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Checkpoint recovered transcription before settling usage

If usage settlement raises after process_segment has already called the paid STT provider and persisted the conversation, execution exits before processed_paths is updated. The next authenticated recovery pass therefore retranscribes the same audio and repeats post-processing; an ambiguous usage write is still idempotent, but the provider dispatch is not. Persist a transcript/content phase checkpoint before attempting usage settlement so settlement retries cannot issue another STT request.

AGENTS.md reference: backend/AGENTS.md:L145-L145

Useful? React with 👍 / 👎.

Comment thread backend/utils/sync/cap_hold_recovery.py Outdated
Comment on lines +83 to +87
ConversationSource(meta['source']),
False,
prefs,
private_cloud_sync_enabled=bool(private),
data_protection_level=protection,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Load speaker embeddings during held-audio recovery

Recovered segments call process_segment without the person_embeddings_cache that both normal sync paths build before transcription. For users with known voice profiles, held audio recovered after a reset or upgrade therefore skips voice-based person matching and persists generic speaker attribution even though the same upload would have identified speakers without the cap hold.

Useful? React with 👍 / 👎.

Comment thread backend/utils/sync/cap_hold_recovery.py Outdated
Comment on lines +94 to +98
if errors or outcome.get('cap_held'):
return False
if ok:
await run_blocking(sync_executor, finalize_recovered_sync_segment, uid, response, bool(private))
seconds = await run_blocking(sync_executor, get_wav_duration, segment_path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Finalize recovered conversations after all pieces join

For a full held capture that VAD splits into multiple pieces, this runs _reprocess_merged_conversations and private-audio finalization inside the per-piece loop. When adjacent pieces merge into the same conversation, the backend repeatedly summarizes and finalizes progressively incomplete versions, unlike ordinary sync which waits for the complete batch; with eager reprocessing enabled this adds repeated LLM work and can expose transient partial enrichment. Accumulate the response across the hold and finalize once after every piece is persisted.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 89120f3d21

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/utils/sync/cap_hold_recovery.py Outdated
Comment on lines +134 to +135
await run_blocking(db_executor, snapshot.reference.update, {'state': 'completed'})
await run_blocking(storage_executor, _erase, uid, snapshot, row)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fence hold completion with the active lease token

If processing, finalization, or usage settlement for the last segment exceeds the 30-minute lease, another authenticated recovery can claim the same hold with a new token. This unconditional update then lets the stale worker mark the successor's claim completed and _erase its manifest and audio, allowing duplicate provider/content work or disrupting the active recovery. Make the processed/completed transition transactional and conditional on the original lease token still being current.

Useful? React with 👍 / 👎.

Comment thread backend/utils/sync/cap_hold_recovery.py Outdated
Comment on lines +105 to +106
idempotency_key=f'cap-hold-{snapshot.id}-{name}',
usage_metadata={'funding': 'omi', 'provider': 'unknown', 'source': 'sync_v2'},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Attribute recovered usage to the provider that ran

For every successfully recovered hold, process_segment records the selected provider in outcome, but usage settlement discards it and hardcodes provider: unknown. The usage-unit projection cannot apply a provider rate to these calls, so recovered STT spend is systematically reported as missing/unknown even though the dispatch result identifies the provider; populate this metadata from the successful outcome.

Useful? React with 👍 / 👎.

Comment thread backend/routers/sync.py Outdated
Comment on lines +659 to +660
'credits_exhausted': True,
'held_audio': True,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Distinguish transient holds from exhausted credits

When admission fails closed because allowance lookup is temporarily unavailable—for example, a subscription/usage read fails or the launch-date configuration is missing—hold_capture_if_exhausted returns the same boolean as a real quota exhaustion and this response unconditionally reports credits_exhausted: true. Clients can therefore present or act on a quota-exhaustion state during an infrastructure/configuration failure even though the existing precheck deliberately treats transient allowance reasons as non-exhaustion; preserve the denial reason and only set this field for actual exhausted-credit outcomes.

Useful? React with 👍 / 👎.

Comment thread backend/routers/sync.py Outdated
Comment on lines +642 to +644
cap_held = await run_blocking(
sync_executor,
hold_capture_if_exhausted,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Run held-audio uploads on the storage pool

Both sync coordinators invoke hold_capture_if_exhausted on the 16-worker sync_executor, but that helper performs the full synchronous GCS upload (plus local audio I/O and Firestore work) before returning. When several exhausted users upload large captures or GCS is slow, these holds can occupy every sync worker and starve unrelated sync pipelines; split the hold into async-coordinated DB/storage leaves and run the blob/audio work on storage_executor, which is the repository-designated pool for GCS and audio chunk I/O.

AGENTS.md reference: backend/AGENTS.md:L269-L276

Useful? React with 👍 / 👎.

# Conflicts:
#	backend/tests/unit/test_calendar_long_range_upcoming.py

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7c0f57fddf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

def sync_allowed(uid: str, seconds: float = 0, provider: Any = 'unknown') -> bool:
if not enabled():
return True
allowance = funded_allowance(uid)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Honor BYOK before applying the sync cap

When enforcement is enabled, an exhausted user whose inline v1/v2 sync request carries a validated X-BYOK-Deepgram key is still denied here because funded_allowance forces byok_active=False and omi_funded=True. The downstream prerecorded path would select the user's Deepgram key, so this customer-funded dispatch should not consume Omi allowance; instead, the upload is held until the Omi allowance resets. Determine the actual provider/funding before applying the Omi cap, or preserve the BYOK exemption for Deepgram.

AGENTS.md reference: backend/AGENTS.md:L141-L141

Useful? React with 👍 / 👎.

Comment on lines +46 to +48
partition = {
key: metadata.get(key) for key in ('timestamp', 'source', 'client_device_id', 'target_conversation_id')
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Include content-ledger ownership in hold identities

When the same WAV is held under two distinct v2 content ledgers—for example, the original batch [A, B] followed by a retry containing only [A]—this identity is unchanged because the partition omits content_id. hold_segment then returns the existing manifest while mark_content_held records its ID on the second ledger; recovery reads the first manifest's content_id, settles and deletes only that hold, and leaves the second ledger permanently held with no backing object. Include ledger ownership in the identity or transactionally attach every ledger owner to the hold.

AGENTS.md reference: backend/AGENTS.md:L145-L145

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c54f5a8e69

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

def defer_segment(uid: str, path: str, metadata: dict, response: dict, lock: Any) -> None:
"""Report a durable late hold to both coordinators, never as silence."""
receipt: dict = {}
hold_segment(uid, path, {**metadata, 'file_name': Path(path).name}, receipt=receipt)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Offload late hold uploads from the sync executor

When allowance becomes unavailable after the initial precheck, process_segment reaches this call while already running on sync_executor, and hold_segment synchronously hashes the WAV, performs Firestore reads/writes, and uploads the encrypted object. Several large late-held segments can therefore occupy all 16 sync workers and starve unrelated pipelines; route this late-hold path through the async coordinator and the designated DB/storage executors just like the initial hold path.

AGENTS.md reference: backend/AGENTS.md:L274-L276

Useful? React with 👍 / 👎.

Comment thread backend/diarizer/batch_pipeline.py Outdated
Comment on lines +354 to +355
held.update(required - allowed)
batch = [w for w in batch if w.segment_id not in held]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve completed microbatches when a later check holds

When a long work item spans multiple ASR microbatches and authorization is revoked after an earlier microbatch succeeds, adding its segment ID to held makes the final loop return HeldWork and discard all words already accumulated for that item. The pump then defers the original raw item, so its next attempt retranscribes those successful windows from the beginning, duplicating GPU/provider work and accepted-audio accounting on every such transition; checkpoint the completed windows or resume only the undispatched remainder.

Useful? React with 👍 / 👎.

Comment on lines +223 to +225
allowance = await run_blocking(db_executor, funded_allowance, uid)
if allowance.managed:
return False

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reconcile existing v1 holds before dispatching a retry

When a v1 held-upload acknowledgment is lost and the client retries after its allowance resets, this early return skips identity lookup even though the prior hold still exists. The authenticated retry also schedules hold recovery, so the recovery worker and the ordinary v1 pipeline can transcribe the same WAV independently and create duplicate conversation content; detect an existing v1 hold before allowing the retry to dispatch, or fence recovery and retry through a shared idempotency record.

Useful? React with 👍 / 👎.

Comment on lines +170 to +171
if receipt['has_content']:
seconds = int(receipt['seconds'])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Aggregate recovered duration before rounding usage

When a held capture is split into multiple VAD pieces with fractional-second durations, converting every receipt to int separately permanently drops each fractional remainder; coverage-trimmed subsecond pieces are recorded as zero. A capture containing many short pieces can therefore consume materially less monthly allowance than the STT audio actually dispatched and reopen the cap early, so settle an aggregate duration or carry fractional units across the per-piece idempotent writes.

Useful? React with 👍 / 👎.

Comment on lines +1594 to +1597
if stt_cap_dispatch_enabled() and not subscription_launch_date_str:
CONFIG_ERRORS.labels(reason='missing').inc()
logger.error('event=stt_cap_launch_config outcome=invalid reason=missing')
raise ValueError('missing subscription launch date')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep launch-date failures inside allowance resolution

When enforcement is enabled with a missing or invalid SUBSCRIPTION_LAUNCH_DATE, this helper now raises, but /v1/users/me/subscription calls it directly while building the usage response, before reaching the exception-safe resolve_transcription_allowance path. The same configuration state that is supposed to conservatively deny funded STT therefore makes the subscription endpoint return a 500 for every ordinary user; return an untrusted-usage sentinel here or confine the exception to the allowance resolver.

Useful? React with 👍 / 👎.

# Conflicts:
#	backend/diarizer/batch_pipeline.py

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 914beb1905

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/utils/sync/cap_hold_recovery.py Outdated
response = _response(receipts)
if receipts:
# Ordinary sync finalizes the joined batch, not each VAD piece.
await run_blocking(sync_executor, finalize_recovered_sync_segment, uid, response, bool(private))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Link raw archives after recovering a hold

When a private-cloud sync upload is held, its original raw_capture_paths have already been archived, but those paths are neither retained in the hold metadata nor passed to link_archive_captures after the recovered conversations are finalized. Consequently, deleting a recovered conversation cannot discover and delete its raw sync archive because delete_conversation_audio_files relies on the archive's conversation_ids metadata, leaving the original audio stored until account deletion. Persist the archive paths with the hold and link them to the recovered conversation IDs before completing recovery.

Useful? React with 👍 / 👎.

row = snapshot.to_dict() or {}
try:
if row.get('state') == 'completed':
await run_blocking(storage_executor, _settle_and_erase, uid, snapshot, row)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep Firestore settlement off the storage pool

In both completed-hold paths, _settle_and_erase is dispatched on storage_executor, but it performs sync_cap_holds.settle_hold, snapshot.reference.update, and snapshot.reference.delete, all synchronous Firestore operations. A recovery burst therefore occupies storage workers with database latency and defeats the repository's executor isolation; split the GCS deletions from the Firestore settlement and run each leaf on its designated pool.

AGENTS.md reference: backend/AGENTS.md:L269-L276

Useful? React with 👍 / 👎.

Comment thread backend/utils/sync/cap_hold_recovery.py Outdated
response = _response(receipts)
if receipts:
# Ordinary sync finalizes the joined batch, not each VAD piece.
await run_blocking(sync_executor, finalize_recovered_sync_segment, uid, response, bool(private))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve Process Now semantics during hold recovery

When an audio_omibatchphone_ upload selected through Process Now is held, ordinary v2 processing passes user_selected=True to _reprocess_merged_conversations, which restores rule-discarded conversations and forces their enrichment. Recovery instead calls this helper with its default False, so an explicitly selected recording can remain discarded and unprocessed even though the content ledger is completed and the retained audio is erased. Retain the user-selected marker in hold metadata and pass it through finalization.

Useful? React with 👍 / 👎.

Comment on lines +879 to +880
if suppress_socket_audio(self.raw, data):
return True

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Advance legacy clocks while cap audio is suppressed

When a managed live session runs with AUDIO_TIMELINE_V2 off, a mid-session cap pause returns here before _seconds and session.audio_seconds advance. If allowance becomes available at a later 60-second refresh, the provider resumes on a clock that omits the suppressed interval, so subsequent transcript timestamps are shifted earlier and can be merged into the pre-pause conversation instead of their actual capture time. Advance the legacy receive clocks for suppressed packets without recording provider-accepted spans or usage.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a8b41192a2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/utils/sync/cap_hold_recovery.py Outdated
'usage_metadata': {
'funding': 'omi',
'provider': outcome.get('provider') or 'unknown',
'source': 'sync_v2',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the v1 source on recovered usage

When audio held by the v1 /v1/sync path is recovered, this hardcodes its usage provenance as sync_v2, even though ordinary v1 accounting uses sync_v1. Because hold_capture_if_exhausted serves both protocols, every recovered v1 segment is consequently attributed to the wrong source in the usage-unit projection; derive the source from the retained hold metadata or the presence of a v2 content ID.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5c5de21b1a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


def authorize_dispatch(segments):
selected = [v for v in leases if v.item.segment_id in segments]
receipt = self.authorize_compute(selected) if self.authorize_compute is not None else {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Debit accepted microbatches before reauthorizing

When one work item spans multiple ASR microbatches and the user begins with any positive finite allowance, every call here re-reads the same persisted usage because accepted audio is not written to hourly_usage until projection after process_batch finishes. Consequently, the first accepted microbatch cannot reduce authorization for later microbatches, and a long upload can consume far beyond the remaining quota despite the advertised per-microbatch checks. Reserve or locally debit accepted audio before issuing the next authorization request.

AGENTS.md reference: backend/AGENTS.md:L151-L151

Useful? React with 👍 / 👎.

Comment on lines +101 to +102
result.update(failed_segments=0, errors=[], outcome='success')
updates.update(status='completed', expires_at=now + timedelta(days=LEDGER_RETENTION_DAYS))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Refresh the original sync job after recovery

When a v2 hold is recovered while its original Redis job is still within the 24-hour reconciliation window, this transition updates only the Firestore content ledger. GET /v2/sync-jobs/{job_id} continues reading the unchanged terminal Redis job, so it keeps returning held_audio: true, zero/stale segment counts, and the pre-recovery memory lists even though recovery has completed. Update the matching Redis job after the final hold settles, or have status reads converge from the completed ledger.

AGENTS.md reference: backend/AGENTS.md:L151-L151

Useful? React with 👍 / 👎.

source=ConversationSource(binding.source),
language=binding.language or payload.get('language'),
is_locked=binding.is_locked,
is_locked=False if cap_dispatch_enabled() else binding.is_locked,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Derive batch unlock state from the persisted admission receipt

When the rollout flag changes between durable batch computation and later projection, consulting the projector's current environment reclassifies already-produced results. In particular, a result computed with enforcement off and binding.is_locked=True becomes unlocked if projection runs after activation, exposing content that was transcribed under the old locked-content policy; a rollback can conversely lock work that was authorized while enforcement was on. Persist and consult the compute-admission receipt, such as the payload's cap_dispatch provenance, rather than the current flag.

AGENTS.md reference: backend/AGENTS.md:L151-L151

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 99c9a95797

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

transcript_segments: List[TranscriptSegment] = []
while True:
phase = 'provider_call'
if stt_cap_dispatch_enabled() and not sync_allowed(uid):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reserve allowance before dispatching sync segments

With enforcement enabled and any finite positive balance, every provider invocation in this upload re-reads the same persisted monthly usage: the five-way segment fan-out and all later chunks complete before record_usage runs near finalization. Thus even one remaining second can authorize every segment—and the empty-result retries—in a multi-hour upload, so locally reserve or debit accepted duration before the next invocation rather than relying solely on the unchanged persisted counter.

AGENTS.md reference: backend/AGENTS.md:L151-L151

Useful? React with 👍 / 👎.

Comment on lines +3358 to +3362
def finalize_recovered_sync_segment(uid: str, response: dict, private: bool) -> None:
"""Use the same enrichment/audio finalization as ordinary sync intake."""
_reprocess_merged_conversations(uid, response)
if private:
_finalize_sync_audio_files(uid, response)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Schedule voice learning after hold recovery

When recovered segments create or update conversations with person-attributed speakers, this recovery finalizer stops after enrichment and optional audio finalization, whereas both ordinary sync coordinators then call schedule_person_voice_learning_retries. After the hold is settled and erased there is no later recovery hook, so a recovered conversation's pending voice-learning work may never run and its audio will not improve the person's voice profile; invoke the same bounded scheduler here after audio finalization.

Useful? React with 👍 / 👎.

Comment thread backend/utils/sync/cap_hold_recovery.py Outdated
Comment on lines +174 to +175
if errors or outcome.get('cap_held'):
return False

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Clear pre-dispatch intent when reauthorization denies

If allowance becomes unavailable after _new_dispatch_allowed succeeds but before process_segment reaches its own pre-provider check, cap_recovery=True makes that call return with cap_held without issuing STT. This branch nevertheless leaves the durable receipt in state='dispatched'; every later claim then exits as reconciliation_required, so audio that is known never to have reached a provider is permanently stranded after an ordinary allowance race. Persist a distinguishable not-dispatched state, or roll back this intent when the denial occurs before the first provider call.

AGENTS.md reference: backend/AGENTS.md:L151-L151

Useful? React with 👍 / 👎.

# Conflicts:
#	web/app/src/components/settings/__tests__/SettingsSubscriptionClarity.test.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d54c9297bd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -2993,15 +3145,23 @@ def _checkpoint_fenced_conversations():
if content_id and failed_segments == 0:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Transfer held segments despite sibling failures

When a v2 batch both late-holds one segment and records an error for a sibling, failed_segments > 0 bypasses this ledger write even though cap_holds is nonempty. The job is still finalized with held_audio, and the ledger claim is then released as retryable; cap_hold_recovery therefore sees the acknowledged hold as waiting and cannot process it unless the client happens to re-upload. Transfer hold ownership while preserving the sibling failure state so durable recovery can proceed independently.

AGENTS.md reference: backend/AGENTS.md:L145-L145

Useful? React with 👍 / 👎.

Comment on lines +1250 to +1253
if not admission.allowed(self.host.stt_service):
admission.omi_selected = True
self.cap_paused = True
return True # Receive/persist without consulting provider routing.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Count initially suppressed multi-channel audio

When a multi-channel session begins already denied, this early return leaves every stt_sockets_multi entry empty. _handle_multi_channel_audio then skips its send block because socket_available is false, so it never reaches suppress_socket_audio; unlike the single-channel _flush_stt_buffer, that path does not call record_cap_audio itself. Consequently all audio suppressed before the first successful refresh is absent from omi_stt_cap_provider_audio_seconds_total, understating cap impact for multi-channel sessions.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c47bc2f9bb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +78 to +82
def _new_dispatch_allowed(uid: str) -> bool:
if not sync_allowed(uid):
return False
restricted, _ = get_hard_restriction_status(uid)
return not restricted and not is_daily_audio_ceiling_exceeded(uid)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Apply backfill reservations before recovery dispatch

When a held capture originated from the backfill lane, both initial and repeated recovery dispatches pass only through this helper, which checks the subscription allowance and the fresh-audio daily ceiling but never calls reserve_backfill_speech. Unlike the ordinary v1/v2 backfill paths, recovered historical audio can therefore bypass the per-user/global daily quotas and backfill dispatch pacing; carry the retained lane and content identity into admission and reserve its VAD duration before invoking STT.

AGENTS.md reference: backend/AGENTS.md:L145-L145

Useful? React with 👍 / 👎.

Comment on lines +270 to +274
if receipt['has_content']:
seconds = int(receipt['seconds'])
await run_blocking(
db_executor,
record_usage,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Meter initially held speech in its fair-use lane

For an initial pre-VAD hold, both sync coordinators return before their normal record_speech_ms stage, and recovery records only hourly subscription usage here. Consequently recovered fresh speech never enters the realtime + sync_fresh Redis totals used by soft caps and the daily ceiling, while recovered backfill speech is absent from its separate meter; persist whether a hold was already metered and idempotently record the recovered VAD duration under the retained lane.

AGENTS.md reference: backend/AGENTS.md:L145-L145

Useful? React with 👍 / 👎.

Comment on lines +1410 to +1415
if stt_cap_dispatch_enabled() and cap_recovery and cap_recovery_checkpoint is not None:
# A held piece gets at most two persisted application dispatch
# attempts. Reconciliation owns the second, not this local retry.
if deferred_outcome is not None:
deferred_outcome['recovery_ambiguous'] = True
return False

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Complete confirmed empty recovery attempts as silence

When VAD admits noise but prerecorded returns a valid empty result, this branch marks the known return as ambiguous instead of checkpointing silence. The second recovery dispatch follows the same branch, and because an empty result creates no ingest proof, the piece eventually becomes failed_reconciliation and a v2 content ledger remains held indefinitely; preserve the ordinary bounded-empty-retry semantics and checkpoint a confirmed empty result as completed with no content.

Useful? React with 👍 / 👎.

Comment on lines +72 to +74
@property
def funded_paused(self) -> bool:
return self.omi_selected and not (self.managed and (self.remaining is None or self.remaining > 0))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep monitoring a customer-funded fallback

If an Omi-funded Soniox or Modulate leg fails over to cloud Deepgram using the request's BYOK key, omi_selected remains permanently true from the earlier leg. Once the Omi allowance later reaches zero, this property reports the whole session as funded-paused even though the current Deepgram socket is still customer-funded and allowed; both STT death monitors and _recover_ingested_audio then skip that live socket, so an upstream close can strand recovery or leave the client listening until the receive timeout. Base the pause decision on the currently serving provider rather than whether any prior leg was Omi-funded.

Useful? React with 👍 / 👎.

@Git-on-my-level
Git-on-my-level merged commit 1239634 into main Oct 16, 2026
67 checks passed
@Git-on-my-level
Git-on-my-level deleted the lane/sub-cap-dispatch branch October 16, 2026 22:28

This branch was successfully deployed

1 active deployment
staging - docs — c47bc2f9 Deployed Oct 16, 2026 by mintlify[bot]
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