H HospAir

Product Roadmap

Every phase shipped, live, and planned.

πŸ—Ί HospAir Roadmap

Every phase of the product: shipped, live, and planned.

Last updated: 18 June 2026 Β· Related: Engine roadmap Β· Docs index Β· Main README


Product vision

  • One Docker stack per hospital site
  • Volunteer-friendly browser studio (Myriad-style)
  • Reliable log ↔ audio alignment (log is source of truth; audio follows)
  • End state: schedule β†’ show β†’ clock β†’ generated hour log β†’ playout

Status at a glance

ID Name Status
0 Greenfield MVP (stack, library, playout, streams) βœ… Done
0.1 Myriad-style studio & library UX βœ… Done
0.2 Hour log, scheduling columns, drag-to-log βœ… Done
0.3 Automated backtiming & hour lock βœ… Done
0.4 Early playout reliability (sync, carts) βœ… Done
1 Customizable dual-monitor playout βœ… Done
2 Layout presets (user + station) βœ… Done
3 Studio parity, SmartInfo, voice links βœ… Done
3.1 SmartInfo polish (traffic, shortcuts) βœ… Done
4A Go button & manual start βœ… Done
4B SWEEP segues, marker trim, warnings βœ… Done
4C Top-of-hour auto backtiming βœ… Done
4D Advert breaks & basic trafficking βœ… Done
4E Admin hub, sample.html UI polish βœ… Done
5 Playout desync hardening βœ… Done
5.1 Playout playhead sync (Liquidsoap 2.3) βœ… Done
5.2 Dead-air protection & stream watchdog βœ… Done
5.3 Liquidsoap 2.4.x pull queue (request.dynamic) πŸ”Ά LIVE: PR 1–2 shipped & on air 12 Jun; hour-boundary landing + re-fill sync hardened 13 Jun (see below); PR 3 (delete push path) + PR 4 (24h soak) remain
5.4 Wall-clock catch-up & time-authoritative logs βœ… Done
5.5 Liquidsoap event push (track / dead-air webhooks) βœ… Done
6.1 Scheduling admin hub βœ… Done
6.2 Show format policies & genre hour fill βœ… Done
6.3 Clock templates βœ… Done
6.4 Clock-driven hour log generator βœ… Done
6.5 Campaign trafficking & rotation βœ… Done
6.6 Schedule rules & auto hour build βœ… Done
6.7 News feeds (hourly import, clock NEWS) βœ… Done
6.8 Studio live mic (arm, VAD duck, monitor) βœ… Done
6.9 Myriad log/library UX (menus, track info) βœ… Done
6.10 Library metadata (genres, facets, normalize) βœ… Done
6.11 Presenter show prep (studio briefing panel) βœ… Done
6.12 Dedicated track edit window (Myriad-style) βœ… Done (phase 1)
6.13 Cross-hour no-repeat (station playout setting) βœ… Done
6.14 Request system (public form, moderation) βœ… Done
6.15 Playlist editor & show builder βœ… Done
7A Play log export & compliance CSV βœ… Done
7A+ Reports hub rebuild (ISRC/album enrichment, track summary, PDF/XLSX/CSV) βœ… Done
7B AI / log-aware voice links ⬜ Planned: folded into Phase 8C (the AI presenter); reuses the 7E voice-track segue
7C On-air time-stretch (backtiming) βœ… Done (12 Jun): AutoFade playbackRate applied physically via process:/atempo in the pull URIs
7D E2E playout test suite ⏸ On hold: until feature-complete (13 Jun; surfaces still changing)
7E Voice tracking pro βœ… Done (12 Jun): auto-trim recorder, Myriad-style segue VT into the log (recorded overlap reproduced on air), remote VT page /studio/voice-track
7F Remote guest contributions (CleanFeed-style) ⬜ Planned
7G Hooks & intro/outro detection ⬜ Partial: worker hints on ingest; full detection TBD
7H Hospital content system βœ… Done
7I WebRTC program monitor (sub-300ms) ⬜ Planned
7J Storage optimisation (single MP3 + cache) ⬜ Planned
7K Multi-station manager ⬜ Planned
7L Live ops hardening (harbor health, webhooks) ⬜ Planned
7M Stream relays (external Icecast/Shoutcast distribution) βœ… Done
7N Listener statistics (snapshots, TTSL, trends) βœ… Done
7O Media tags (tag pools, playlist tag rows, automation resolution) βœ… Done
7P Podcast sources (RSS subscribe, auto-download, auto-import) βœ… Done
7J Storage optimisation (single copy + cache) πŸ”Ά Mostly done (15 Jun): two-mode store shipped: local /data/audio home by default, or external S3 home + size-capped local LRU cache; MinIO removed, no on-disk doubling. Remaining: tune cache eviction under real disk pressure
7Q Segue editor pro (wavesurfer multitrack) βœ… Done (18 Jun): drag-overlap multitrack editor, drawn per-track volume envelopes that play on air (clock-safe time-varying amplify), and in-editor voice-link recording with auto-duck. Phases 1–4; see below
7R Real waveforms & studio telemetry βœ… Done (17 Jun): ingest-time mono peaks (Track.waveformData), SoundCloud-style canvas renderer + decode/prefetch cache, play-duration telemetry, unattended recovery
7S Per-presenter remote DJ broadcasting (show-gated) βœ… Done (18 Jun): per-DJ harbor logins (dynamic input.harbor auth), shows opt in + pick permitted DJs, evaluator fades the right DJ on air when their hour starts and back to the log when they leave; dashboard countdown + studio presence avatars; full user management (roles/active/delete). See below
8 AI audio creation & presenter πŸ”Ά In progress: TTS foundation (ElevenLabs voice gen in Voice Studio) and LLM-drafted presenter scripts (DeepSeek, show/clock/day-aware) shipped 14 Jun; admin AI + integration keys in DB; live-link insertion (8C.4) remains (see below)

