Dialogue · Design notes · Roadmap
← Design notes

Roadmap — planned features

Explored, agreed-direction features for the live room — recorded in idea.md so the reasoning survives until they are built. Captured 2026-06-10 from a design discussion. Since then three have shipped§2 room dispatches (v47), §5 the smart panel selector (2026-06-18), and §1 the floor producer (deployed to production 2026-07-03): its v0 deterministic baseline landed on main 2026-06-24 as a per-room toggle, and the smart engine (v1p / v1f) — v1p the live default when the producer is On — plus an independent opening producer (ships dark, admin-armed) followed, all wired as admin-console toggles (the Models tab); §3 listen mode and §4 compaction remain planned. Separately, the perceived-speed / local-first infra has shipped to production — see Perceived speed & the slow-network playbook — and the room LANGUAGE system shipped + deployed 2026-06-21: an account default language (4 langs, OS-seeded) and a per-room target the panel speaks in-character, with a faithful translation appended under a hairline when a persona slips script — see Room language. Two further ideas were added 2026-06-21 at discussion-stage§6 persona mailboxes and §7 send-to-many — recorded while their direction is still forming (earlier in life than the settled §1–§5).

Each of these earned its place in a design discussion and has a settled direction — they are not a wish-list. The job of this page is to keep the why alive: the architecture choices, the traps named, and the lines that must not be crossed when the work finally lands.

1 · Floor producer — rhythm for the megaprompt room✓ shipped — live 2026-07-03

The problem

The megaprompt scheme wins on fluency (exp-010: hosts felt real and distinct, conversation flowed) — and its flatness is the same architectural property seen from the other side. One mind holds the whole conversational field, so handoffs are perfectly timed and nobody collides (fluency); and one mind smooths the whole field — it averages the room, gives everyone something to say, resolves tension at a single author's comfortable rate (flatness). Telling the megaprompt "vary your rhythm" drifts back to smooth over a long room: the writer and the staging-decider are the same context, and the writer always has something for everyone to say.

The idea

A separate per-turn agent that reads the room and emits a one-line staging directive before each panel call. It decides participation, order, length, and temperature — never content. The single writer stays intact for micro-coherence; unevenness is injected exogenously, as an outside signal the writer must accommodate rather than a disposition it must remember to maintain. The directive (~50 tok) is appended as a small control message before the panel call, fitting §8 append-only caching — the cached prefix stays untouched.

ROOM STATE public only · transcript tail · turn stats · room dials FLOOR PRODUCER emits one staging line · ~50 tok « Plato then Confucius. Brief. Let the disagreement stand. » appended BEFORE the panel call — the cached prefix stays untouched megaprompt panel call — writes the lines

Job description — stagecraft, never content

Producer MAY sayProducer may NOT say
who speaks this turn / who stays silentwhat anyone should say or argue
order ("Plato first, then the room")topics to raise ("push him on the ethics angle")
length budget ("one line each" / "let Plato run")positions ("disagree with the guest")
temperature ("press", "let it breathe", "wrap")emotions as content ("be angry about X")

The most valuable directive in the vocabulary is silence ("Confucius sits this one out"). LLM hosts are over-helpful; rhythm comes from suppression more than stimulation. A content-leaking producer manufactures drama on schedule — the exp-004 lesson generalized: an engineered cue upstream produces an engineered beat downstream, invisible in the transcript. In the megaprompt scheme that's "only" a quality bug; if this ever runs over isolated actors, the producer channel would be the intent-leakage vector — so the vocabulary contract is cheap insurance taken early.

Design decisions

namingFloor producer, not director. A director shapes what is said; a floor producer decides who has the mic, for how long, and when to wrap — and never writes a line. A smart router, not a co-author — compatible with the dumb-pipe principle. (Interactive-narrative literature calls this a drama manager; its famous failure mode is railroading — the same trap as the content column above.)

Evaluation plan

