feat(director): talk look-ahead survives music, depth-2 buffer [spec 04]#30
Merged
Merged
Conversation
bbdc828 to
e286aee
Compare
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>
e286aee to
007d61b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:_prefetch_talkrefills when the buffer is empty (one coherentnext_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.Steercancels 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._LOOKAHEAD_ATTEMPTS) on the refill'snext_talksand 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
mainper feature would have skipped the cherry-pick.🤖 Generated with Claude Code