Legend: βœ… Done Β· ⬜ Planned Β· ⏸ On hold Β· Partial = started, not production-complete


Recently completed (Jun 2026)

Shipped 14–18 June

  • Per-presenter remote DJ broadcasting (7S): remote DJs are no longer one shared password. Each presenter with Remote broadcast enabled (Admin β†’ Users) gets a personal harbor username + password, validated by a dynamic input.harbor auth callback (/api/internal/harbor-auth), the station-wide password remains a shared break-glass only. A show can be made remote-aware (Allow remote DJs + a permitted-DJ picker); a worker-tick evaluator applies the rule on-air = show.allowRemote AND a permitted DJ connected, so a DJ can connect early, fade on air when their hour starts, and fade back to the generated log when they leave or the show ends, with crossfade transitions on the program switch. It never overrides a manual REMOTE DJ takeover or emergency lock (Station.remoteShowAutoLive). The connected DJ sees a dashboard countdown (to-air / time-left); the studio header shows pulsing presence avatars (red on air, amber waiting). Plus full user management: edit roles, activate/deactivate, hard-delete (with self-lockout guards). Schema: User.canRemoteBroadcast/remoteLogin/remotePasswordEnc, Show.allowRemote + ShowRemotePresenter, LiveSource.connectedUserId/connectedLogin.
  • Segue editor pro (7Q): the old slider modal is replaced by a wavesurfer-multitrack editor. All tracks drag to set the overlap; you draw per-track volume envelopes over the waveforms and they play on air, applied by a clock-safe time-varying amplify in hospair_transition (envelopes stored as tailEnvelope/headEnvelope breakpoints + segueCrossfadeSec on QueueItem; no envelope β‡’ byte-identical to the classic transition). The mic records a voice link straight into the editor as a middle lane (auto-ducking the music); on save it uploads, waits for processing, and inserts the row between the songs via /api/queue/voice-track. Built in four phases (UI β†’ persistence β†’ live engine β†’ voice recording).
  • AI creation, first slices (8C): ElevenLabs voice generation in the Voice Studio (TTS clip β†’ mixer β†’ save as a library track); LLM-drafted presenter scripts (DeepSeek) aware of the show, clock, day and time of day, shown in the studio show-prep pane; Admin AI settings + third-party integration keys (Last.fm, National Highways) stored in the DB. Key reuse path from the 8C plan held, generated audio rides the existing voice-track/clock machinery.
  • Two-mode storage (7J), MinIO removed: replaced MinIO-as-home + local-cache (which doubled every track on disk) with: local mode (default) where /data/audio is the permanent home, no object store; or S3 mode where an external bucket is the durable home and /data/audio-cache is a size-capped LRU filled on demand and trimmed by the reaper. Ingest writes the original to /data/audio/raw, the worker transcodes locally and only uploads in S3 mode. Admin β†’ Storage shows the mode + local usage and offers manual migrate (localβ†’S3) and pull (S3β†’local).
  • Real waveforms & telemetry (7R): ingest generates mono peaks (Track.waveformData); studio renders a SoundCloud-style canvas waveform with an in-memory decode/prefetch cache, play-duration telemetry, and unattended recovery.
  • Library shell: /library nav, column picker, and dashboard tweaks.
  • Station branding & privacy: station branding (stream art/name), Icecast fixes, and listener-metadata privacy controls.
  • Scheduling & content: shows attach a pool/playlist with weighted music-vs-episode fill; per-content-pool play order (sequential / random / avoid-recent); drag-paint, relocate and right-click on the assignment grids; on-air show + clock highlighted in admin grids; a Station ID clock item drawn from the ID library category; show-end-aware content-pool picks.

