Skip to content

fix(director): the beat speaks from the real program, not an imagined one [spec 04] - #165

Merged
wine-fall merged 1 commit into
mainfrom
zachg-0831--beat-music-grounding
Sep 2, 2026
Merged

fix(director): the beat speaks from the real program, not an imagined one [spec 04]#165
wine-fall merged 1 commit into
mainfrom
zachg-0831--beat-music-grounding

Conversation

@wine-fall

Copy link
Copy Markdown
Owner

Fixes the self-initiated talk beat fabricating music state. Evidence (.dev/dev.log 2026-08-31): in the no-song stretch after a 14:06 music.end, beats claimed a track was still playing and ending; during the 14:23–14:28 pick-failure window (dead probe, "nothing suitable found") the beats invented an entire broadcast — a song chosen, put on, heard to its end, the listener's kettle heard boiling — and called the afternoon over at 14:28.

Root cause: the talk prompt carried no music facts and no clock — only the six-hour scene bucket — so the model filled the silence in. Beats also air minutes after generation via the spec-04 look-ahead buffer.

Related spec: specs/spec04/04-no-dead-air.md (§3.4 context enrichment — bugfix, spec unchanged per convention).

What changed

  • ContextPack gains time (real local clock, ICU-free formatClock) and music — a discriminated MusicState: playing (the on-air label), quiet (nothing playing, optionally the last track), picking (search in flight), pickFailed (latest search empty). Derived in the Director from the segment, the pick slot, the search outcome, and the last aired track; a wired radio always states a music fact.
  • The talk builders render both plus anti-fabrication red lines: the system announces tracks (the startTrack announce path, unchanged); the beat never narrates picking/starting a song, never claims listener-side sounds, never narrates time passing.
  • Peer-review hardening: the respond/steer prompts get the same clock + music facts (without the beat-shaped red lines); a dropped pick resolving late is epoch-guarded out of pickFailed; a forced MURMUR_SCENE drops the clock line rather than contradict it.

Unchanged by design: mood-driven pick selection (spec 04), gapSeconds, the announce path.

Peer review (codex gpt-5.6-sol, xhigh): 4 findings, 4 applied, 0 dismissed.

Gates: 1012 vitest green, tsc, oxlint. By-ear: whether the beats actually stop fabricating on a real run is a stochastic outcome — worth an ear during the next normal listening session, no dedicated pass owed.

AI coding brief

Original request: Fix the bug of self-initiated talk beats fabricating music state (songs announced that never played, imagined listener-side sounds, time narrated faster than real), with evidence and the fix plan pre-settled in-session: ground the ContextPack with real music status + real clock, and add prompt red lines.

Manual interventions: None during the build — the invocation carried the full investigation (log evidence, root cause, fix design, constraints), so the session went straight to test-first implementation.

Retro: Front-loading the diagnosis into the /murmur-ship brief (evidence timestamps, named code seams, settled design, explicit non-goals) let the build skip re-investigation entirely; the only additions came from the codex closing review, which the brief correctly left open.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MSvKfUaQzE95K77oqaBzDU

… one [spec 04]

A buffered talk beat invented a whole broadcast: songs announced that never
played, a kettle heard from the listener's room, an afternoon narrated to its
end in five real minutes (.dev/dev.log 2026-08-31 14:06-14:28). The prompt
gave the model only a six-hour scene bucket and no music facts, so it filled
the silence in.

The ContextPack now carries the real clock (formatClock, ICU-free) and the
program's live music status (playing / quiet / picking / pickFailed), derived
from what the Director actually knows: the on-air segment, the pick slot, the
latest search outcome (epoch-guarded against a dropped pick resolving late),
and the last track that aired. The talk builders render both plus
anti-fabrication red lines (the system announces tracks; never narrate
picking, listener-side sounds, or time passing); the respond and steer
prompts get the same facts without the beat-shaped rules. A forced
MURMUR_SCENE drops the clock line rather than contradict it.

Peer review (codex gpt-5.6-sol): 4 findings, 4 applied.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MSvKfUaQzE95K77oqaBzDU
@wine-fall
wine-fall force-pushed the zachg-0831--beat-music-grounding branch from ea21293 to 3bb7f19 Compare September 2, 2026 06:48
@wine-fall
wine-fall merged commit d05c316 into main Sep 2, 2026
3 checks passed
@wine-fall
wine-fall deleted the zachg-0831--beat-music-grounding branch September 2, 2026 06:50
wine-fall added a commit that referenced this pull request Sep 2, 2026
…191)

The grounding that landed in #165 tells the host the local time to the
minute. It still could not tell a Monday from a Saturday: the six-hour scene
bucket cannot, and neither could a bare "2:28 pm". `formatClock` now renders
the weekday and the zero-padded date before the 12-hour local time, so every
prompt that speaks aloud knows which day it is. Spec 04 §3.4 records the
field's shape.

Taken from #162 by Eis4TY, whose diagnosis this is. Its other half — the
on-air track's played/remaining seconds — is left out on purpose: self-
initiated beats are composed two deep ahead of air (spec 04 §3.3), so a
countdown stamped at compose time is already false by the time it is spoken,
and works against the anti-fabrication rules #165 added.



Claude-Session: https://claude.ai/code/session_01DnTSsRfAnvPPhcbipAMrgW

Co-authored-by: Tianyi <41855716+Eis4TY@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
wine-fall added a commit that referenced this pull request Sep 4, 2026
"Say real things" shipped: the off-loop topic pool, weighted by the
listener's spoken language and their timezone, landed via #201 and #203. The
file's own rule is that a delivered line is deleted rather than archived, so
the row and its section go, and spec 13 becomes the record — its two
pointers into the deleted section are retargeted at itself. What survives
the line is tracked elsewhere: #202 joins the by-ear list, and #44 closes on
that issue's first box.

Line 0's other half is resolved too, though not the way the file predicted.
It called for the parallel clock and grounding work to be reconciled into
one change; what actually happened is that #165 landed the grounding, #191
then landed only the weekday-and-date half of #162, and the play-progress
half was deliberately dropped because the look-ahead makes a countdown false
by the time it airs. Recorded as a decision, not a merge, so nobody
re-proposes the progress fields. What is left of line 0 is the dropped first
line, unchanged.

Two cross-references pointed at the deleted line. #98's eval track is the
sharper of them: line 2 shipped a prompt whose whole job is stochastic, and
the only thing that caught its first draft getting that wrong was a person
reading a dev log — so the eval is overdue, not upcoming.


Claude-Session: https://claude.ai/code/session_018KVNGUoCkDfWFdLwkGWsAi

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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