Skip to content

feat(talk): real-world topics — an off-loop pool the host can mention [spec 13] - #203

Merged
wine-fall merged 7 commits into
mainfrom
zachg-0903--rwt
Sep 4, 2026
Merged

feat(talk): real-world topics — an off-loop pool the host can mention [spec 13]#203
wine-fall merged 7 commits into
mainfrom
zachg-0903--rwt

Conversation

@wine-fall

@wine-fall wine-fall commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Implements specs/spec13/13-real-world-topics.md (new). Stacked on #201 (the clock-as-bearings fix); merge that first.

What

The self-initiated talk task had no source of information — one terminal tool, no built-ins, a persona and a transcript — so it invented its topics and landed on the same cozy imagery every cold boot (#44; the listener's last 30 ledger topics were all afternoon light / warm cups / fridge hum). ROADMAP §2 set the line: real material, off the live loop, weighted by language and timezone, region never stored.

Poolsrc/rwt.ts: cache/rwt.json, entries {id, title, gist, category, fetchedAt, used}, 48 h expiry, stale after 6 h; take() marks used and persists. FetchBrain.fetchTopics: runTask with a new builtins seam (WebSearch on tools and allowedTools), terminal submit_topics (zod), 12 turns, Haiku, a researcher framing that is never the persona. RollRwtRoll, RandomCadence's shape (p 0.35, minGap 1, maxGap 4 batches; MURMUR_RWT_* env knobs). Director — one offer() per ordinary generateTalks (never anchor:*, never the coda), maybeRefresh() poked at every boundary beside the compactor. PromptrwtLine: "Something from out in the world, if you want it … Material, not an assignment". KnobrwtEnabled (settings file < --no-rwt), change_settings({ rwt }) + a line in the steer rule so "stop with the news" works; the stub brain fetches nothing. Taste$MURMUR_HOME/rwt-policy.md, the music-policy shape, seeded on first use.

Language is read where the host reads its own. The first real run produced English gists on a Chinese persona: detectLanguage() follows LANG (now en_US), but the persona was seeded when it said Chinese and records that as "Always speak in Chinese (Mandarin)". So the request language is settings.language, else personaLanguage(persona) (the seed's "speak in X" clause), else the persona's own first line of prose held up as the example — a generated persona is written in the listener's language and never names it. Never the machine locale. Spec 13 §3.5 records the measurement.

This is the fourth network call; DESIGN.md's "three" is already stale per #104 and is not edited here.

Evidence (the deterministic seams, not the model's word)

Real fetchTopics through the SDK (scratch smoke, language "Chinese", tz Asia/Singapore): 6 entries in 105 s, gists in Chinese, weighted to the region (Taiwan defence budget, a Singapore animated film, Asia Cup cricket). A make dev run (--no-music --max-segments 10, MURMUR_RWT_P=1) in .dev/dev.log:

18:31:45 rwt.policy seeded /Users/zach/.murmur/rwt-policy.md
18:33:25 rwt.refresh n=6 ms=100229
18:33:25 rwt.pool fresh=6 used=0
18:34:01 talk.refill need=1 queued=1
18:34:01 rwt.offer ea853598          ← "The Gentlemen Season 2 drops on Netflix today" (entertainment)
18:34:01 rwt.pool fresh=5 used=1
18:35:35 radio: …我有个朋友前阵子还跟我念叨,说下了班本来盘算着看部剧,那种热热闹闹的、一伙人折腾着干大事的片子,结果往沙发上一坐,看了两眼就困了。你说这不挺好么,那剧它又不跑…

That beat proved the seam — and disproved the first draft of the line. The material reached the batch and none of it reached the air: a Netflix release scrubbed to "a friend wanted to watch a show", the #44 attractor wearing a fig leaf. The prompt had forbidden "a headline read out" and let the host "leave it". A radio names things. The second commit redraws the line on register, never content: name the thing — the title, who, where, when — say what happened in a sentence or two and what you make of it, then carry on; what stays forbidden is the newsreader's rundown, the "here is the news" frame, the list. The default policy asks the fetch to keep the hard nouns for the same reason. Spec 13 §2.5 records the measurement; #202's first box matches. The by-ear questions (does it sound like a host, is the proportion right) are #202.

Re-run with the redrawn line (2026-09-04, same flags), .dev/dev.log:

11:11:36 rwt.refresh n=5 ms=153065
11:11:42 rwt.offer e7c1de27      ← 新加坡霾害升至不健康级别 (news)
11:13:05 radio: 我今早还看到一条,说新加坡那边的天,今儿个中部一带脏得厉害,那个污染的数儿一下过了一百,到了对身子不好的地步了。这也不是一天两天的事儿,是印尼那头的林子,断断续续都烧了五十多天了……自打二○一五年往后,最凶的一回。你说这烟火啊,它不吵不闹……
11:13:05 rwt.offer 3d933063      ← 尼泊尔洪灾隧道救援三百多人 (news)
11:13:57 radio: 我今早看着一条消息,心里头沉了一下。尼泊尔那边,是冰河崩下来闹的水患……昨天救援队从一座发电厂的隧道里,把三百五十号人给拽了出来……死的已经过了一千一百,还有小五千人,到现在连影子都没着落……

Place, numbers, dates, the cause — named, in the host's own voice, one item per beat, no rundown. Titles and gists now arrive in the persona's language (the pool this run left behind is Chinese and stays).

The third commit is a one-line test fix outside spec 13: test/memory-fold.test.ts (from #196) reopened a store on the wall clock after recording on a fixture clock of 2026-09-01, so the 48 h recent window went red on 2026-09-04 for every PR. It now reopens on its own clock.

Tests (test-first, per step)

test/rwt.test.ts (pool expiry / take / persistence / refreshDue; roll determinism; single-flight refresh, failure posture, avoid list; the fetch task's builtins, bound, framing, and submit_topics termination), test/prompts.test.ts (rendering present/absent, never on anchor/coda, the fetch prompt's contract lines, the steer rule), test/brain.test.ts (agenticOptions builtins; stub returns nothing), test/config.test.ts / test/app.test.ts / test/steer-tools.test.ts (flag, file, env knobs, store seeding, change_settings({rwt}), buildRwt wiring, language resolution), test/persona.test.ts (personaLanguage), test/director-rwt.test.ts (one offer per ordinary batch, none on anchor/coda, knob off, boundary poke).

Peer review

Peer review (codex gpt-5.6-sol): 3 findings, 3 applied, 0 dismissed — language is never taken from the machine locale (a localized persona is held up as its own example, and a manner clause is not a language); an unwritable cache logs instead of throwing on the talk path; expired entries no longer block a recurring title. Each has a regression test.

Follow-ups

AI coding brief

Original request: second of two sequential PRs from a finished design — write spec 13 (English, coding-agent form, spec 07's header structure), then build it test-first under murmur-build-spec without waiting for review. Every design point was pinned in the dispatching session (pool/fetch/roll/knob/policy/debug seams).

Manual interventions: none in this session. One decision taken mid-task: no pane row for the knob (spec 12's pane stays at eight; flag/file/conversation reach it) — logged in the spec's non-goals.

Retro: the real-seam smoke found the language bug the unit suite could not (persona-recorded language vs. machine locale); running the dev-log check before the PR, as the brief demanded, is what caught it. Next time, name "where the host reads its language" in the brief as a file:line, not a prose pointer.

🤖 Generated with Claude Code

https://claude.ai/code/session_018KVNGUoCkDfWFdLwkGWsAi

wine-fall and others added 7 commits September 3, 2026 18:18
The talk and reply prompts rendered the clock bare ("It's Monday
2026-08-31, 2:28 pm.") with no word on what it was for, so the host read
the date and minute out on every beat. It now arrives with its usage:
bearings, mentioned only when the hour or day lands in what is already
being said, never as an opener or a time-check. Same source line for the
talk builders and the reply status block.

The profile block also injected the fading ledger's [seen YYYY-MM-DD] /
[stable] tags verbatim, which the host read as content ("you told me on
the 31st"). They are stripped at injection; the file keeps them, and the
recall block's dates stay — those are deliberate.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018KVNGUoCkDfWFdLwkGWsAi
… [spec 13]

The self-initiated talk task had no way to learn anything: one terminal
tool, no built-ins, a persona and a transcript. So it invented its topics,
and a cold boot landed on the same cozy imagery every time (#44).

Spec 13 gives the host real material without touching the live loop. A
bounded WebSearch task (the harness gains a `builtins` seam, bounded via
`tools` and pre-approved via `allowedTools`) fetches a handful of items —
title, a two-to-three-sentence gist in the persona's language, a kind —
into cache/rwt.json. The refresh runs the way the compactor's fold does:
poked at every boundary, single-flight, unawaited, total. A roll shaped
like RandomCadence decides whether a talk batch is offered one at all;
anchors and the coda never are. The prompt renders it as material, not an
assignment.

The knob rides the settings layer (file < --no-rwt), a `rwt` field on
change_settings so "stop with the news" works, and env numbers for the
roll and freshness. The taste half is $MURMUR_HOME/rwt-policy.md, the
music-policy shape. Language is read where the host reads its own: the
override, else the persona's "speak in X" clause — the machine locale that
seeded it may have changed since, and on this machine it had.

Smoked at the real seam: a fetch wrote 6 entries in ~100 s; a make dev run
logged rwt.offer for a batch whose beat carried the item as a thread.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018KVNGUoCkDfWFdLwkGWsAi
…not content [spec 13]

The first draft forbade "a headline read out" and let the host "leave it",
and the measured result was the item scrubbed to mood: a Netflix release
became "a friend wanted to watch a show". That is the #44 attractor with a
fig leaf — the material was there and none of it reached the air.

A radio names things. The line now asks for the title, who, where, when,
said in a sentence or two the way a host says it, then carried past; what
it forbids is the newsreader's rundown, the "here is the news" frame, the
list. The default policy asks the fetch to keep the hard nouns for the same
reason. Spec 13 §2.5 records the measurement; #202's first box matches.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018KVNGUoCkDfWFdLwkGWsAi
… the wall clock

The fixture records at 2026-09-01 and reopened the store on the real clock,
so the 48 h recent window kept the row for two days and then aged it out —
green on the day #196 landed, red from 2026-09-04 on every PR.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018KVNGUoCkDfWFdLwkGWsAi
@wine-fall
wine-fall merged commit feac31f into main Sep 4, 2026
3 checks passed
@wine-fall
wine-fall deleted the zachg-0903--rwt branch September 4, 2026 05:47
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