Shipped 13 June (pull-engine reliability pass: branch fix/dead-air-truthful-hour-logs, on dev, pending soak)

  • Hours land on :00, truthfully: fill no longer closes the tail with an oversized song that overshoots the boundary by minutes; end-of-hour gaps fill with a single best-fit song (smaller preferred) and at most one jingle/station ID as a last resort (never a stack of idents). Verified future hours land within Β±2s of :00.
  • Advert breaks fill their reserved length: clock breaks reserve 120–180s but trafficking dropped in one ~30s spot, shrinking the slot and underrunning the hour by ~5 min. Breaks are now stacked to the reserved duration at generation time (distinct adverts β†’ jingle/ID filler), so they survive regeneration (no trafficking-vs-generator churn) and the hour stays on :00. Root cause of the recurring over/underrun: the oversized tail song and the break under-fill were cancelling each other.
  • Re-fill / clear no longer desyncs log ↔ audio: a fill now always reconcile-then-syncs (re-derive now-playing from what Liquidsoap is actually airing, then replace only the pending tail), removing the gate that could reset the serve cursor out from under live audio on a clear+refill. (Partial progress toward 5.3 PR 3's "gentler reconcile.")
  • Studio player handoff flap fixed: the now-playing widget no longer jumps next β†’ previous β†’ next during the crossfade window (stateless resolver reverted to the DB anchor at remaining=0); added forward-only hysteresis.
  • Login splash background: /images/ was caught by the auth middleware matcher and 307-redirected, breaking the login background; excluded it.

Shipped 5–11 June (post previous roadmap update)

  • Wall-clock catch-up (5.4): time-authoritative logs: hour rows keep frozen :00-anchored times; on startup, resync, and a routine tick the playhead fast-forwards to the row the wall clock says should be on air. Guards prevent skipping the next hour's leading rows or dragging the next hour in early. Unit-tested (apps/web/src/lib/wall-clock-catchup.test.ts).
  • Liquidsoap event push (5.5): Liquidsoap now pushes events to the control server (/internal/track-push on track start, /internal/dead-air-push on dead-air engage/clear) instead of relying purely on polling; control server fires webhooks to the web app. Control server also keeps a wall-clock playhead for when Icecast telemetry freezes.
  • Stream relays (7M): Admin β†’ Stream relay: push output to external Icecast/Shoutcast servers; relay config stored on Output rows, rendered into relay-outputs.liq by generate-relay-outputs.mjs; health + listener polling per relay.
  • Listener statistics (7N): periodic ListenerSnapshot per output; Admin β†’ Statistics with overview/historical/real-time tabs, peak/average/TTSL, CSV export.
  • Reports hub rebuild (7A+): playout log enriched with album, ISRC, category, playlist/tag name, presenter; track summary + recently played tabs; PDF/XLSX/CSV export.
  • Media tags (7O): station-defined tags with colour; tracks assignable; playlists support TAG placeholder rows resolved to a rotating track at automation/queue-build time; library tag filtering.
  • Voice Studio (7E phase 1): multi-track browser recorder/mixer for voice links: voice/music/SFX lanes, trim, per-track gain, fades, OfflineAudioContext mix-down to WAV, upload to library.
  • Podcast sources (7P): RSS feed subscriptions with check interval; episode discovery, selective or auto download, auto-import to playlist or content pool; PODCAST track category.
  • Playout stability fixes: duplicate log row dedupe (transition silence / mid-song skips), routine catch-up no longer skips queued items at transitions, AutoFade stretch budget credited in coverage (stops 10s re-stretch churn), next-hour generation no longer blocked on current-hour coverage, studio perf (single log scan per queue build, cache invalidation once per track change).

Playout sync & studio UI (5.1)

Major regression after Liquidsoap 2.3: playhead read delayed Icecast metadata first, causing log/UI desync and watchdog fast-forward.

  • radio.on_air_meta telnet command, authoritative playhead from automation bus (pre-Icecast)
  • Playhead priority: radio.on_air_meta β†’ request.metadata β†’ Icecast (file/id only, never title)
  • Faster track-change poll (500ms); filename settle reduced to 400ms
  • Studio display: telemetry-led on-air when ahead of DB; predictive handoff (~2s before track end)
  • Monotonic countdown: remaining time no longer bounces on noisy telemetry
  • Queue invalidation when telemetry track id changes
  • isNextQueuedAlreadyOnAir fix, no false fast-forward when remainingSec is null

Dead-air & streams (5.2)

  • Dead-air detection β†’ standby fallback engagement + webhooks
  • Icecast output polling reliability fixes

Hour log & music rules (6.13)

  • Admin β†’ Playout: No repeat across hours (hourLogNoRepeatHours, 0–168h)
  • Show-level noRepeatHours still overrides per hour
  • Burn window uses playout history + active queue (played, skipped, queued)

Liquidsoap 2.3 migration (part of 5.1)

  • Queue control API updates for LS 2.3
  • settings.request.deprecated_on_air_metadata for telnet playhead reads
  • Stale queue/audio fixes, skip settling, expected-next reconciliation

Completed work (historical summary)

  • Docker monorepo: web, worker, Liquidsoap, Icecast, Postgres, Redis, optional WebRTC bridge (audio on local disk, or an external S3 bucket, 7J; MinIO removed 15 Jun)
  • Auth (NextAuth), RBAC, audit log, health endpoints
  • Library upload β†’ worker ingest (ffmpeg) β†’ READY tracks
  • Queue CRUD, Liquidsoap sync, playout webhooks, crossfade, control server
  • Icecast public + wards; Harbor for remote encoders (/live, /browser-live)
  • Hour log with schedule labels, drag-to-log, hour fill, auto fill at :45/:50
  • Backtiming, hour lock, soft/absolute markers, SWEEP segues
  • Cart overlay ducking, anchor sync, replace_pending, emergency modes
  • Desync detection/Resync, catch-up heal, log-edit sync, studio-gated reconcile
  • Timezone-aware top-of-hour terminal fade (marker trim + crossfade skip into :00)
  • Dual/custom studio layouts, layout presets
  • SmartInfo (wiki, weather, traffic), voice-link recorder, keyboard shortcuts
  • Show Prep pane, on-air + next 8, backtiming pill, weather/news/traffic, guest notes
  • Go / STANDBY / awaitingGo, backtiming warnings in UI
  • Live mic: ARM MIC β†’ PCM β†’ Harbor; VAD duck; YOU (foldback) vs MONITOR (program ~200ms)
  • Remote DJ via BUTT/Harbor /live
  • Context menus on station log & library; Media Item Information popout
  • Track edit window (/track-edit): titling, timing, restrictions, waveform, FFmpeg tools
  • Log edit: insert palette, swap rows, play heat map
  • Media wall, segue editor, studio monitor feed (OGG/Opus proxy)
  • Scheduling hub: shows, weekly rules, playlists, clocks
  • Genre/duration/no-repeat policies per show; clock editor with timeline
  • generateHourLog(), active show + clock (breaks, jingles, NEWS, hospital slots)
  • Campaign trafficking (/admin/ad-breaks); auto hour log pre-window when enabled
  • News feeds: hourly URL import, clock NEWS rows, hard-cut playout
  • Music rules: artist/title separation, burn hours, genre weighting, decade balance
  • Genre normalization, multi-genre facets, batch normalize, studio filters
  • Hospital content: HOSPITAL clock slot, pools, weighted rotation, expiry
  • Request system: public /requests, moderation, approve β†’ log
  • Playlist editor (/admin/playlists/[id]), apply running order to log
  • Play log CSV export (/admin/reports)

Outstanding backlog

Priority 1: Reliability (do before new features)

ID Item Notes
5.3 Liquidsoap 2.4.4 pull queue Replace telnet push/flush with HTTP snapshot + request.dynamic; enables stable crossfade: liquidsoap-roadmap.md
7D E2E playout test suite ⏸ On hold until feature-complete (13 Jun). Scope when resumed: pure invariant tests (hours land on :00, breaks fill reserved length, no oversized tail, re-fill preserves on-air row) β†’ DB-backed generateHourLog integration β†’ snapshot-API smoke β†’ npm test glob + GitHub Actions
7L Live ops hardening Fix live webhook schema; harbor connect presence; per-session tokens; liveHealthTimeoutSec. Note: dead-air detection + webhook already exist (5.5): remaining gap is routing them to human notifications (ntfy/email)
7C On-air time-stretch ffmpeg atempo via queue URI after 5.3 pull queue ships; unlocks softer backtiming
- PlayoutLog catch-up Ensure history panel stays in sync when webhooks lag (optional PlayoutLog writes on heal)
- Deploy discipline Rebuild migrate image whenever schema changes (prevents /api/station 500)
- Top-of-hour fade reliability Surfaced 13 Jun: the boundary FADE is scheduled but occasionally never fires (one hour ran its tail full instead of fading at :00): the on-air safety net. Investigate checkTopOfHourFade timer in control-server.mjs.
- Live-hour top-up churn Surfaced 13 Jun: the live hour's tail is rewritten on most worker ticks (planned-vs-playout projection). Cosmetic now (lands on :00) but the documented dead-air trigger: make the live top-up idempotent.
- Backtiming drift display Surfaced 13 Jun: DRIFT can read +3600s / always-red transiently when next-hour rows are momentarily untagged during re-fill churn (projectLogEndBeforeTarget). Self-corrects; cosmetic.
- LS 2.4.x prefetch desync Editing the immediate-next track can mismatch log↔audio for one track (engine can't flush an already-prefetched track): architectural; minimise by not replacing the prefetched row on re-fill.

Priority 2: Studio polish

ID Item Notes
- Dual/custom studio live Wire ARM MIC + bridge to /studio/dual and /studio/custom
- Myriad log edit polish Replace/insert row types, multi-select swap
6.12+ Track edit phase 2 Richer ribbon; per-clock restriction rules
7G Hook detection Refine worker peak analysis; manual re-analyse in edit window
7I WebRTC program monitor Sub-300ms MONITOR (today ~200ms via Icecast proxy)

Priority 3: Audio & remote

ID Item Notes
7F Remote guest contributions CleanFeed-style: see phased plan below
7E Voice tracking pro Auto-trim links, segue preview, remote VT
- Audio processor Station AGC/limiter/EQ in Liquidsoap

Priority 4: Platform & scale

ID Item Notes
7J Storage optimisation πŸ”Ά Mostly done: two-mode store shipped (local home, or external S3 home + local LRU cache; MinIO removed). Remaining: tune AUDIO_CACHE_MAX_BYTES eviction under real disk pressure
7K Multi-station manager Central admin, shared library option
7B AI voice links Log-aware generated links: now Phase 8C.4 (see below)
8 AI audio creation & presenter Creation studio first (8C); schedule assistant (8A/8B) deferred

Phase 7F: Remote guest contributions (CleanFeed-style)

Today: one studio mic (ARM MIC over automation) and one remote DJ takeover (Harbor /live). No invite links, guest roster, mix-minus, or talkback.

Sub-phase Goal Key work
7F.0 Harden live ops Webhook schema, harbor presence, scoped tokens (7L)
7F.1 Single guest MVP Harbor /guest-1, invite token API, guest join page, host mute fader
7F.2 Mix-minus Liquidsoap monitor bus (program minus guest); downlink to guest browser
7F.3 Talkback + roster Host TALK β†’ guest private; join/leave/mute UI; audit log
7F.4 Multi-guest WebRTC SFU (LiveKit/mediasoup) + TURN; per-guest levels; Opus path

Builds on: Harbor ingest, webrtc-bridge WS→ffmpeg pattern, VAD ducking, RBAC.


Phase 8: AI audio creation & presenter

Current focus = the creation studio (8C). Let users generate jingles, station IDs, and "live" presenter links, script + voice, and drop them into the library/log. The schedule assistant (8A/8B) is deferred behind it.

Key reuse: this is not a new playout path, and mostly not new plumbing. A generated link is just a voice track whose audio came from TTS instead of a mic, so it rides the existing 7E segue machinery (segueOverlapSec on QueueItem, /api/queue/voice-track). Generated IDs/jingles are ordinary library tracks (ID/JINGLE) that feed the clock slots already built (STATION_ID/JINGLE). The only genuinely new capability is text β†’ speech.

Decisions (14 Jun): TTS = ElevenLabs (realism + low latency + voice cloning). "Jingles" = spoken-over-bed now, sung/produced jingles a later phase. Voices = stock now, presenter cloning a later opt-in.

8C: AI audio creation studio (build slices)

Slice Goal Key work
8C.1 TTS foundation + Station IDs βœ… Shipped 14 Jun: ElevenLabs voice generation in voice-studio.tsx: generate speech β†’ clip in the mixer β†’ save as a library track (feeds the STATION_ID/ID slot). Provider key + AI settings live in Admin (DB), not env.
8C.2 Spoken-over-bed jingles/sweepers Layer generated speech over a BED track and mix down (the Voice Studio already does OfflineAudioContext mixdown, voice-studio-audio.ts); save as JINGLE.
8C.3 LLM drafts the script βœ… Shipped 14 Jun (provider: DeepSeek, not Claude): presenter scripts aware of the show, clock, day and time of day, drafted server-side and shown in the studio show-prep pane; editable.
8C.4 "Live presenter before a track" ⬜ Remaining glue: Studio log β†’ Add AI link before β†’ context (prev/next track) β†’ script (editable) β†’ TTS β†’ insert VT row that talks over the next intro (7E). Script-drafting (8C.3) + TTS (8C.1) + in-editor voice-link insertion (7Q) now all exist; wire them together. The headline feature.
8C.5 Presenter voice cloning Opt-in per presenter: recorded consent β†’ ElevenLabs clone β†’ map presenter β†’ voiceId.
8C.6 Sung / produced jingles Melodic jingles via a music-generation model (Suno/Udio-class): separate provider, later phase.

8A/8B: schedule assistant (deferred)

Sub-phase Goal Key work
8A Schedule assistant: read-only Admin chat; claude-opus-4-8 + tool-use loop over existing queries (get_request_summary, find_underfilled_hours, explain_log_row). No on-air impact.
8B Assistant: gated writes Dedicated tools (propose_clock_edit / fill_hour / swap_track) with confirm-before-apply on anything touching the live log. Claude API tool runner, not Managed Agents (tools are our own lib ops).

Guardrails (carried from existing lessons)

  • Degrade gracefully: a missing/failed TTS render must never reach air as dead air, an unfilled ID/jingle slot falls back to music, same as an empty jingle slot today. With no ELEVENLABS_API_KEY the generate controls disable cleanly with a clear message.
  • On-air contract: generated audio goes through the same normalisation/cue/duck rules as recorded voice tracks, generation must not bypass loudness/segue handling.
  • Editorial scope: hospital station, live presenters are part of the value; AI links target unstaffed hours and automation, not daytime shows.

Builds on: 7E voice-track segue + Voice Studio mixer, clock item types (6.3 + the STATION_ID pattern), ID/JINGLE/BED categories, track upload/ingest, clock-driven generator (6.4).


What to focus on next

7D (test suite) is on hold until the feature set is complete. Decision 13 Jun: new features keep reshaping the surfaces a test would assert against (hour fill, projections, sync), so investing in tests now means re-writing them as features land. Build features to "feature-complete" first, then lock behaviour in with 7D. Until then, keep verifying by ear and record regressions in the issues table.

Now (this week)

  1. Verify the segue editor (7Q) by ear: record a voice link into a real upcoming segue and confirm: the green voice lane appears and drags, the music auto-ducks, and the drawn envelopes play on air where they were drawn (sub-second buffering offset is expected). Built + deployed 18 Jun; not yet exercised on a live segue.
  2. AI live-link insertion (8C.4): wire LLM script (DeepSeek) β†’ ElevenLabs TTS β†’ inserted voice-track row that talks over the next intro. The script-drafting and TTS pieces now exist; the studio "Add AI link before" flow is the remaining glue.
  3. 24h soak on the pull engine (5.3 PR 4 / LS-8): boundaries land on :00 and re-fill no longer desyncs; soak it while watching the fade-reliability and live top-up churn items above.
  4. Top-of-hour fade reliability: the boundary fade is the on-air safety net and it intermittently didn't fire (13 Jun). Worth a focused look before the soak is trusted.

Next (1–2 weeks)

  1. 5.3 PR 3, delete the push path: remove pushTrack/mergeCommittedItems/orphan-heal heuristics (~800 lines) and calm the web's forceResync storms. The 13 Jun "always reconcile-then-sync" change is a step toward this; finishing it removes a class of churn.
  2. 7L, Live ops hardening: dead-air webhooks exist; route them to human notifications. Unblocks guest work, makes Admin β†’ Broadcast trustworthy.
  3. Volunteer studio pass: real shows on /studio: mic, Go/Skip, hour lock, news slots, show prep, track edit.
  4. Re-pin Liquidsoap to v2.4.5 when it releases (currently rolling 2.4.5-dev digest).

Then (2–4 weeks)

  1. 7J, Storage optimisation: disk pressure at scale; staging cache only.
  2. 7C, On-air time-stretch: after 5.3 pull queue is stable.
  3. 7F.1, Single guest: if live interviews are a near-term need.

Defer

  • AI (7B, 8), multi-station (7K), full WebRTC monitor (7I), audio processor, until core playout is boringly reliable.

Architecture

flowchart TB
 subgraph admin [Admin / Scheduling]
 Grid[Schedule grid]
 Show[Show + format policies]
 Clock[Clock templates]
 News[News feeds]
 Gen[Hour log generator]
 end
 subgraph studio [Studio]
 Log[Station log]
 Mic[ARM MIC + monitor]
 Transport[Go / Skip / Pause]
 Carts[Media wall / carts]
 end
 subgraph engine [Engine]
 Web[Next.js API]
 LS[Liquidsoap]
 IC[Icecast]
 end
 Grid --> Show --> Clock --> Gen --> Log
 News --> Clock
 Log --> Web --> LS --> IC
 Mic --> LS

Playhead (5.1): radio.on_air_meta β†’ DB queue β†’ studio UI (telemetry leads near transitions).


Operations & docs

Topic Location
Liquidsoap engine plan liquidsoap-roadmap.md
Volunteer runbook OPERATIONS.md
Deploy & migrations README, OPERATIONS
Nginx / Cloudflare NGINX-PROXY.md, CLOUDFLARE-DNS.md
Studio /studio, /studio/dual, /studio/custom
Admin /admin/*
News feeds /admin/news
Playout settings /admin/playout
Live / remote DJ Admin β†’ Broadcast; Studio β†’ ARM MIC / Remote DJ