Skip to content

feat(director): talk look-ahead survives music, depth-2 buffer [spec 04]#30

Merged
wine-fall merged 1 commit into
mainfrom
zachg-0715--talk-lookahead-survives-music
Jul 16, 2026
Merged

feat(director): talk look-ahead survives music, depth-2 buffer [spec 04]#30
wine-fall merged 1 commit into
mainfrom
zachg-0715--talk-lookahead-survives-music

Conversation

@wine-fall

Copy link
Copy Markdown
Owner

Implements specs/spec04/04-no-dead-air.md §3.3 — the talk look-ahead now survives music.

Problem

The talk look-ahead buffer was discarded when a song went on air and never refilled during playback. So the music→talk boundary always hit a cold next_talks + synth — dead air after every song. The music side already prefetched during talk (slice 1); the talk side had no symmetric prefetch during music.

What changed

A depth-2 talk look-ahead buffer (_TALK_LOOKAHEAD) that mirrors the music slot:

  • Survives music, refilled when drained. A song no longer discards the buffer — a song is the ideal window to prepare the next talk. _prefetch_talk refills when the buffer is empty (one coherent next_talks(N) batch, beats synthesized in parallel), fired after an aired beat is recorded and at a music segment's start, so the refill's Brain+synth overlap the song and the post-song talk airs warm.
  • Coherent batches. Refilling only-when-empty and only-after-record means each batch regenerates over context that already includes every prior aired beat (the real brain is stateless) — no duplication against beats still buffered.
  • User priority preserved. A talkback Steer cancels an in-flight refill without awaiting it, so a reply is never blocked on a background prefetch's teardown (spec 01 §3.3); the refill settles on the next refill or at shutdown.
  • Resilience. Bounded retry (_LOOKAHEAD_ATTEMPTS) on the refill's next_talks and on synth, degrading (lose the look-ahead / the one beat, never the radio); every stage logs to the dev log.

Tests

New fakes for transient next_talks / synth failures. New unit tests: survives-a-song (buffered beat airs post-song, no cold regen), depth-2 across two music completions (both post-song talks warm), Brain-retry, synth-retry. Updated the tests whose contract changed (song no longer discards; steer-resume asserts the invariant not an exact beat number). pytest: 248 passed. pyright + source-language gates green.

Verified at the deterministic seam (fakes) per the acceptance criteria; the end-to-end first-music latency re-measurement (the ~76s that motivated spec 04) is tracked as still-open in specs/STATUS.md.

Peer review

codex (gpt-5.5, xhigh) vs origin/main: 2 findings, 2 applied, 0 dismissed — (1) cold/refill using stale context could duplicate buffered beats → fixed with refill-when-empty + refill-after-record; (2) reply blocked on a stale refill's cancellation → fixed with cancel-not-await on the steer path.

AI coding brief

  • Original request: While reviewing spec 04, the user observed that at startup two TTS beats + a music pick are prepared, but during music playback nothing regenerates the next talk — so the music→talk boundary is cold. They asked to build the missing symmetric prefetch: a talk slot, capacity 2, that always refills from current context when drained ("just like the music-find logic"). The "why": remove the dead air after songs.
  • Manual interventions: The user set the acceptance bar explicitly — buffer capacity 2 (cover the next two music completions), parallelize where possible, and retry + logging on the important stages. codex review then redirected the refill design (refill-when-empty + after-record) to avoid stale-context duplication and to keep replies off the refill-teardown path.
  • Retro: Prompt faster next time by stating the capacity/parallel/retry/log constraints up front (the user did) — they shaped the design cleanly. The one avoidable detour was committing onto a stale already-merged branch; branching fresh from main per feature would have skipped the cherry-pick.

🤖 Generated with Claude Code

@wine-fall wine-fall force-pushed the zachg-0715--talk-lookahead-survives-music branch from bbdc828 to e286aee Compare July 15, 2026 09:59
Before this, the talk look-ahead buffer was discarded when a song went on air
and never refilled during playback, so the music->talk boundary hit a cold
next_talks + synth — dead air after every song. Now a depth-2 buffer
(_TALK_LOOKAHEAD) is kept topped up and survives the song; refilling at a music
segment's start overlaps the refill's Brain+synth with the song so the post-song
talk airs warm.

- Held at depth 2 (not drained-then-refilled): `_prefetch_talk` tops up whenever
  the buffer is below depth — after an aired beat is recorded and at a music
  segment's start — so the next talk is always ready.
- Coherent refills: the top-up passes the queued-but-unaired beats into the Brain
  context as prior `radio` turns (the buffered text is in the Director), so the
  stateless brain continues the monologue instead of duplicating a buffered beat.
- A talkback steer cancels an in-flight refill without awaiting it, so a reply is
  never blocked on a background prefetch's teardown (spec 01 §3.3 priority); it
  settles on the next refill or at shutdown.
- Bounded retry (`_LOOKAHEAD_ATTEMPTS`) on the refill's next_talks and on synth,
  degrading (lose the look-ahead / the beat, never the radio); every stage logs.

Spec 04 §3.2/§3.3 + acceptance updated (survives-music, held-at-depth-2,
coherent-refill, resilience); STATUS notes the still-open end-to-end latency
measurement.

Peer review (codex gpt-5.5, xhigh): 2 findings, 2 applied, 0 dismissed —
stale-context duplication (fixed by threading queued beats into the refill
context) and reply-blocked-on-refill-teardown (cancel-not-await).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@wine-fall wine-fall force-pushed the zachg-0715--talk-lookahead-survives-music branch from e286aee to 007d61b Compare July 16, 2026 03:23
@wine-fall wine-fall merged commit 1b30f1d into main Jul 16, 2026
4 checks passed
@wine-fall wine-fall deleted the zachg-0715--talk-lookahead-survives-music branch July 16, 2026 06:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant