Commit Graph
100 Commits
Author SHA1 Message Date
lukeandClaude Opus 5 61d24ae7d8 Style transcript turns and bump style.css cache version
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>
2026-08-14 03:52:06 -05:00
luke 56a6a2dfbe Add episode page renderer with PodcastEpisode schema 2026-08-14 03:46:01 -05:00
luke fdb9f57660 Add RSS feed loader for episode page generation 2026-08-14 03:41:53 -05:00
luke cc0d9ffc5d Add transcript parser for episode page generation 2026-08-14 03:34:14 -05:00
lukeandClaude Opus 5 f902eab701 Track the 19 episode transcripts that were never committed
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>
2026-08-14 03:29:18 -05:00
lukeandClaude Opus 5 8c71da543f Add Big Bend location signal to homepage metadata and llms.txt
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>
2026-08-14 03:28:16 -05:00
lukeandClaude Opus 5 5f7e3e7415 Update site copy for the Big Bend relocation and current caller system
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>
2026-08-14 03:14:59 -05:00
lukeandClaude Opus 5 00ddc2d1b4 Recover real episode metadata on --resume instead of rebuilding from the slug
A --resume run rebuilt the episode title from the Castopod URL slug, which is
lowercased and stripped of punctuation, so episode 58 went to YouTube as
"Episode 58: Rayfield S Nephew The Marfa Lights And Why Nobody Believes Concho"
with a placeholder description. save_chapters() only persisted chapters, so
resume had no real metadata to fall back on.

Metadata is now written to <audio>.metadata.json during a normal run and read
back on resume. If that file is missing (episodes published before this change)
the title and description are read from Castopod instead. Slug reconstruction
survives only as a last resort: it warns loudly, flags the result, and anchors
the episode prefix strip so it no longer eats the phrase mid-title.

Reading back from Castopod needed its own fix — TO_BASE64 wraps every 76 chars
and mysql renders those breaks as a literal backslash-n, which made b64decode
throw and the lookup silently return None.

Also includes two changes that were already sitting in the working tree: the
YouTube tag budget fix that episode 58's invalidTags failure prompted (with its
test), and a metadata model bump to claude-haiku-4.5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 02:54:29 -05:00
lukeandClaude Opus 4.6 1c7ac334b4 Collapse caller gen to single batch of 10 with anti-collision rule
Previously _build_backgrounds ran two parallel Sonnet calls of 6
callers each, with zero cross-batch awareness. Each batch independently
gravitated to the same quirky archetypes (Coast to Coast weirdos,
specific hobbies) and produced clustered rosters — one recent show
had two BBQ competitors, two taxidermists, and two conspiracy-pattern
callers across 10 slots.

Collapses to a single Sonnet call generating all 10 at once so the
model can self-diversify across the full roster. The BATCH_SYSTEM_PROMPT
also gets an explicit ANTI-COLLISION RULE with concrete forbidden
examples (no two BBQ competitors, no two taxidermists, no two mystery-
signal callers, etc.) and an instruction to scan output for collisions
before finalizing.

max_tokens bumped 8000 -> 16000 to accommodate the larger response.
Wall-clock cost: ~30s -> ~60s, acceptable for the diversity gain.
Verified on a fresh reset: 10 fully differentiated callers, zero
archetype collisions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 02:05:17 -06:00
lukeandClaude Opus 4.6 8a20e679f6 Fix caller name mismatch after mid-session theme change
Setting a show theme after the caller lineup was loaded caused every
caller button to keep its pre-theme label while the LLM dialog used
the post-theme bg names — host says "Hi Phil", caller replies "it's
Cody, actually". Three underlying bugs, all fixed here:

- _regenerate_backgrounds_for_keys ignored its keys parameter and
  replaced the entire caller_backgrounds dict, clobbering used slots
  along with unused ones. Now only the listed slots are touched.

- set_show_theme's used_keys detection compared record.caller_name
  (the slim bg name) against CALLER_BASES[k]["name"] (the randomized
  fallback name) — two different namespaces that never match, so
  every slot was flagged unused. Now it matches against
  session.caller_backgrounds[k]["name"].

- set_show_theme fired the regeneration as a detached asyncio task,
  so the POST returned before bg was consistent. Even if the frontend
  did reload /api/callers, it would race the regen. Now awaited.

Frontend setShowTheme/clearShowTheme now call loadCallers() after
the theme POST resolves so the button list actually refreshes, with
a "Regenerating..." button state during the ~30-60s wait.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 02:00:31 -06:00
lukeandClaude Opus 4.6 35ff78a922 Merge branch 'feature/caller-redesign' into main
Brings in the Phase 5B caller-generation rewrite:
- Slim two-stage pipeline: Sonnet 4.6 batch identity pregen at session
  start, Haiku 4.5 live dialog per turn. Deletes CallerBackground
  dataclass, shape/style/voice-matching, per-model routing, preflight UI.
- New caller_gen.py (slim prompt builder) and regulars_v2.py (Obsidian
  lore loader for named recurring callers).
