The execution tracker for the study: eleven steps, one at a time, owner-triggered("step N, go"). Each step ships standalone value, rehearses machinery the next step needs, and the risky things come after their cheap rehearsals prove out. The furniture session runs in parallel and feeds visuals in. This page is the living status — updated in the same change as each ship. Created 2026-07-20; nothing started yet.
i18n_audit for new UI strings · BUILD + SW CACHE bump · Style Guide sync on every UI ship · the matching room2 page updated in-change.| # | step | size | furniture dep | status |
|---|---|---|---|---|
| 1 | host @-mentions — fail-soft chips + telemetry | S | none | owner-tested ✓ (v536–v537) box deploy pending |
| 2 | host reactions — persona actors · tally into panel · public attribution | S–M | none | owner-tested ✓ (v538) box deploy pending |
| 3 | /roll — the host's roll | S | die (placeholder box-with-number shipping; real die from the furniture session swaps in later) | self-tested · awaiting owner test |
| 4 | the handed roll + THE TABLE + step 4.5 (armed single-cast · the hand · the patient tap · continuations) | M–L | die (placeholder box) | 4.5 self-tested · awaiting owner test |
| 5 | the router + <note> — the envelope | M | envelope (placeholder card) | self-tested · awaiting owner test |
| 6 | <board> — pinned HUD | M | board pin (placeholder) | self-tested · awaiting owner test |
| 7 | the gate — collects, sealed, collect-roll | M–L | roster card | complete (7a circle · 7c sealed bubbles · 7b collect-roll) — self-tested; circle owner-tested ✓; bubble visuals deferred by owner |
| 8 | the session runtime — cards go live | L | card face | queued |
| 9 | wake — the follow-up | M | capsule | queued |
| 10 | the aside — whispers | M–L | whisper bubble | queued |
| 11 | the door — invite · retire · cast search | M | proposal chip | queued |
Step 0 — the five game cards as authored content (prototype/cards/) — can happen any time; it needs no code and its openers already exist in the study. It becomes one-tap at step 8.
Goal: the panel emits anchored @{u:N} when addressing a human; the client renders the same mention chip humans already know. Mechanics: a small universal-SP addition (the model already reads every id off 「Name (uN)」 lines); panel-output validation against room membership — unknown/malformed token renders as plain text (degrade-never-delete) and increments a parse-health counter; chip rendering extended to persona bubbles; a sweep of the non-resolving surfaces (speechText/TTS, push previews via resolve_markers, f's tail).
Accept: chips render on device; malformed-token rate visible in parse stats. This telemetry is the evidence gate for step 10.
Goal: the host reacts to human bubbles; reactions become a channel the host can also see; attribution is public. Mechanics: a <react> output block scoped to the current turn's batch lines (v1: no line ids in the prompt — positional only); react_line grows persona actors (p:slug) in the event + projection; a reaction-tally line enters the panel prompt (f's warmth read stays as-is); the badge opens a who-reacted-what list, host included; SP manners (sparing, in-character, the quiet half of a turn).
Accept: host 👍s an answer without taking the floor; anyone taps the badge and sees who reacted what.
Goal: server truth exists. Mechanics: a <roll> request block (roll / flip / pick-among-members); server RNG writes a new roll system event; rendered as the die furniture card (result as pips + the seal + provenance line); rides history so the panel narrates it. No user-facing command anywhere.
Accept: draws and rolls land as system cards, visually unmistakable from bubbles; fairness unit test on the RNG path.
Goal: the host hands a player the dice. Mechanics: <roll for=uN> → the first mutable stream object, built on the established idiom: append-only events (roll_offer → roll_result) + read-time projection, so redact/replay discipline is inherited; a tap endpoint authed to the target user only; the composer dock for the addressee; SSE update-in-place; the result enters the loop as an input event so the host narrates it. No timeout logic in v1 (the host can re-offer in words).
Accept: two-account test on :8011 + a phone — 乙 taps, everyone watches it stamp, the host calls the 7.
Goal: the wildcard router becomes real, and the host gains hidden memory. Mechanics: refactor the turn decode from ad-hoc <speak>/<confer> parsing (+ steps 2–4's blocks) into a typed-block routing table — one parser, registered handlers, degrade-never-delete per row; <note> persists in state.json (beside pending/turn_queue), re-injects into every megaprompt, never renders; a sealed-capsule system event marks the commit visibly (timestamped); <reveal> opens the envelope. The confer is not a hiding place — the drawer is guest-readable by design.
Accept: a full game of 20 Questions — the reveal matches the turn-1 commitment, and the capsule's timestamp proves it. After this step, 6/7/9/10/11 are each "one more row + its UI."
Goal: public state stops living as chat spam. Mechanics: a router row; content in a constrained grammar (text · tables · the vendored mermaid — never arbitrary HTML, per the study's guard); append-only board events + projection (the reactions idiom → free history); pinned under the topbar, updates in place, collapsible; re-injected into every prompt (the board is a public note — the model must remember what it pinned).
Accept: trivia night with a live scoreboard and zero scoreboard re-posts in the stream.
Goal: go-arounds — everyone in, then one turn. Mechanics: collect open/release events; held submissions stay out of both replay paths until release (the see_from read-path law — the panel must not see unrevealed content); the roster card shows who's in/outstanding; the ambient composer dock announces the mode before typing; sealed as a flag (open collect = visible but held); release — all-in or a human tap — drains everything into one panel turn; the collect-roll composes here (for= a list routes through the gate; ties re-roll server-side).
Accept: a brainwriting round, an RPS match, and a collect-roll 定先手 — all with the host speaking exactly once per round.
Goal: the step-0 card texts become playable objects. Mechanics: card loader (prototype/cards/<slug>/card.md, frontmatter renders the face — the persona-card pattern); attach/detach with the assembly stack (universal SP + profiles + moves manual for enabled moves only + the card + history — plain rooms byte-identical); casting with invisible defaults (personas present take persona seats, humans present are the players); the session strip; the receipt event + 再来一场. Entry per the encounter principle: host offers and one-tap accepts first — no deck UI yet.
Accept: 默契考验 played end-to-end from a card by someone who's never seen a maker-vocabulary word.
Goal: the host may open a turn. Mechanics: a schedule store + timer in run_room; the turn itself is kickoff-shaped (the guest-message-less turn already exists twice: kickoff, greenroom); per-room consent + rate caps — ship-it-shy; the arrival capsule names why it fired (punctuality, not surveillance); rides the notices rail + v489 push for away users. Composes with the gate (straggler → nudge).
Accept: the agreed 明晚八点 follow-up fires on time, once; nothing ever fires unconsented.
Goal: the host takes one player aside. Mechanics: a router row with inverted failure discipline: degrade-never-deliver — unknown target means undelivered, never delivered wide; anchored uN targets only; per-viewer replay filtering at every exit (the delete-for-me _hide_filter seam); per-user SSE delivery (the rail's precedent); whispers ride the megaprompt (secret from humans, never from the model — correct for a host); the dashed-border whisper bubble with its standing label.
Accept: three-account dating-coach scene — the counterpart never sees the tip, on any device, after any reload.
Goal: the cast becomes alive. Mechanics: door router rows (invite/retire, anchored p:slug/u:N); the consent gradient from the study §9 (free only inside a card-declared 「+ 嘉宾」 seat; propose-and-confirm chips elsewhere; removing a human is never host-executed); cast search = a truth-utility block running the picker's fuzzy search over tags.txt, gated by discoverable(); arrivals through the kickoff/roster plumbing with one entrance line; cast-size and floor caps bind unchanged.
Accept: Asimov proposes Doyle for a crime game; a human's tap seats him; he enters in character — and a private persona can never be summoned into someone else's room.
<roll> tags from hist, so the model saw results "just happening," learned "I say rolling and dice appear," and by turn 4 narrated intent with no tag. Fixed same day: hist keeps the tags (clients still never see them), roll events land after the turn's events (replay order = live order), and the pending note re-teaches the grammar. In-context evidence beats any SP instruction — the model's own transcript is its strongest teacher. Owner's Plato-room test (five clean rolls, correct parity narration) surfaced the timing critique: the card was welded to the reply that asked — ask and answer as one staged beat, sometimes a bare card with no speech at all. v540 polish: the tumble beat (a live card lands mid-tumble, resolves in place ~1.4s later; replay resolves instantly) + the SP rule never roll silently. The full "who rolls, when" answer is step 4 — the handed roll, where the player's tap decides the moment. Step 4 implemented + self-tested 2026-07-20 (v541), owner-directed widening: THE TABLE — the room's die became live state in a fixed dock (always visible while dice are in play; pending「点一下掷出 / 等 X 掷」→ tumble in place → the latest face), the stream demoted to compact 🎲 record capsules, placement rule now three surfaces (stream=records · table=state · composer=your action). Mechanics: roll_offer/roll_result event pair + dice_state() projection (rides replay, cold-open safe, v539 rolls read as idle faces); <roll for="uN"/> hands the die — the addressee's tap resolves it (auth'd, current-die-only) and the tap is the human input: it queues the narration turn and wakes the worker; the host's own rolls auto-resolve behind a 1.6s beat. Found: only speak events minted lids — the dice pair joined the sequence. v542 (owner test): the dock painted under the bubbles and wasn't tappable — content-under-bars slides the stream's box over anything unpositioned in that band; the table joined the z-ladder (relative, z 20 — topbar 36 · composer 30 · table 20). Step 4.5 (v543, owner-directed redesign after live play): armed single-cast — the die is armed for exactly ONE throw (for="uN" exclusive · for="any" the room, first throw wins · no for = the host's own) so re-roll cheating is structurally impossible; the table went read-only (state + attribution:「2d6 · 5+6 · Dan 掷」) and the hand became the only cast control — the die in your hand, fixed above the composer at thumb reach, tap → tumble → fly up to the table; the patient tap — a human's cast no longer summons the host: the narration cue waits (~8s, extends while typing) so roll-then-speak batches into ONE turn; continuation turns give the host speak-roll-speak on its own casts (loop-guarded: a roll-cue turn cannot cast again); <roll away/> clears the table, bare <roll/> re-casts. Trap hit: the measured --composer-h var ran stale (86px vs a real 138px) — the v488 lesson again; the hand anchors on the composer's live rect instead. Owner's Mark Twain session (v543): mechanics held ("when it works it feels great"); v544 polish — the table's attribution wears the roller's avatar + colour (by_slug rides the events). The remaining wobble — "who has the dice" wrong at times — is the model's game-state tracking (whose-turn + parity slips, self-aware: 「我这人霸占骰子的毛病」), i.e. exactly what steps 5–6 externalize (rules in the note, whose-turn on the board). Patience window (8s) noted as tight; owner defers. Step 5 implemented + self-tested 2026-07-21 (v545): THE HINGE is real — extract_panel_blocks is the routing table (rolls · reacts · notes · reveals in one pass, hist-frozen first; a future move = one row + its apply), and the envelope works end to end: <note>…</note> seals into the panel's persisted notebook (rides every turn's content; hist keeps tag+content — double memory) with a visible timestamped capsule; <reveal/> opens the oldest seal as the card — content · seal time · 「封存后未曾改动 ✓」. The acceptance battery ran the full 20-Questions chain: the reveal carries the original seal timestamp, the notebook survives a restart, and the secret never touched a bubble. SP gains SEALED NOTES (seal BEFORE play · answer FROM the note — "an unsealed secret is an improvised one, and rooms can tell"). Step 6 implemented + self-tested same day (v546), one router row as promised: <board title="比分">…</board> pins/replaces the public state strip (board_set events + projection — update-in-place, restart-safe, rides every turn's content so the panel remembers what it pinned); <board away/> takes it down; zero stream records — the acceptance ran a trivia round with a live scoreboard and no scoreboard re-posts. The dock became one zone with two slots (board pin above the die, collapsible, hides only when both empty). SP: THE BOARD — state lives on the board, never re-posted as chat; whose turn belongs there (the Twain whose-turn fix, structural at last). The Holmes room (owner test) found the router's blind spot — the model played a flawless quiz (sealed every answer, boarded every score, revealed every round) but placed its action tags outside the <speak> blocks, and the router only scanned decoded speech: every board and reveal silently vanished, and the visible wreckage (no whiteboard · unrevealed seals · cascading prose leaks) looked like model failure. Fixed same day: extract_outside_blocks salvages tags above/below the speak blocks (paired spans stripped first, unclosed speak swallows the rest — no double-collect; guarded off the json-content/quarantine paths where raw IS the replies). Actions are actions wherever they sit. SP also gains the anti-leak line: a re-ask or hint must never contain the answer — the reveal card is the only door. The Trivia room (10 clean rounds — board · seals · reveals all flowing) exposed the envelope’s last gap: on composed logic puzzles, rulings contradicted the seals (sealed 一小时, ruled 45分钟 correct; sealed one riddle’s answer, judged another) — facts converge across turns, compositions don’t: the model re-derives instead of consulting. Countered same day (fbf3a74): notes must be labeled(第5题答案:…), the notebook’s riding note now binds rulings, and SP gains THE JUDGING RULE — if the note seems wrong, rule by it and admit the flaw; a ruling that contradicts a seal is the one unforgivable move. Step 2 implemented + self-tested 2026-07-20 (v538): hosts react via <react emoji="…"/> (stripped, applied as p:<slug> events on the batch's guest line; a react-only reply = the nod without the bubble); humans' reactions reach the panel as delta notes; the sheet shows host reactors — attribution public. Owner-tested same day: first direct ask landed a real 👍 chip (old room, post-restart). Finding worth keeping: compliance is character-gated by design — Jack Ma taps 👍 cheerfully, Bob Dylan declines in voice ("I'm a song and dance man, not a telegraph operator"). The tool works; the taste belongs to the persona — the SP's persona-first rule extending to furniture, as intended. Organic (unprompted) react rate = watch the parse-health reacts counter before any SP warming. Step 7a implemented + self-tested 2026-07-21 (v547): THE CIRCLE, sealed collect end-to-end — <gate>问题</gate> opens a go-around over the room's humans (one router row, as promised); while open every human message is HELD in the gate object (state.json only) — no event, no transcript line, no panel wake: the read-path law enforced by construction, so neither replay path can show a held answer (Ben's replay literally does not contain Amy's text). A second message revises; the sender's bubble seals (dashed + 🔒), everyone else sees only the dock's roster card (question · who's in · n/m — never what). Release = all-in auto · any member's two-tap Release · <gate away/> (posts what's in — typed words are never destroyed); the answers become real events AT ONCE (original nonces stamp the sealed bubbles into ordinary ones) and ONE cue-carrying turn reads the whole round. Live probe (Twain, deepseek, two users over HTTP): four rounds — brainwriting round released all-in and the model read the answers as a set(「"复苏"与"泥土"——两位各抓住了一半的春天」), early release via the route posted the lone answer, and the release cue batched with a queued human line into one turn (the batching seam holding). Bug caught at paint level: the `gate` close SSE outruns the release's `post` events — clearing my-held-nonce there left the sender's bubble sealed forever; the post with my nonce is what unseals it. Remaining for step 7: the collect-roll (7b) — for="u3,u5" arming through the gate, ties re-rolled server-side, the 定先手 acceptance. The Cameron-ish room (owner test, v548) caught the one-brain hole live: solo-human RPS, the host as referee-and-player — its own move was never sealed anywhere, and it "revealed" the perfect counter after reading the human's answer four rounds straight (4:0; honest odds 1-in-81). Nothing had taught the two tools to compose. Fixed same day: SP gains the host-as-player rule (seal YOUR move via <note> WITH the gate; <reveal/> FIRST at release — "announcing a move you never sealed, after reading everyone's answers, is cheating, and the timestamps prove it either way"), the roster card grows the opener's ✉ chip (host_sealed = an unrevealed seal by the opener — the human can SEE the commitment), and the release cue demands reveal-before-ruling. Re-probed live: Cameron sealed(石头), revealed on the answer, and ruled against himself(「你的布把我包了——你赢了」). Also from the owner test: all circle copy left internal jargon("opened the circle")for plain 密封收集 language — the open capsule may be the host's entire move (a pure-gate reply drops its bubble), so it must explain itself. Two open observations: first-turn tool compliance is variable (the model sometimes mimes the furniture in prose before operating it next turn — the step-8 card's opener will pin the first-move discipline), and for a solo human the envelope alone already makes the game fair — the gate earns its keep at two-plus humans. 7c (v549, owner redesign, same day): SEALED BUBBLES — one commitment channel for everyone. The owner's call superseded the Cameron patch with structure: every circle submission — human or persona — lands in the stream immediately as a sealed bubble (author + 🔒; content server-held; yours shows your text), and all open together at release. The persona submits via <seal>出招</seal> riding the gate-opening reply (one router row; subs keyed p:<slug>) — its move is committed before any human answer exists, which is the whole fairness; at release persona moves unseal first as real speak events (chronology on record), and the cue lists them verbatim — or tells a playing host that sealed nothing you have no move; judge only. Live-probed through four RPS rounds: the no-seal backstop held(「这把只判,不出招——布赢了空气」), the sealed round ruled honestly from the committed move(sealed 石头 → 「石头砸剪刀——我的」), a three-player round released Cameron-剪刀 / Amy-剪刀 / Ben-石头 → 「Ben赢!」, and both viewer seats verified at paint level (the observer sees placeholders land live; the completer's own bubble unseals with its lid — the release runs inside the completing /say, so its post SSE outruns the response: the route hands the lid back and the client unseals directly). The one-brain caveat stands for persona-vs-persona simultaneity — and the owner's stated future architecture (one independent LLM per persona) dissolves it when it arrives. 7c′ (v550, owner feedback from the Ang Lee room): two fixes. The seal leak — the model narrated its move in the visible prose AND sealed it(「手指蜷了蜷…剪刀 ✌️」+ <seal>剪刀 ✌️</seal>, three rounds; the router was fine, the theater leaked); countered twice: a server guard strips any standalone visible line that IS the sealed text verbatim (the exact observed pattern — safe because the gate question lists options inline, never as a bare matching line), and the SP now forbids the move appearing in visible words in any form(named · emoji · theater — "perform the gesture, never the content"). True send order — sealed bubbles now render in submission order(gate_payload.order), your own held bubble sits in its slot, and at release every bubble reveals in place (posts and persona unseals replace their placeholders; the server interleaves release events by the same order, so replay = the order the room lived it). Re-probed live: order ticked [ang-lee] → [ang-lee, u8] → reveal 布·石头·布 in sequence, opening bubble clean. Owner-tested ✓ same day("It worked")— the sealed-bubble/unsealing visuals deferred by owner for a later pass. 7b, the COLLECT-ROLL (v551) — step 7's last piece: <roll dice="1d20" for="all"/>(or for="u3,u7")arms one die per person — roll_cast events land publicly as they come (server facts have no anchoring problem; the table's meta grows per-thrower chips), each member's hand die retires with their throw, and when the last is in the server settles it: top ties re-roll automatically (round-stamped, on record), then ONE multi roll_result(the 定先手 capsule「Ben 8 · Amy 5 — Ben 先」)and ONE patient narration cue — partial casts wake nothing, so the host speaks exactly once per round: the step's acceptance, passed live first try(Twain armed for="all" unprompted; "the dice have spoken — the floor is yours, Ben"). Grammar note: all now means everyone rolls; any/anyone stay first tap wins. Owner's Newton-ish + Ang Lee tests (v552) sharpened the boundary law: the collect record had "goes first" baked in — wrong for a compare-rolls game — so the furniture now states FACTS only (the numbers, winner bold; the cue hands the host「highest: X」and says the meaning — turn order, the round's point — is the game's rule, declared once). The Ang Lee RPS meltdown (a two-布 round scored to nobody, an improvised tie house-rule, +2 self-award, then「这算谁赢?」asked at the players) plus Newton's initial flat prop-denial(「这里没有实物的骰子和白板」— zero tags four turns, cured only by an explicit-syntax opener no real user should ever type)both point the same way: per-round rule-judging, scoring, termination, and put-the-tools-away are SCENARIO discipline, and scenario discipline is the CARD (step 8) — pinned rules the host re-reads, an opener that makes the first move mandatory, a closing move that clears the table. Also overdue: the one-game sonnet A/B on game-state discipline (third data point now: Trivia seals · Lee's judging · Lee's termination). v553 — the owner's three pre-step-8 sanity rulings, all shipped. ① The abstract die: a roll is a draw from a distribution — numeric(NdM)or categorical(<roll pick="红, 蓝, 绿" for="all"/> now collects too). ② Winning is the room's contract: the server never ranks, never names a winner, and the auto tie re-roll is gone(it assumed highest-wins-unique; in 比大小 a tie means no points, and re-rolling would change the game)— results post as facts in throw order, ties stand, the cue says meaning is the game's agreed rule(default conventions allowed, said aloud)and re-arm-the-tied is the break. ③ Tool invocation: the SP gains ONE WORKED EXAMPLE — a complete tagged opening reply(board + collect in one <speak>)plus the two anti-patterns(never narrate props into existence · never claim the room has none). A/B'd immediately: the same Newton + the same natural-language prompt that drew flat denial in the morning → first reply armed the collect AND pinned the board, judged round 1 right, board ticked 1:0. In-context shape beats description — the v539 law applied forward. (Escalation ladder if compliance sags again: hist-seeded exemplar exchange → keyword-triggered riding nudge → the card's pinned opener.)