Skip to content

feat(director): the brain gets the clock to the minute and the song's real progress [spec 04] - #162

Closed
Eis4TY wants to merge 1 commit into
wine-fall:mainfrom
Eis4TY:air-and-clock
Closed

feat(director): the brain gets the clock to the minute and the song's real progress [spec 04]#162
Eis4TY wants to merge 1 commit into
wine-fall:mainfrom
Eis4TY:air-and-clock

Conversation

@Eis4TY

@Eis4TY Eis4TY commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

What

The speaking brain was missing two pieces of ground truth, and invented both:

  • The clock. The scene bucket is a mood, not a schedule — late-night spans 23:00–04:59, so a model reading only the cue could not tell 01:00 (hours to dawn) from 04:00. Every prompt that speaks aloud (the two self-initiated builders, the reply, the steer task) now renders Local time: Monday 2026-08-31, 01:23. beside the scene cue.
  • What is actually on air. The model cannot hear the engine, so it re-narrated its own announcements as the air — a song "finished" because the host said it would. The Director now stamps compose-time facts on the pack (ctx.air), derived from the same startedAt+durationS arithmetic the spec-10 progress rail runs on, and the prompt renders them as a facts block: On air now: Song — Artist (2:10 of 4:05 played, about 1:55 left). A music session between tracks says no track is on air; a talk-only session renders no block at all.

Where

  • src/contracts.tsContextPack.now + ContextPack.air (both optional, additive; absent renders nothing).
  • src/prompts.tsformatClock / formatProgress (pure, pinned with injected clock values, the scene_for pattern) + the clockLine / programFacts renderers, threaded into all four speaking builders. Prompt text stays centralized here (DESIGN §0).
  • src/director.tscontext() samples the clock once per compose; airFacts() derives the on-air state from the segment stamp (clamped to the track's length).
  • Specs: specs/spec04/04-no-dead-air.md §2 / §3.4 amendments / §5 criteria 13–14; specs/spec01/01-core-loop.md §2.1 pack docstring.

Evidence

  • Test-first: each step landed red before its implementation (test/prompts.test.ts clock + progress + facts states; test/director-music.test.ts mid-song / between-tracks / talk-only pack assertions; test/director-steer.test.ts the steer context clock).
  • Gates in the worktree: pnpm run typecheck ✅, pnpm run lint ✅, pre-commit run --all-files ✅ (all four hooks), pnpm test 984 passed / 6 failed — the 6 are pre-existing machine-local failures in test/config.test.ts / test/app.test.ts (they read this machine's real ~/.murmur/voice.json through the NO_ENV homedir fallback; identical on the stashed clean baseline, unrelated to this change).

中文摘要

本次给模型的提示词补充了两项"现场事实",目标是让 AI 基于事实判断情境,而不是靠模糊提示脑补:

  1. 精确到分钟的本地时间。此前提示词只有"上午 / 下午 / 深夜"这类粗粒度时段,而深夜桶覆盖 23:00–04:59,AI 无法区分凌晨一点和凌晨四点,常在午夜说出"快天亮了"这类错判。现在每条开口提示(自主播报、听众回复、steer 任务)都会在场景提示旁附带精确时间,例如 Local time: Monday 2026-08-31, 01:23.

  2. 当前在播歌曲及播放进度。AI 听不到音频引擎,只能凭自己先前说过的话"回忆"在播什么,于是歌还没播完就被它宣告结束。现在 Director 在组合上下文时,把在播曲目、已播时长与总时长作为事实块注入提示,例如 On air now: Song — Artist (2:10 of 4:05 played, about 1:55 left).;两首歌之间会明确说明当前没有歌曲在播(避免 AI 凭空续播已结束的歌),纯谈话会话则不注入该块。

… real progress [spec 04]

The scene bucket is a mood, not a schedule (late-night spans 23:00-04:59),
and the model cannot hear the engine — so every speaking prompt now carries
the compose-time local date-time beside the scene cue, and a program-facts
block with the on-air track and its wall-clock progress off the spec-10
startedAt+durationS stamp. A music session between tracks says no track is
on air; a talk-only session renders no block.
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

wine-fall commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Thanks for the diagnosis! it was right, and it landed the same day from two directions.

The clock half of this PR is now on main via #191, with you as commit author (squash commit eccdcc0 carries Co-authored-by to your account). The music-grounding half had already landed in #165 with a wider state model (playing / quiet / picking / pick-failed) plus anti-fabrication rules.

The on-air progress numbers were 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 when spoken, and cuts against #165's rule to say nothing that turns false when the music changes. Progress on the immediate reply path alone would be accurate — a separate, smaller change if wanted.

Closing in favour of #191.

@Eis4TY

Eis4TY commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Okay, I understand your point. There definitely needs to be a better system for tracking playback progress.

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.

2 participants