- Reworked caller buttons and info panel around the slim background:
  identity, situation, signature, secret want. Removes shape badges,
  energy dots, emotion info-badges.
- Inworld TTS: emotional_register -> (temperature, speed_adjust) mapping
  via _emotional_register_to_params(). applyTextNormalization ON.
- Silas identity/voice leak fix, avatar gender, pre-warm batch gen.
- Archives old regulars to data/regulars.archived.json, adds 10 sample
  caller transcripts under docs/samples/.

Post-merge fixes applied during resolution:
- intern.py: kept main's richer new_show() (NEW SHOW history marker,
  trim, _save()) and _track_suggestion(); removed the branch's
  duplicate minimal new_show() stub.
- tts.py: added 5 voice speed overrides (Graham, Malcolm, Victoria,
  Loretta, Marlene) that main had tuned locally. Evelyn deliberately
  NOT added to VOICE_PROFILES — she is in BLACKLISTED_VOICES for
  unnatural prosody.
- Main's cost dashboard polish, LLM per-model params, and Devon show
  context memory from commits c087c03..61b3cba carried through cleanly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 23:43:56 -06:00
lukeandClaude Opus 4.6 61b3cba412 Publishing, website, reaper, and doc updates
Publish script, clip maker, website worker + data, reaper lua helpers,
audio settings, and CLAUDE.md doc reorg.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 23:39:57 -06:00
lukeandClaude Opus 4.6 44ac569fec Devon: track show transitions and avoid repeating suggestions
Adds new_show() that inserts a NEW SHOW marker into Devon's history
so he won't reference previous episodes' callers or topics, trims
older history while keeping some long-term memory, and clears
research_cache + pending suggestions. Buffered suggestions are now
tracked in history via _track_suggestion() so Devon won't suggest
the same thing twice. Devon history scoped to current show on ask.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 23:39:52 -06:00
lukeandClaude Opus 4.6 54ad04ae09 LLM: per-model params for caller_dialog + longer timeouts
Different models need different tuning for natural conversation:
Qwen gets high frequency penalty to fight repetition loops, Llama
gets warmer temp to reduce terseness, Grok/Mistral/DeepSeek/Kimi get
slightly warmer than Sonnet defaults. Bumps base httpx timeout from
10s to 30s and fallback per-call timeout from 8s to 20s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 23:39:45 -06:00
lukeandClaude Opus 4.6 c087c0362a Cost dashboard: TTS providers endpoint, header link, polish
Adds /api/costs/tts endpoint backed by get_tts_providers() in cost_db,
surfaces a Costs link in the control panel header, and small polish
to the costs page HTML/JS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 23:39:40 -06:00
lukeandClaude Opus 4.6 d0bb50fe12 Fix Silas identity/voice leak, avatar gender, and add pre-warm batch gen
session.caller was reading name/voice from CALLER_BASES randomized defaults
instead of the slim bg dict — causing Silas to appear as "Earl" with wrong
voice. Now prefers bg data when populated.

Also: avatar endpoint infers gender from voice pool, /api/callers returns bg
name, regulars get canonical voice force-locked, batch gen splits into two
parallel calls with pre-warm so subsequent resets are instant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:08:46 -06:00
luke 4e28f3fb84 Tune Inworld TTS: temperature, text normalization, emotional register mapping
Changes to backend/services/tts.py:
- Add temperature: 0.9 and applyTextNormalization: "ON" to Inworld payload
  (text normalization auto-speaks "$5,432", "Dr.", phone numbers, dates, etc.)
- Add _emotional_register_to_params() mapping caller's emotional_register
  to (temperature, speed_adjust) across 5 families: sadness/grief,
  anger/aggression, manic/excited, nervous/earnest, gruff/restrained.
- generate_speech_inworld() and generate_speech() now accept optional
  emotional_register kwarg; all providers take it via the dispatch lambdas
  (only inworld uses it; others ignore)
- Clamp speed to 0.5-1.5 after both emotional-register and per-text adjustments

In backend/main.py: plumb emotional_register through the two caller-dialog
TTS calls (auto-respond and ai-respond) by reading from the slim caller
background dict. Devon and cohost/announcer calls pass empty string and
hit the default (0.9, 0.0) branch — no behavior change.
2026-04-05 14:54:15 -06:00
luke 9e37fbf124 Phase 5B commit 6: delete CallerBackground dataclass, relationship_context, update docs
Final cleanup pass for the caller generation redesign:
- Delete CallerBackground dataclass + all isinstance() checks
- Delete orphaned _build_relationship_context and session.relationship_context
- Delete Session.get_caller_model (caller_dialog category routes to haiku-4.5)
- Delete dead _get_show_energy (CallRecord no longer tracks energy_level)
- Delete unused tone_streak field
- Drop topic_category/emotional_state/energy_level from CallRecord
- Simplify caller property, _find_thematic_match, enrich, promotion paths to slim dict
- Drop broken emotional_state=/energy_level= kwargs from generate_speech calls
- _load_checkpoint drops pre-slim schema backgrounds; startup re-pregens if empty
- Rewrite CLAUDE.md "Caller Generation System" section for the new two-stage architecture