measured 2026-06-23The production diagnosis this plan called for has run — all 80 live rooms, the 39 multi-host conversations scored mechanically and by an LLM judge against real user feedback. Headline: redundancy 0.40, median reply ~314 words, and the extreme dial delivers ~⅓ of the conflict it promises — the heat goes host→human, not host↔host. It sizes the levers above and adds one: the producer must be able to assign genuine host↔host contention. → the diagnosis · the full design.

2 · Room dispatches — internet for the room, not the chars✓ shipped — live v47

Personas are bounded by the base model's knowledge cutoff plus their profile — but "Plato meets today's news" is one of the strongest things the room can do. The fix: search is not a persona tool. A hidden @Web Search that the user @-mentions runs a web correspondent (Haiku 4.5, max 1 search, ~$0.027) that distills a neutral, dated, sourced ~150-word fact-brief. It lands once in the stream (visible to hosts and humans alike) and the personas react under their own extrapolation contract — same fact, divergent voices, the single best live demo of the isolation thesis.

Why search is not a persona tool: (1) voice contamination — raw news prose makes a host paraphrase the journalist instead of reacting in his own voice; (2) it breaks the extrapolation anchor — a current event is a distance-2/3 question with a fact-brief handed across the table, not something the figure "knows"; (3) §8 cache + latency — raw results would bloat every later prefix, and agentic multi-search adds 10–30 s while live humans wait.

The staged live sequence: searching bubble → dispatch card + cost pill + ∞-pane artifact → conferring egg → replies. There is no topic policy — a deliberate user decision (fetch anything, wire-service neutral). Done — see Rendering & speed and the live app.

3 · Listen mode — bucket-voiced, mood-directed TTSplanned

Read each host's bubbles aloud in a per-character voice. Serves people who'd rather listen than read, mobile use, and the room's nature — it is already a panel show, and audio is its native medium. With the floor producer (§1) pacing it, this composes into a generated-podcast pipeline.

decisionAccent buckets, never voice cloning. The library includes living, famous people (Musk, Buffett, Karpathy) — cloning their actual voices is a right-of-publicity / AI-voice-statute minefield. Borrow the kind of voice, never fabricate the person's: the audio analog of the extrapolation contract. An accent+gender voice says "a man from this background," not "this is Buffett." Scales right too — N personas → K voices; a new persona costs one frontmatter line.

The four problems

