Skip to content

AI Processor Worker

Content pending

This page is being written as part of the 2026-07 documentation program. Coverage is tracked in docs/planning/docs-ledger.md.

Runs AI jobs (transcription, content generation, guest research) as Cloudflare Workflows. The app inserts an ai_jobs row and sends { jobId, jobType } to the ai-jobs queue; the worker's queue consumer spawns a Workflow instance whose id IS the job id (duplicate deliveries dedupe at the platform). Workflows meter AI credits (hard reserve for transcription, headroom check + soft record for generation), write versioned artifacts (raw.{jobId}.json, write-ahead), and handle failed-job successor spawning. Code: workers/ai-processor/ (see its CLAUDE.md for the full step diagrams). See AI Features for the feature-level architecture.

Media-plane integration (plan 2026-07-18-media-processing-plane.md §7.3, revised): transcription accepts any publishable enclosure (MP3 and M4A). Chunk planning prefers a silence-aligned prep manifest from the Media Processor (sample-exact chunk objects — no MP3 frame/reservoir seam ambiguity reaches Whisper; duration-gated at 6h). Without one, a cheap credit-headroom READ runs first (a zero-credit account is refused and the job marked skipped BEFORE any prep container spend; a job that already reserved passes through — the replay-across-deploy guard), then every format enqueues a transcription_prep job on the media-jobs queue and hibernate-polls for the manifest via durable step.sleep (MP3 ~35m; no-fallback formats poll ~135m, covering the prep workflow's full two-attempt retry budget). MP3 alone keeps the original streaming frame scan (byte-gated at ~2h/100 MB) as an automatic FALLBACK when the plane cannot deliver — enqueue failure, prep job failed/skipped, unusable manifest, or poll exhaustion — so MP3 transcription degrades gracefully instead of hard-failing; M4A has no fallback and fails terminal. Manifests are staleness-guarded on the episode's current audio_url, containment-checked (chunk keys must be siblings of the manifest), and TTL-swept after 7 days (re-derived on demand). Whisper calls carry a static vocabulary initial_prompt (podcast + episode title) with an automatic retry-without on rejection.

Paragraph coalescing (src/lib/paragraphs.ts): Whisper's per-chunk segment granularity is nondeterministic — the same audio can decode as one 27s segment or a chain of 5s sentence-fragments, and the transcript workspace renders one row per segment. After diarization (which runs on the native segments for finer speaker attribution), consecutive same-speaker segments merge into sentence-complete paragraphs: close at the first sentence-final ending once 25s long, break on speaker change or an inter-segment gap over 1.25s, split a closing segment that dangles mid-sentence at its last internal sentence boundary using word timings (rebuilt token text, the continuation-truncation precedent), and hard-cap punctuation-starved speech at 40s. The words array is never remapped, so player word-sync is unaffected. The artifact's segments are therefore paragraph-sized units everywhere: workspace rows, published VTT cues, and exports.

Internal documentation - Not for public distribution