35 tests passing. backend/main.py: 4981 -> 4810 lines.
2026-04-05 13:56:08 -06:00
luke 83c7f441a8 Phase 5B commit 5: delete template generation, content pools, and topic history
Template-based caller generation has been replaced by the batch sonnet-4.6
path (caller_gen.generate_batch). All the template-era content now goes:

Template functions (~700 lines):
- _generate_returning_caller_background{_sync}
- _generate_pool_weights, _is_spicy, _is_absurd, _filter_used
- _pick_unique_reason
- generate_caller_background (the big template fallback)
- _generate_caller_background_llm
- pick_location, _get_time_context, _get_moon_phase, _get_seasonal_context

Content pools (~4600 lines):
- PROBLEMS, STORIES, ADVICE, GOSSIP, HOT_TAKES, CELEBRATIONS, WEIRD,
  TOPIC_CALLIN, TV_TONIGHT, PROBLEM_FILLS
- INTERESTS, QUIRKS, PEOPLE_MALE, PEOPLE_FEMALE, RELATIONSHIP_STATUS,
  CALLING_FROM, MEMORIES, STRONG_OPINIONS, VERBAL_TICS
- JOBS_MALE, JOBS_FEMALE, LOCATIONS_LOCAL, LOCATIONS_OUT_OF_STATE
- CALLER_STYLES, CALLER_STYLE_KEYS

Topic history (~50 lines):
- TOPIC_HISTORY_FILE, _topic_history, _load_topic_history,
  _save_topic_to_history

Orphaned helpers:
- SHAPE_DIRECTIVES, _get_pacing_block, _get_speech_block

Rewrite _regenerate_backgrounds_for_keys to re-run _pregenerate_backgrounds
so the batch path handles theme-change regen too. Rewrite
Session.get_caller_background to be a pure getter — backgrounds are
populated by _pregenerate_backgrounds at session start, no lazy fallback.

Drop session.pool_weights field and its checkpoint save/load.
2026-04-05 13:38:54 -06:00
luke 9982f65742 Phase 5B commit 4: delete style/shape/voice-matching/model-routing systems
Backend cleanup:
- Delete STYLE_SPEED_MODIFIERS, STYLE_PHONE_QUALITY constants
- Delete _normalize_style_key, _match_voices_to_styles,
  get_style_speed_modifier, get_style_phone_quality functions
- Delete _pick_caller_style, _HEAVY_STYLES, _LIGHT_STYLES,
  _EVASIVE_STYLES (only called by template path)
- Delete _is_informal_style + _INFORMAL_STYLES
- Remove orphaned calls to deleted _assign_call_shape
- Drop shape parameter from _pick_response_budget
- Simplify _assess_call_quality (no more shape/style/pool_name)
- Delete Session fields: caller_styles, caller_shapes,
  caller_model_strategy/pool/map/fallback/models/_cycle_idx
- Remove those fields from Session.reset, checkpoint save/load,
  and session.caller property
- Drop style/shape lookups in chat callsites and promotion gate
- Update config.py caller_dialog model stale comment
2026-04-05 13:17:37 -06:00
luke ab811bda91 Delete caller-model routing + preflight admin UI
Backend (~273 lines):
- Removed GET/POST /api/caller-models endpoints (strategy/pool/map/fallback config)
- Removed POST /api/caller-models/{caller_key} override endpoint
- Removed GET /api/show/preflight diagnostics endpoint

Frontend:
- HTML: dropped Preflight header button, Caller Models settings panel,
  Preflight modal, and caller-model-badge/override from info panel.
- JS (~328 lines): removed loadCallerModels, saveCallerModels,
  updateCallerModelUI/Badges, overrideCallerModel, showCallerModelBadge,
  populateCallerModelOverride, runPreflight + render helpers,
  MODEL_ABBREVS, CALLER_STYLES, and all their listeners and init calls.
- CSS (~184 lines): removed .cm-*, .preflight-*, .caller-model-*,
  .model-tag, .info-badge.model, .caller-model-override rules.

Session fields (caller_model_strategy/pool/map/fallback/models,
caller_styles, caller_shapes) still present — they wire into shape
picking and checkpoint restore and get deleted in the next commits.
2026-04-05 12:42:59 -06:00
luke 13d613df5f Rework caller buttons and info panel for slim backgrounds
Backend:
- /api/callers now returns slim fields: identity, situation, signature,
  secret_want, voice (was: energy_level, emotional_state,
  communication_style, signature_detail, situation_summary, pool_name,
  call_shape).
- /api/call/{caller_key} caller_info swapped to slim fields too.

Frontend:
- Caller buttons stripped to name + shortcut + returning star. No more
  energy-dots or shape-badges — those systems are being deleted.
- Caller info panel shows identity (title) / situation / signature
  (quoted detail) / secret_want (amber italic, host-only).