#ProblemThe shape of the fix
1Collision within a roomaccent×gender is too coarse (Buffett + Jobs + Feynman + Karpathy = four American males). Add a timbre/age/pace axis, plus room-scoped collision-avoiding seating persisted like the colour PALETTE — voice anchors identity only if stable across sessions.
2Accent × room languagerooms run in zh/ja/en (now a first-class room-language system); "British English" means nothing when Churchill speaks 中文. Mapping: card voice: → bucket (accent·gender·age) → room seating → per-language provider voice ID. Bilingual voices (e.g. CosyVoice) collapse the per-language column.
3The parody lineaccent aids identity, never becomes the bit — the audio edition of "amplitude, not parody." The bucket includes a dignified-neutral option; assignment is a judgment call recorded in the persona card by the authoring pipeline, not auto-derived from birthplace. (Jack Ma's English: iconic and his. Confucius: neutral gravitas.)
4What gets voicedon-stage <speak> only — never <confer>, artifacts, or code. Stage directions become SSML pauses (*[long pause]* → an actual pause). Strip markdown before synth.

Mood-directed delivery hard requirement

Flat reading kills the panel feel — the TTS must take a delivery instruction per utterance. The signal already exists in-band; the style prompt is derived, not authored (no new content channel, no new leak vector):

style prompt = bucket base register — static, from the bucket repo (“warm elder, measured…”) + in-bubble stage-direction cues — *[laughs]* → laughter tag + the dials envelope — intensity / dynamics + floor-producer temperature — §1: “press” / “let it breathe”

Instruction-following is a hard filter on provider choice — flat direction-following (gpt-4o-mini-tts, Kokoro) is disqualifying; speech-specialized models are the bar.

Delivery architecture hard requirement

media never touches the boxAsync synth job → provider CDN URL → browser. Browser-direct synthesis is a trap — it would leak the key in a multi-user app (the legacy single-user shape can't carry over). The box submits the job, the provider returns a CDN URL, the browser fetches the mp3 there: no key leaves the server, no media bytes through the box.

China consequence: Gemini / ElevenLabs CDNs are blocked from mainland — direct delivery fails exactly for China-path users, while Alibaba OSS / MiniMax CDNs work there. So: either one China-reachable instructable provider for everyone (DashScope / MiniMax), or per-region routing (CN → DashScope, intl → ElevenLabs/Gemini). This is the evaluation to run when building.

Caching: audio is immutable per (text + style, voice) → lazy synth on first play (readers who never listen cost zero), browser-cached, provider-URL expiry accepted (re-synth is fractions of a cent). First candidate: Qwen3-TTS-Flash on DashScope — instructable, OSS-URL delivery, China-reachable; ~0.4¢ per average bubble. (The vendor-synergy tiebreaker has since lapsed: the dictating mic shipped on iFlytek, not DashScope — TTS is now a standalone vendor choice.)

4 · Compaction — capping the append-only contextplanned

The problem

§8 caching is append-only: every turn re-sends the whole history and the prefix only grows — it never shrinks. Left alone, a long room's model context climbs without bound, and three costs scale with it: dollars (a 500K cached prefix still costs ~5× a 100K one to read, and past the 1h TTL it's a full re-read), latency (TTFT grows with prefix size even when cached), and effective-context quality (lost-in-the-middle and mild drift grow as the working set fills, well before the hard limit).

priorityReal, but not urgent — and record why. The default models (DeepSeek v4 Pro / Flash) run a 1M-token window on a sparse-attention architecture built for cheap long context. At 100K a room is at ~10% of the window — the ceiling is far and per-turn cost growth is gentle. So compaction is a cost + effective-context optimization for LARGE rooms (hundreds-of-K toward 1M), not a fix for a near-term wall. It earns a slot because append-only makes the cost structural and monotonic; it does not earn a build until rooms actually get big. (An earlier "switch to Sonnet for headroom" read was wrong — Sonnet's 200K is smaller than v4's 1M.)

The idea

When a room crosses a high token threshold, fold everything older than the last N turns into a single model-written «story so far» block, and keep the recent turns verbatim. The persona cards (stable prefix head) and the recent tail stay; the long middle becomes a summary.

STORED — state.json (never shrinks) persona cards (stable head) turn 1 … turn 190 every turn re-sent, forever — grows without bound turn 191 … turn 200 MODEL SEES — after compaction persona cards (unchanged) « STORY SO FAR » arc · decisions · positions open threads · key disclosures artifact refs (title + id) turn 191 … turn 200 (verbatim) fold Full history is never lost — compaction touches only the model’s working prefix; the UI still shows everything.

What the «story so far» must carry — faithfulness is the risk

A summary that drops a load-bearing detail or smooths a persona's position is the megaprompt-flatness failure in a new place. So it preserves, explicitly: the arc (what's been covered), decisions/facts reached, each persona's established positions (so they don't contradict their earlier selves), open threads (so they aren't silently dropped), key human disclosures (the room's whole point), and artifact references (title + id, not full text — artifacts already live in state.json and the wildcard pane). The recent N turns stay verbatim — recency is where micro-coherence lives.

Design decisions

5 · Smart panel selector — convene a panel from a goal, don't browse a catalog✓ shipped — live 2026-06-18

