The worker rewrote <title> and og: tags for episode pages, but only when the
User-Agent matched a hardcoded social-crawler list. Googlebot, Bingbot, GPTBot,
ClaudeBot and PerplexityBot were all absent, so search and answer engines only
ever saw the generic shell. Serving crawlers different HTML than users is also
cloaking, which Google disallows.
Episode pages are static now, so the injection is dead code. Replaces it with a
301 from /episode.html?slug=X to /episode/X/. Published YouTube descriptions and
social posts still use the old form, so the redirect stays indefinitely.
The slug is restricted to [a-z0-9-] before it reaches the Location header:
verified that //evil.com, ../../etc/passwd, a CRLF payload and "><script> all
reduce to harmless on-domain paths.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The renderer emits .transcript-turn and .transcript-speaker, neither of which
had any CSS, so speaker labels and turns rendered unstyled. Extends the
existing .speaker-label rule rather than duplicating it.
Bumps style.css to v=7 everywhere — the file changed, and pages referenced a
mix of v=5 and v=6, so some visitors would have kept a cached copy without the
new rules.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Transcripts for episodes 39-58 existed only on disk. Wrangler deploys the
directory rather than the git tree, so the live site served them while git
never had them — a fresh clone or worktree came up 19 files short.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The site carried no geographic signal at all: the homepage title, description
and schema keywords never mentioned Alpine, Marfa or the Big Bend, and llms.txt
still described the show as broadcasting from a fictional desert hermit's RV
and being produced in New Mexico.
- Homepage title, description and OG/Twitter tags now lead with Alpine, Texas
and name the surrounding towns; keywords put the location terms first
- PodcastSeries schema gains contentLocation with Alpine's real coordinates
(from BIG_BEND_TOWNS), spatialCoverage for the region, and an about array
covering Alpine, Marfa, Terlingua, Big Bend National Park and the Marfa Lights
- llms.txt: corrects the setting throughout, adds the region to Key Facts, and
answers "what places does the show talk about"
- Fixes the stale "37+ episodes" in five places, using 50+ so it ages better
- Corrects three claims that were also wrong on How It Works: 68 voice profiles
(now 82), adaptive call shapes (deleted system), and 5 recording stems (now 7)
Also adds the Tier 2 plan for crawlable per-episode pages.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The How It Works page still described the show as set in New Mexico and Arizona
and cited stats from the caller generator that was deleted in the redesign.
- Rewrites the geography section around the six Big Bend locales the batch
prompt actually carries, and moves the two caller examples to Marathon and
Alpine
- Replaces the four stat tiles, which counted a name pool, call shapes and
regulars that no longer exist, with figures taken from the current code:
callers per show, usable voice roster, identity fields, named regulars
- Swaps the 70/30 advice split for the real roster mix, drops the "1,000
calling reasons" pool, and replaces the call-shape prose with the
anti-collision rule that superseded it
- Names Silas and Crispin as the regulars instead of Leon and Shaniqua
- Corrects the stem count to seven and control room channels to eight, both
of which gained a Devon track
- Removes two claims that the show tracks energy and pacing at runtime; no
such state exists, so the text now describes the thematic matcher and the
roster mix instead
- Moves the eight homepage testimonials from New Mexico and Arizona towns to
Big Bend, including the two whose quotes named the old location
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Hide h1 (sr-only) on homepage — banner already shows show name
- Promote tagline as visual lead after banner
- Fix avatar gender: add .gender marker files, re-fetch on mismatch
- Clear stale avatar cache so all re-fetch with correct gender
- Blacklist Celeste voice from caller pool
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove cover art from hero (duplicated in clips below)
- Merge about section into hero for single flowing layout
- Center hero content, remove side-by-side layout
- Fix _match_voices_to_styles() bypassing BLACKLISTED_VOICES —
Sebastian could get assigned to non-Silas callers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Share buttons on episode and clip cards (Web Share API + clipboard fallback)
- NEW badge on latest episode card
- Sticky call-in CTA bar (appears after hero scrolls out)
- Daily AI Briefing newsletter cross-promote in footer
- Bump cache versions to v=5
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Website:
- Add persistent top nav across all pages
- Add skip-to-content links, focus-visible styles, ARIA on audio player
- Fix text contrast for WCAG AA compliance
- Add 600px breakpoint, mobile typography scaling
- Extract shared footer.js, player.js, episode.js components
- Episode pagination (10 + Load More), featured clip dedup
- Worker meta injection for social crawler OG tags
- Unify Plausible analytics proxy across all pages
- Sanitize innerHTML for XSS safety
- Custom 404 page, enhanced llms.txt, fix sitemap
- Bump cache versions to v=4
Reaper:
- Add dual silence threshold: 2.5s for speaker transitions, 6s for same-speaker gaps
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add real-time LLM/TTS cost tracking with live status bar display and post-show reports
- Fix PTT bug where Devon suggestion layout shift stopped recording via mouseleave
- Devon: facts-only during calls, full personality between calls
- Double WEIRD topic pool (109→203), bump weight to 14-25%
- Auto-generate YouTube thumbnails with bold hook text in publish pipeline
- LLM SEO: llms.txt, robots.txt for LLM crawlers, structured data, BreadcrumbList schemas
- Publish episode 37
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Massively expanded all 8 caller topic pools from ~1200 to ~2500 entries to
reduce repeat calls. Added persistent topic history (data/used_topics_history.json)
with 30-day aging to prevent cross-episode duplicates. Published episode 35.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Whisper base → distil-large-v3 for much better live transcription accuracy
- Add context hints to transcription (caller name, screening status)
- Increase beam_size 3→5 for better decoding
- Add explicit role clarification in caller system prompt so LLM knows Luke is the host
- Prefix host messages with [Host Luke] in LLM conversation
- Fix upload_clips episode list sorting (natural numeric order)
- Episodes 26-28 transcripts, data updates, misc fixes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix TTS text pipeline: new caps handling (spell out unknown acronyms, lowercase
emphasis words), action-word lookahead for parenthetical stripping, abbreviation
expansions (US→United States, NM→New Mexico), pronunciation fixes
- Inworld TTS: camelCase API fields, speakingRate per-voice overrides, retry logic
with exponential backoff (3 attempts)
- Footer redesign: SVG icons for social/podcast links across all pages
- Stats page: show "Rate us on Spotify" instead of "not public" placeholder
- New voices, expanded caller prompts and problem scenarios
- Social posting via Postiz, YouTube upload in publish pipeline
- Episode transcripts 15-25, terms page, sitemap updates
- Fix invoice script: match Timing totals using merged Task+App intervals
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bypass flaky YouTube RSS ingestion by converting MP3+cover to MP4
and uploading via YouTube Data API. Videos are auto-added to the
podcast playlist. Includes yt_auth.py for token management.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Returning callers now keep their voice across sessions (stored in regulars.json)
- Backfilled voice assignments for all 11 existing regulars
- Discord button on homepage + link in all page footers
- REC and On-Air buttons now toggle together (both directions)
- Fixed host mic double-stream bug (stem_mic vs host_stream conflict)
- SEO: JSON-LD structured data on episode + how-it-works pages
- SEO: noscript fallbacks, RSS links, twitter meta tags
- Episode 9 transcript and sitemap update
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add podcast_stats.py with --json/--upload flags for BunnyCDN
- Add website/stats.html fetching stats from CDN
- Add stats CSS styles
- SEO: shorten title/description, add og:site_name, twitter cards,
theme-color, image dimensions, consistent favicons and cache-busting
- Add all episode transcript pages to sitemap.xml with lastmod
- Auto-add new episodes to sitemap in publish_episode.py
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Re-label all 8 episode transcripts with LUKE:/CALLER: speaker labels
using LLM-based diarization (relabel_transcripts.py)
- Add episode.html transcript page with styled speaker labels
- Update publish_episode.py to generate speaker-labeled transcripts
and copy to website/transcripts/ for Cloudflare Pages
- Add SVG favicon with PNG fallbacks
- Fix CPU issue: tie host audio stream to on-air toggle, not per-caller
- Update how-it-works page with post-production pipeline info
- Add transcript links to episode cards in app.js
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- On-air toggle uploads status.json to BunnyCDN + purges cache, website
polls it every 15s to show live ON AIR / OFF AIR badge
- Publish script downloads Castopod's copy of audio for CDN upload
(byte-exact match), removes broken slug fallback, syncs all episode
media to CDN after publishing
- Fix f-string syntax error in publish_episode.py (Python <3.12)
- Enable CORS on BunnyCDN pull zone for json files
- CDN URLs for website OG images, stem recorder bug fixes, LLM token
budget tweaks, session context in CLAUDE.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New stem recording system captures 5 time-aligned WAV files (host, caller,
music, sfx, ads) during live shows. Standalone postprod.py processes stems
into broadcast-ready MP3 with gap removal, voice compression, music ducking,
and EBU R128 loudness normalization.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ads playback system with backend endpoints and frontend UI
- Diversify AI callers: randomize voices per session, expand jobs/problems/interests/quirks/locations
- Update website tagline and descriptions to "biologically questionable organisms"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Expanded meta description with keywords
- Canonical URL
- JSON-LD PodcastSeries structured data
- RSS alternate link for feed discovery
- robots.txt allowing all crawlers
- sitemap.xml
- llms.txt for LLM indexing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Warm rustic bar palette (dark wood browns, orange neon accent, cream
text) replacing the previous navy/pink theme. Added ?v=2 to all
cover art URLs to force new image.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fetches episodes from Castopod RSS feed, renders episode cards with
play buttons, and provides a sticky bottom audio player with progress
bar and seeking. Falls back to CORS proxy if direct fetch fails.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Static site for lukeattheroost.com with cover art, phone number,
subscribe buttons (Spotify, YouTube, Apple, RSS), and OG meta tags.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>