- Removed .energy-dot, .shape-badge, .info-badge.shape/energy/emotion
  CSS rules. Added .caller-identity and .caller-secret-want.
2026-04-05 04:26:04 -06:00
luke effd52a95c Populate CallRecord from new slim caller backgrounds
Replace the CallerBackground isinstance branching in _summarize_ai_call
with direct reads from the new slim caller background dict
(emotional_register, situation, specific_details). Decouples CallRecord
population from caller_styles/caller_shapes so those fields can be
deleted in a later commit.

CallRecord schema preserved for backward-compat with existing
session_checkpoint.json. topic_category, emotional_state, energy_level
are now always empty strings (obsolete under slim architecture).
2026-04-05 04:21:05 -06:00
luke 76feeb22a0 Collapse caller generation to single path, remove feature flag 2026-04-05 04:05:09 -06:00
luke 3f85ee9140 Add sample caller dialogue generation script + 10 validation samples 2026-04-05 03:40:07 -06:00
luke 604769a949 Tighten slim prompt: hard-ban stage directions 2026-04-05 03:15:53 -06:00
luke 969f8fb2fd Wire batch gen into Session.reset behind CALLER_REDESIGN flag 2026-04-05 03:06:42 -06:00
luke d4f1043be1 Add feature flag for slim caller path (haiku-4.5) 2026-04-05 03:02:45 -06:00
luke a18e1329f3 Add slim caller prompt builder (~400 tokens) 2026-04-05 03:01:16 -06:00
luke 2fd027b82a Add writer for promoted tier-2 regulars 2026-04-05 02:56:20 -06:00
luke 0c5d36d182 Strip markdown fences in _call_sonnet response 2026-04-05 02:46:38 -06:00
luke d7e475331d Add promotion gate for tier-2 regulars 2026-04-05 02:43:32 -06:00
luke e4a18e13ab Archive non-Silas regulars for redesign cutover 2026-04-05 02:40:28 -06:00
luke 470e92f8c4 Add Regular dataclass + lore file loader 2026-04-05 02:36:25 -06:00
luke b792c3cca0 Strip markdown fences in parse_batch_response 2026-04-05 02:31:17 -06:00
luke baa6db5aa2 Add batch generation function using sonnet-4.6 2026-04-05 02:25:24 -06:00
luke dec6211f7c Add batch prompt builder for caller_gen 2026-04-05 02:23:34 -06:00
luke ade5510bd5 Add voice roster validator for caller_gen 2026-04-05 02:22:10 -06:00
luke f56ca3d4bc Add CallerIdentity dataclass and batch JSON parser 2026-04-05 02:14:58 -06:00
lukeandClaude Opus 4.6 a196e8f088 Add caller generation redesign implementation plan
22 tasks across 5 phases: scaffolding, regulars v2, main.py integration
behind feature flag, user validation gate, then deletion of ~2000 lines.
TDD where possible; manual verification of LLM outputs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 02:09:27 -06:00
lukeandClaude Opus 4.6 ca7e82f19f Add missing InternService.new_show() method
Session.reset() calls intern_service.new_show() to reset per-show state,
but the method was never defined (added in 376265e without the impl).
Clears pending_interjection, pending_sources, and research_cache.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 02:02:53 -06:00
lukeandClaude Opus 4.6 1b988a0303 Ignore .worktrees/ directory
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 01:58:32 -06:00
lukeandClaude Opus 4.6 fa491c1c27 Add caller generation redesign plan
Two-stage architecture: sonnet-4.6 pre-gen batch for rich identities,
haiku-4.5 for live dialog. Replaces static pools + 9-model routing +
scoring/weighting scaffolding. Introduces tiered regulars system with
Silas as canonical (lore in Obsidian vault) and hard-gated arc regulars.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 01:57:32 -06:00
lukeandClaude Opus 4.6 486842d2d1 Add cost dashboard frontend: HTML, CSS, JavaScript
- costs.html: summary cards, 4 Chart.js charts, expensive calls table,
  sessions table with drill-down, session detail view
- costs.css: dark theme matching existing style.css variables, responsive
  grid layout, card/table/chart styling
- costs.js: parallel API fetching, Chart.js rendering (timeline, doughnut,
  bar, session bars), session detail drill-down, period tab filtering

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 23:58:22 -06:00
lukeandClaude Opus 4.6 9c9f5c6602 Add cost dashboard API endpoints and page route
7 new endpoints: summary, timeline, models, categories, sessions,
session detail, expensive calls. All support period filtering.
Page route at /costs serves costs.html before the catch-all.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 23:49:42 -06:00
lukeandClaude Opus 4.6 73d555b269 Dual-write cost tracker records to SQLite
Each LLM/TTS call now writes to both in-memory records and SQLite
via cost_db. Session totals updated on save(). All DB writes wrapped
in try/except to never break live shows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 23:47:41 -06:00
lukeandClaude Opus 4.6 283e4a2f3c Fix cost_db review issues: thread safety, indexes, consistent filtering
- Add check_same_thread=False for background thread access
- Add standalone timestamp index for non-session queries
- Join to sessions table for consistent period filtering in get_models/categories/expensive_calls
- Wrap record imports in try/except with .get() for timestamp
- Add avg_cost_per_session to get_summary()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 23:46:40 -06:00
lukeandClaude Opus 4.6 0d2a0e42a8 Add SQLite cost analytics database module
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 23:39:57 -06:00
lukeandClaude Opus 4.6 376265eec7 Show quality fixes + preflight check
Ep47 post-mortem: fixed theme ignored by callers (backgrounds now
regenerate when theme is set), style-to-model race condition (fallback
to sonnet instead of pool[0]), removed bad pronunciation fixes, added
age-awareness to voice matching, raised MIN_RESPONSE_WORDS to 50.