shipped · 2026-06-18Live in production, built to this design. Describe a situation → DeepSeek v4 Pro curates three panels, each seat carrying its one-line why (the rationale shipped as a feature, per the load-bearing decision below) → a streamed auto-title + a topic-aware kickoff. Hand-pick remains the other door (cold start). The curated set lands in the same editable grid — add/remove freely. The deferred "persistent, re-curating panel" is still deferred. What follows is the design that drove the build, kept for the why.

→ open the interactive mockup — the full new-chat window with the project's real persona library (describe → three panels → editable rows with the reason → hand-pick → folded dials).

The problem

§1–4 are all about conversation quality once a room exists. Nothing addresses how a room gets formed — the one cost that grows with every persona added. Today the only way in is "browse all personas and hand-pick," which fails two ways: cold-start (a new user has no idea who 小胖看房 is, or why they'd want Buffett plus a skeptic — the blank picker asks them to choose from strangers) and scale (browsing 8 is fine, 80 is not, and the catalog is designed to keep growing — structural, monotonic cost, the room-formation twin of §4's append-only prefix).

The shape — two doors into the existing +New picker

Not a new surface — a mode toggle on the v0.3 picker modal. The substrate exists: tags.txt per persona + persona-search.js fuzzy search. The smart door is the default (better cold-start); both are first-class, and the curated set lands in the same grid as a pre-filled, editable selection — never a separate screen, never a locked black box. Augment browsing, don't replace it.

New room Describe your situation Hand-pick = today “Leaving my job to start a company. First-time founder.” Convene a panel CURATED · PRE-SELECTED · EDITABLE Buffett — capital / downside angle Xiao Pang — practical, been-there skeptic — productive friction … add / remove freely Create room
the load-bearing decisionCurate for tension, not similarity. The naive build — embed the goal, return the top-K most-similar personas — gives five people who already agree with each other and with the user. That's an echo chamber, and the exact inversion of this product's thesis ("same fact, irreconcilable voices", §2). The objective is cover the relevant angles / create useful friction, not maximize similarity. A panel earns its name by being complementary, not redundant — which decides the architecture: a model that reasons about composition, not a ranker.
Deterministic (tag / embedding match)LLM-curated (chosen)
Reads "anxious first-time founder"weak — keyword overlap onlyyes — reads intent
Picks complementary anglesno — similarity rank → echo chamberyes — can be instructed to
Explains why each seatnoyes — the rationale is a feature
Cost~freeone cheap call per room creation — infrequent, high-leverage

Design decisions

Evaluation plan

deferred — scope creep for v1"Status → a persistent panel that re-curates as the situation evolves" is a real concept but out of scope. v1 = a single goal / question → one editable panel, at creation time.

6 · Persona mailboxes — email a persona, get a replydiscussion

Captured 2026-06-21 — discussion-stage, nothing built. Earlier in its life than §1–§5: the direction is still forming and the open questions below are real.

→ open the persona-homepage mockup — the page before the DM: who a persona is and why you'd write, with the machinery hidden (switch across four persona types).

The opportunity

§1–5 all live inside the live room — synchronous, group, SSE-streamed, on the critical path. There is exactly one way to reach a persona today: open the app and convene a panel. But a persona is pure data driven by one system prompt; that engine doesn't have to be spoken to only through the room. Email is a second front door — a 1:1, asynchronous, store-and-forward channel that meets the user in their own inbox. "A letter back from Buffett" is a different product feeling than the panel: intimate, unhurried, pen-pal — and it costs the user nothing to keep open (no app, no session). Because it's async, latency stops being a cost (a reply is expected to take minutes), so it's a lighter lift than the room in every way except deliverability ops.

