fix(podcast): the left rail showed the workforce, not the episodes
deploy / test (push) Successful in 4m20s
deploy / build (push) Successful in 5m56s

The PODCAST tier had no branch in the left column, so it fell through to the
default — the org/company/team roster. Opening the podcast page showed a list of
agents, which is the one thing on that screen that has nothing to do with it.

`PodcastList` now fills the rail with one card per episode (date, title,
duration, size), the same shape `MissionsList` and `RepoList` give their tiers:
objects in the rail, the selected one in the canvas. It selects the newest on
first load so the canvas is never blank, and refreshes on the render sweep's
own two-minute cadence so a new episode appears without a reload.

`PodcastPanel` loses the duplicated list and becomes what a canvas should be:
how to subscribe, and the selected episode with a player. The empty state points
at the Continuous Research mission that produces one rather than just saying
there is nothing.

Verified on the served page: PODCAST renders between AGENT and REPOS.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Omar Sobh
2026-08-18 12:15:17 -07:00
co-authored by Claude Opus 5
parent fe45f72f09
commit ba98c29481
5 changed files with 193 additions and 98 deletions
+7 -3
View File
@@ -230,7 +230,9 @@ pub async fn list_episodes(
"SELECT e.id, e.episode_date, e.title, e.bytes, e.duration_secs,
e.rendered_by, e.created_at, e.mission_id, m.title AS mission_title
FROM podcast_episodes e
JOIN missions m ON m.id = e.mission_id
-- LEFT: an episode outlives its mission (migration 0080). An inner
-- join would hide exactly the back-catalogue that change protects.
LEFT JOIN missions m ON m.id = e.mission_id
WHERE e.workspace_id = $1 AND e.bytes > 0
ORDER BY e.created_at DESC
LIMIT 50",
@@ -246,8 +248,10 @@ pub async fn list_episodes(
let created: time::OffsetDateTime = r.get("created_at");
serde_json::json!({
"id": r.get::<uuid::Uuid, _>("id"),
"missionId": r.get::<uuid::Uuid, _>("mission_id"),
"missionTitle": r.get::<String, _>("mission_title"),
"missionId": r.get::<Option<uuid::Uuid>, _>("mission_id"),
"missionTitle": r
.get::<Option<String>, _>("mission_title")
.unwrap_or_else(|| "(mission deleted)".to_string()),
"title": r.get::<String, _>("title"),
"date": r.get::<String, _>("episode_date"),
"bytes": r.get::<i64, _>("bytes"),