Swapped problematic model mappings: conspiracy→qwen, know_it_all→mistral,
quiet_nervous→llama, emotional→kimi.

Added GET /api/show/preflight endpoint with 4 checks: model diversity,
theme penetration, voice-age alignment, response coherence (2-exchange
simulation of all callers). Frontend preflight modal with expandable
check cards.

Fixed active caller button not highlighting (moved highlight code before
potentially-failing caller info panel code).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 01:17:34 -06:00
lukeandClaude Opus 4.6 f3c91fc385 Devon personality + Whisper name fix + music vocal filtering
- Devon: more conversational when addressed directly (500 tokens, 3-5 sentences)
- Devon: monitor prompt rewritten to encourage more contributions
- Devon: polling interval 15s (was 30s), removed 2-message minimum
- Whisper: no fuzzy name matching for 3-char names, require first letter match
- fetch_music.py: post-fetch vocal detection filter using musicinfo tags
- scan_music_vocals.py: new script to scan existing library for vocal tracks

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 23:59:03 -06:00
lukeandClaude Opus 4.6 c69c2ad532 Fix tonight's show issues: Whisper bias, boring callers, Devon, short responses
- Remove caller names from Whisper hint (was corrupting transcriptions)
- Background gen switched to Claude Sonnet 4.6 (cheap models = thin backgrounds)
- "WHAT MAKES A GOOD CALLER" rewritten with concrete examples
- Grok guardrails loosened (were cutting too much edge)
- Response length guidance added to caller prompt
- Retry under-20-word responses once for more detail
- Devon monitor softened from "default silence" to balanced
- Ban stalling phrases: "where was I", "as I was saying", etc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 05:21:23 -06:00
lukeandClaude Opus 4.6 8dbbd92d3a Fix returning caller eligibility — 1+ calls, not 2+
The 2+ requirement created a catch-22: regulars couldn't return because they
needed 2 calls, but couldn't get a second call without returning. Dynamic
count already prevents flooding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 03:42:53 -06:00
lukeandClaude Opus 4.6 fa36f8d184 Dynamic returning caller count — need 3+ eligible for variety
Only inject 2 returners if pool has 3+ eligible (so it's not the same every show).
With 2 eligible, inject 1. With 1 or 0, inject none.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 03:38:47 -06:00
lukeandClaude Opus 4.6 794ad98cf0 Replace music dropdown with genre quick-select buttons
- One-click genre buttons play random track from that genre
- Active genre highlighted, now-playing bar shows track name
- Only genres with tracks shown, crossfade on genre switch
- M key replays active genre or picks random

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 03:34:44 -06:00
lukeandClaude Opus 4.6 f5eabd7dc4 Add fetch_music.py (Jamendo API) + expand genre keywords
- Downloads instrumental tracks from Jamendo by genre (jazz, lofi, blues, ambient, etc.)
- Filters: no vocals, 60-300s, sorted by popularity
- Saves to music/ with genre tags, tracks attribution
- Add genre keywords: ambient, chill, acoustic, classical, country, electronic

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 03:18:26 -06:00
lukeandClaude Opus 4.6 f717edeacb Fix style map key mismatch — API uses 'map', frontend was using 'style_map'
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 02:45:21 -06:00
lukeandClaude Opus 4.6 56607879ee Fix style-matched dropdowns — populate from full model list, not just pool
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 02:42:53 -06:00
lukeandClaude Opus 4.6 fcefabdaee Expand style-matched routing to 10 models for maximum caller variety
- Grok 4.1 Fast: high_energy, bragger, comedian, small_town_gossip
- Grok 4 Full: confrontational (needs deep reasoning for arguments)
- Claude Sonnet 4.6: quiet_nervous, emotional (genuine vulnerability)
- Kimi K2: sweet_earnest (warm, creative, different texture than Claude)
- Mistral Large: deadpan, mysterious (dry, precise)
- DeepSeek Chat: angry_venting (raw, unfiltered rage)
- DeepSeek R1 Distill: oversharer, conspiracy (commits fully, no hedging)
- Qwen: storyteller, rambling (loves tangents and detail)
- Gemini 2.5 Pro: know_it_all (pedantic, cites sources)
- Llama 3.3 70B: world_weary, reluctant, first_time (casual, natural)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 02:40:09 -06:00
lukeandClaude Opus 4.6 58495d2c75 Fix stale model detection — validate against current OPENROUTER_MODELS
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 02:37:21 -06:00
lukeandClaude Opus 4.6 51961dc19b Fix stale model map detection — check if all values are same model
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 02:33:47 -06:00
lukeandClaude Opus 4.6 c516402402 Update model routing with latest OpenRouter models
Style-matched defaults:
- Grok 4.1 Fast for edgy callers (high_energy, confrontational, comedian etc.)
- Claude Sonnet 4.6 for emotional callers (quiet_nervous, sweet_earnest, emotional)
- Mistral Large 2512 for deadpan/mysterious/world-weary
- DeepSeek R1 Distill for storyteller/oversharer/conspiracy/rambler
- Gemini 2.5 Flash for know_it_all
- Llama 3.3 70B for first_time/reluctant callers

Category routing: Grok 4.1 Fast for dialog/devon/backgrounds, Gemini Flash for monitor/summary
Updated OPENROUTER_MODELS and OPENROUTER_PRICING with all new models

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 02:31:33 -06:00
lukeandClaude Opus 4.6 e614599650 Fix checkpoint restoring stale caller model defaults
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 02:20:32 -06:00
lukeandClaude Opus 4.6 d36de95577 Default caller model strategy to style_matched
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 02:18:36 -06:00
lukeandClaude Opus 4.6 0147be4e0c Normalization diagnostics + SFX track support
- Detailed logging for normalize_track_items (item count, RMS, gain, applied/skipped)
- Add SFX track normalization (track 5)
- Will reveal why ad/ident normalization silently fails

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 02:14:34 -06:00
lukeandClaude Opus 4.6 390f138601 Devon improvements: independent audio, realism overhaul
- Devon audio independent of caller hangup (separate stop events)
- Personal anecdotes capped at ~30% of responses (was every time)
- Interjection criteria tightened ("default is silence")
- Devon sees his own recent history to avoid repeating info
- Response variety: permits minimal reactions, confusion, silence
- Monitor prompt rewritten to be gatekeeping, not encouraging

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 02:08:22 -06:00
lukeandClaude Opus 4.6 9eaf2fe5e3 Fix avatar misgendering, returning caller overflow, false callbacks
- Avatar prefetch checks gender marker, re-fetches on mismatch
- Returning callers need 2+ actual calls before re-eligible (was 1)
- Promotion rate lowered 10% → 5% to prevent pool flooding
- Callback injection skipped for returning callers (already have context)
- Show history clarifies "you are NOT that caller" to prevent identity confusion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 02:03:07 -06:00
lukeandClaude Opus 4.6 314d5f9452 Caller model routing — cycle, style-matched, mid-show override
- Three strategies: single model, cycle through pool, style-matched
- 18 communication styles mapped to 7 models (Grok, Sonnet, Mistral, Qwen, DeepSeek, Gemini, Llama)
- Per-caller model locked for entire call, overridable mid-show
- Model badges on caller buttons and info panel
- Settings UI for strategy, pool, style mapping, fallback
- Fallback to Sonnet on model failure
- 6 new models added to pricing and dropdown
- Checkpoint persistence for all model state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 01:58:03 -06:00
lukeandClaude Opus 4.6 e0fb3cac68 Make make_clips.py resilient — timeouts, retries, skip-on-failure
- 60s timeout + retry on all LLM calls
- 120-300s timeout on all subprocess/ffmpeg calls
- Per-clip error isolation (one failure doesn't kill the run)
- Progress indicators for each clip being processed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 17:36:41 -06:00
lukeandClaude Opus 4.6 4589670b37 Fix Whisper misspelling caller names — hint + fuzzy correction
- Pass all caller names as Whisper initial_prompt hint for correct spelling
- Post-transcription fuzzy match corrects remaining misspellings (Levenshtein)
- Prevents AI callers from "correcting" the host on their own name

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 07:42:18 -06:00
lukeandClaude Opus 4.6 eb1e18a997 Strip stage directions before TTS, strengthen prompt bans
- Regex strips all parentheticals and asterisk actions before TTS
- Catches (laughs nervously), *sighs*, etc. that Grok generates
- Strengthened SPEECH ONLY instructions in caller and Devon prompts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 23:40:45 -06:00
lukeandClaude Opus 4.6 6dcdf20289 Grok 4 routing, guardrails, pricing fix, strip silence improvements
- Route caller_dialog, devon_ask, background_gen to x-ai/grok-4
- Add Grok-4 to OPENROUTER_MODELS and OPENROUTER_PRICING
- Add Grok-specific banned phrases (I hear you, fair enough, that's wild, etc.)
- Add background gen guardrails for Grok (no active violence, no real public figures)
- Soften theme prompt hot-take language for organic connections
- Tighten Devon flirting guardrail (awkward not crude)
- Fix Devon "first day" contradiction on line 36
- Strip silence: preserve music intro, fix ad normalization (direct WAV reading)
- Strip silence: loop range starts 0.5s before audible music

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 17:46:29 -06:00
lukeandClaude Opus 4.6 762b5efc3b Strip silence: preserve music intro, fix ad normalization, smart loop range
- Preserve first silence in first DIALOG region (music intro before host speaks)
- Fix ad/ident normalization using direct WAV reading (accessor failed after splits)
- Loop range starts 0.5s before audible music, ends at last item
- Disable broken music lead-in nudge (intro preservation handles it)
- Caller dialog model set to Grok for testing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 02:32:34 -06:00
lukeandClaude Opus 4.6 3dd6a83c68 Full app audit: 24 fixes across backend, frontend, infra, content, social
Critical fixes:
- Fix hangup-during-respond crash (null caller guard)
- Fix double-click caller race condition
- Stem recorder: non-daemon thread, disk error handling, 30s flush timeout
- Frontend startCall() error handling

High priority:
- Devon: filter tool errors from speech, shorter monitor prompt, 30s interval
- TTS ghost message fix (add to history after TTS, not before)
- Expand banned phrase list (12 new phrases)
- Increase returning callers from 1 to 2 per session
- Platform-tailored social posts with staggered scheduling
- YouTube dynamic tags from episode content
- Social post retry logic (2 attempts, 5s delay)
- Frontend: error handling on all raw fetch calls

Medium:
- stem_recorder null check race (local var capture in audio.py)
- Reactive shape directive expanded
- REACT TO LUKE moved higher in caller prompt
- Devon tenure updated ("few weeks" not "first day")
- D shortcut Escape to unfocus
- Volume slider debounced (150ms)
- Settings modal widened to 550px
- Backup script (daily MariaDB dump + data/ rsync to NAS)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 14:57:50 -06:00
lukeandClaude Opus 4.6 5e98ed0e11 Fix LinkedIn posting to use correct account, blocklist personal profile
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 14:02:47 -06:00
lukeandClaude Opus 4.6 fcf13bae22 Fix repetitive episode titles — require specific caller/situation references
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 04:06:12 -06:00
lukeandClaude Opus 4.6 c30a75cc8f Fix X/Twitter posting — add who_can_reply_post and __type params
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 04:02:55 -06:00
lukeandClaude Opus 4.6 90e51698b8 Devon fixes, theme prompt rewrite, sentence trimmer, cost tracker, normalization
- Fix Devon "if that makes sense" overuse (limit to once per show)
- Suppress Devon failed lookup notifications for self-initiated searches
- Strengthen show theme prompts (2/3 callers call because of theme)
- Fix sentence trimmer splitting on abbreviations (Mr. Mrs. Dr. etc.)
- Fix cost tracker data lost on server restart (persist in checkpoint)
- Ad/ident normalization targets -4dB below dialog for perceived loudness match
- Lower cross-speaker transition threshold to 5s

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 03:55:55 -06:00
lukeandClaude Opus 4.6 5d8ab57e20 Show theme feature, Irish music genre, strip silence overhaul
- Add show theme UI in header bar + backend API (inject into caller prompts)
- Add Irish genre category for music dropdown
- Strip silence: RMS-based speaker detection (fixes Devon not being identified)
- Strip silence: Devon-specific 3s threshold for interjections
- Strip silence: sparse track item handling in shift logic
- Strip silence: music lead-in preservation after silence removal
- Strip silence: no max gap limit (IDENT/AD regions protect breaks)
- Add analyze_gaps.py tool for per-show threshold analysis

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 03:30:15 -06:00
lukeandClaude Opus 4.6 d33a022676 Add show theme feature for themed episodes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 23:46:48 -06:00
lukeandClaude Opus 4.6 7e2ef1fa2b Add MIT license, add X to social posting platforms
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 01:39:25 -06:00
lukeandClaude Opus 4.6 164cad456c Devon own stem/track/channel, per-category LLM routing, settings UI cleanup
Audio:
- Devon gets own stem, Reaper track (Input 17), and configurable channel
- play_caller_audio accepts stem_name + channel_override params
- Reaper script checks 4 voice tracks (Host, Devon, Live Caller, AI Caller)
- postprod.py includes devon stem in gap detection

Cost optimization:
- Per-category model routing: Sonnet for caller dialog, Gemini Flash for everything else
- Estimated 65% cost reduction ($4.32 → ~$1.50/show)
- Category models configurable from settings UI

Frontend:
- Settings panel: clean routing grid for output channels, model routing grid for LLM categories
- Devon channel added to audio routing
- Share icon SVG fill fix (currentColor)
- Website homepage iterations

Publishing:
- Revert Castopod API workaround (API re-enabled)
- Fix container media path

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 17:05:19 -06:00
lukeandClaude Opus 4.6 0b091a1afd Homepage redesign, ep38 publish, Castopod fix, share icons, avatar gender
Website:
- Full homepage redesign: new hero with punchy tagline, social proof strip
  with real caller quotes, featured episode spotlight, clips moved up
- Remove Q&A section, cover art from hero, secondary links
- Fix share icon fill (currentColor), add .sr-only class
- Bump cache versions to v=6

Backend:
- Blacklist Celeste voice
- Fix avatar gender caching with marker files
- Fix _match_voices_to_styles() bypassing BLACKLISTED_VOICES

Publishing:
- Fix Castopod container path (/var/www/castopod/ → /app/)
- Revert CLOUDFLARE_UPLOAD_LIMIT workaround (API fixed)
- Publish episode 38

Reaper:
- Dual silence threshold (2.5s transitions, 6s same-speaker)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 14:31:37 -06:00
lukeandClaude Opus 4.6 cfc7ad39f2 Add missing .sr-only CSS class
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 03:40:51 -06:00
lukeandClaude Opus 4.6 8a64a269f3 Remove duplicate h1, fix avatar gender caching, blacklist Celeste voice
- 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>
2026-03-16 03:28:18 -06:00
lukeandClaude Opus 4.6 908255e5cf Clean up hero section, fix Silas voice exclusion bug
- 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>
2026-03-16 03:21:13 -06:00
lukeandClaude Opus 4.6 39297d4aa5 Growth features: share buttons, NEW badge, sticky CTA, newsletter cross-promote
- 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>
2026-03-16 01:23:43 -06:00
lukeandClaude Opus 4.6 d39cb3f3d4 Website overhaul: nav, accessibility, shared components, SEO, Reaper silence detection
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>
2026-03-16 00:56:29 -06:00
lukeandClaude Opus 4.6 c70f83d04a Cost monitoring, PTT fix, Devon tuning, WEIRD pool expansion, YT thumbnails, LLM SEO, publish ep37
- 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>
2026-03-15 05:33:27 -06:00
lukeandClaude Opus 4.6 3329cf9ac2 UI cleanup, Devon overhaul, bug fixes, publish ep36
- Fix Devon double messages, add conversation persistence, voice-to-Devon when no caller
- Devon personality: weird/lovable intern on first day, handles name misspellings
- Fix caller gender/avatar mismatch (avatar seed includes gender)
- Reserve Sebastian voice for Silas, ban "eating at me" phrase harder
- Callers now hear Devon's commentary in conversation context
- CSS cleanup: expand compressed blocks, remove inline styles, fix Devon color to warm tawny
- Reaper silence threshold 7s → 6s
- Publish episode 36

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 16:42:21 -06:00
lukeandClaude Opus 4.6 6d4e490283 Caller generation overhaul, Devon intern, frontend redesign
Caller system: structured JSON backgrounds, voice-personality matching (68 profiles),
thematic inter-caller awareness, adaptive call shapes, show pacing, returning caller
memory with relationships/arcs, post-call quality signals, 95 comedy writer entries.

Devon the Intern: persistent show character with tool-calling LLM (web search, Wikipedia,
headlines, webpage fetch), auto-monitoring, 6 API endpoints, full frontend UI.

Frontend: wrap-up nudge button, caller info panel with shape/energy/emotion badges,
keyboard shortcuts (1-0/H/W/M/D), pinned SFX, visual polish, Devon panel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 01:54:08 -06:00
lukeandClaude Opus 4.6 d3490e1521 Expand all caller topic pools, add cross-episode topic dedup, publish ep35
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>
2026-03-13 05:45:22 -06:00
lukeandClaude Opus 4.6 0c2201fab5 Fix Remotion render error reporting and harden clip timestamps
- Show full stderr (head + tail) instead of truncating to last 500 chars
- Add --timeout=60000 and --log=verbose to Remotion render command
- Clamp word timestamps to [0, duration] to prevent negative/OOB values

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 19:22:03 -06:00
lukeandClaude Opus 4.6 f7b75fa72f Clips page, new episodes, TTS/audio improvements, publish pipeline updates
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 05:38:58 -06:00
lukeandClaude Opus 4.6 2c7fcdb5ae Move hardcoded secrets to .env, add .env.example
Castopod password, DB password, BunnyCDN keys, Postiz JWT/IDs,
and monitoring token all moved to environment variables.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 15:19:20 -07:00
lukeandClaude Opus 4.6 0bdac16250 Upgrade Whisper to distil-large-v3, fix caller identity confusion, sort clips list
- 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>
2026-03-05 12:46:51 -07:00
lukeandClaude Opus 4.6 6eeab58464 TTS fixes, Inworld improvements, footer redesign, episodes 15-25, invoice script fix
- 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>
2026-03-02 12:38:58 -07:00
lukeandClaude Opus 4.6 08a35bddeb Play idents in stereo on channels 15/16 with configurable ident_channel setting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 22:28:26 -07:00
lukeandClaude Opus 4.6 bbcf767a8f Add idents playback section — loads from idents/ folder, plays on ads channel
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 22:24:40 -07:00
lukeandClaude Opus 4.6 b1bd4ed365 Add direct YouTube upload to publish pipeline, publish ep14
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>
2026-02-17 15:07:16 -07:00
lukeandClaude Opus 4.6 2b3551cada Add paragraph spacing on how-it-works page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 05:35:58 -07:00