v0.11.5 — podcaster icons and episode media restored
Patch release. The headline fix restores podcast cover art and episode media across the site; the rest is the social-autopublish pipeline reaching a working end-to-end run.
User-facing
Podcaster icons and episode media are back (#529)
tempfile.mkstemp creates files at 0600 and os.replace preserves that mode, so every artifact written into the media store was unreadable by Caddy, which runs as a different user. The result was a 403 on the entire media tree — podcast cover art, episode mp3s, transcripts and summaries alike. Both writers (backend/src/services/gcs_content.py and pipelines/services/podcast/src/service/gcs_storage_service.py) now chmod 0644 before the atomic rename.
The same incident had a second face: every avatar was a bare <img> whose PodMark fallback only fired when the URL was absent, never when it failed to load. With alt="" that renders as an empty box, which is why subscribed channels appeared to have no icons at all. A new PodAvatar component falls back on onError, and all six avatar call sites route through it — so a dead media URL now degrades to the initial mark instead of a hole.
Existing files on the VPS were repaired with a one-time chmod; this release stops new writes from recurring the problem.
Pipelines — social autopublish end-to-end
The Threads / vocus / Substack autopublish path now completes a full scheduled run. Fixes in this release:
- Threads copy is carried through to the episode and its trigger result is read from the platforms envelope (#523, #528)
- Threads copy voice rewritten with its own pinned model (#520)
- Syndicate step reads the summary key the pipeline actually writes; the endpoint accepts
publish_substackinstead of dropping it (#524, #525) - Deterministic row pick when one title exists under two episode ids (#525)
- Admin calls go to staging's direct origin rather than through Cloudflare (#528)
- Ingest: every show gets a usable episode limit, runs that did nothing no longer report success, psycopg gets a URL it can parse, and a recovery path when an endpoint refuses to disable reasoning (#518, #519, #521, #522)
Full changelog: v0.11.4...v0.11.5