inbound — Cloudflare Email Worker → box USER’S INBOX To: buffett@… “Should I sell?” INBOUND GATE 1 · sender on the whitelist? 2 · to-address → persona slug 3 · single-speaker reply call 4 · per-(user, persona) thread PERSONA profile.md + system-prompt (1-host slice) MX reads DKIM-signed reply out
the load-bearing decisionThe whitelist is the firewall, not a "future" nicety. "Only reply to senders in the user's profile" reads like a v2 refinement; it's the thing that makes the feature safe to exist. An open, public LLM-backed email responder is (1) an unbounded API-spend faucet — anyone who learns the address can bleed cost — and (2) a spam-relay / abuse magnet. Gating replies to a registered user's verified address (keyed to the accounts auth.py already issues) closes both, and hands the persona a known correspondent. So this idea carries a real dependency: a user-profile / verified-identity layer that only partly exists today.

Design decisions & open questions

the reframe — my readThe reply engine is the feature; email is its heaviest transport. Separate what from how (the move §3 makes for audio). The new capability is a single-persona, threaded, whitelisted reply engine; email is one transport for it — and the most expensive to stand up. An in-app "DM a persona" mode delivers ~90% of the value on ~10% of the infra (reuses auth + the existing UI, no MX / DKIM / spam surface), and it's a strict prerequisite anyway — the email path needs that engine underneath. Recommend: build the DM mode first; add email as a transport once the conversation shape is proven and "lands in your real inbox" is worth the ops. Since then: the DM half has shipped — human↔human DMs (v465) and the persona greenroom, a private 1:1 where the persona speaks first (v424). The single-persona reply shape is proven; what stays unbuilt is the email transport and its MX/DKIM ops.

7 · Send-to-many — the isolation thesis as a productdiscussion

Captured 2026-06-21 — discussion-stage, nothing built. Open question flagged at capture: a feature, or a separate product?

The idea

One question — or a whole questionnaire — fired at many personas at once, each answering in a clean, isolated context with no shared transcript. Collect the replies, then aggregate them into something readable (a matrix, a contrast, a summary). Pitched as market research across personalities: how would N different archetypes each react to this pitch / message / decision?

Why this is the most on-thesis idea in the file

The room is the mixed condition: personas share one append-only context and react to each other (fluency, but convergence pressure). Send-to-many is the isolated condition at its limit — N independent inferences, zero shared context, zero convergence. §2 already named the prize ("one fact → N divergent reactions… a monolithic model given search would converge them"); this is that, with contamination set structurally to zero. It's the legacy AI↔AI instrument's core question — what does shared context do to the answers? — promoted to a user feature. The project already does this internally: the cold-subagent persona audit is send-to-one-cold-context.

same prompt, N clean contexts ONE QUESTION or questionnaire (M Qs) Buffett · clean ctx → reply Xiao Pang · clean ctx → reply Confucius · clean ctx → reply Musk · clean ctx → reply · clean ctx → reply REDUCE cluster by stance KEEP divergence REPORT matrix · contrast · summary N independent calls · no shared transcript

Where the design energy actually goes (not where it looks)

honest framingPersonas, not respondents. The output is "how these archetypes would react" — synthetic, directional, hypothesis-generating research, not a substitute for real people. Stating that plainly is the same epistemic honesty as the extrapolation contract; a "market research" label that hides it is the product lying.
feature, or product?A mode, not a fork. It shares the substrate (personas, the §6 reply engine, §5's selector as the "pick whom to survey" door) but not the room's interaction model (no live room, no SSE, no shared context) and not the output (a report, not a chat). Build it as a compose → fan-out → report surface over the existing substrate; "separate product" is a packaging / go-to-market call you can defer — forking the persona library to get there would be the mistake. Free bonus: the same panel + question run room (mixed) vs fan-out (isolated) is a built-in A/B on what shared context does to the answers — the legacy instrument's experiment, now a feature users can run.
Dialogue · roadmap · recorded in idea.md · updated 2026-07-20 (§1 the floor producer shipped; §2 + §5 shipped; §6 + §7 added; §3 the STT vendor note, §6 the DM half shipped) · see also Room language · Perceived speed · Rendering & speed · Function map