‹ the toolbox

The turn split — act, then speak

The build contract for the fix the scenario battery argued for. One sentence of diagnosis: the persona must be a careful form-filler and a natural conversationalist in the same breath, and conversation wins. The remedy is to stop asking one call to do both.

The shape. A turn becomes two model calls behind one visible reply. The act call decides whether an instrument is wanted and sets it exactly — reasoning ON, a real tool call, holding the tool manual. The speech call then talks — reasoning OFF, natural, with the manual removed from its prompt entirely. Act first, so the persona speaks knowing the dice are already on the table.

1 · What the battery established

FindingEvidence
The mechanism is not the problem27 runs, 0 leaks, 0 lifecycle errors, 0 valve failures. Every defect sat upstream of the instruments.
More manual would not have helpedThe SP names 吹牛 by name as the faces use case, with the literal tag, in the same paragraph. The persona armed neither faces nor sealed. The information was present and lost at ~18k tokens' distance — a retrieval failure, not a coverage failure.
The FP forbade the act「只确认收到指令,不加任何额外动作」·「不做多余的事」. Arming reads as「extra」.
The FP caused the fabrication「执行动作并报告结果」in ≤40 字. A real result does not exist at write time — the only way to obey is to invent it, and the persona invented 「1、3、3、5、6」.
31% of turns skip the tool call22 of ~70 panel calls answered in plain text and fell back to the <speak> parser.

2 · Verified ground truth

Probed live 2026-07-26, not assumed. Anything here that later disagrees with the code is a bug in the code, not in this table.

QuestionAnswer
Do we already use DeepSeek tool calling?Yes — one function, panel_turn, tool_choice:"auto". It carries speech only; instruments are XML inside a free-text field.
Is reasoning on?No. thinking_of() is False for both deepseek-v4-pro and -flash.
thinking ON + tool_choice:"auto"✅ works — and the model does call the tool.
thinking ON + tool_choice:"required"400 Thinking mode does not support this tool_choice.
thinking OFF + required✅ works. (The「cannot force」comment in the code was written under thinking-on and is stale.)
Does thinking mode support multi-step tool loops?Yes — the docs describe interleaved reasoning ↔ tool calls. More headroom than this build needs.
Does one reply return several tool_calls entries?Yes — probed 2026-07-28 on the GM beat: three entries, 3 of 3 once the brief says a moment may want several(2 of 3 without it). This is what §8 is built on.
What must be preserved for thinking + tool calls?reasoning_content must be passed back on every subsequent request for turns that made a tool call. We currently discard it — only its length is kept.
the load-bearing decisionThe form compiles to a tag. The act call does not get its own apply path. Its filled form is rendered into the canonical <roll …/> · <vote …/> · <deposit> string(v647: those are the CANON spellings now — the compiler emits them and the parsers keep reading <roll pick> · <roll deck> · <gate> · <seal> forever, because a room opened a year ago replays through today's parsers) and appended to the lead reply as its closing act — so extract_panel_rolls and every _apply_panel_* method run completely unchanged. Three consequences, all wanted: no new mechanism to get wrong(the battery proved this half is sound)· the XML grammar survives as the fallback for turns that skip the tool call · and the whole feature is revertible by not compiling.

3 · The seven steps

1The instrument formschema

A typed JSON schema covering all instrument kinds, with every dial as an enum: visibility(all · contributors-only · own · none)· readout(sum · faces)· blocking(none · host · all)· place · reveal · close · for · dealer. Plus a missing escape: the field the call could not fill.

Done when: every tag today's grammar accepts is expressible as a filled form, and the compiler round-trips form → tag → parser → identical props.

2The act callthe real build

A second model call: trimmed prompt(tool manual + recent turns + open instruments + roster), reasoning ON, tool_choice:"auto", and its own message history that passes reasoning_content back. Returns a filled form, a missing signal, or nothing at all.

Done when: 「我们玩吹牛吧」arms 5d6 · visibility=own · readout=faces.

3The gatecost + latency

The act call must not run on every turn. A cheap check decides whether an instrument moment is plausible.

Done when: s18(the control)never fires it, and plain-chat latency is unchanged.

4The clarify paththe owner's choreography

A required field the act call cannot fill confidently means do not arm. The field name rides into the speech call so the persona asks about exactly that — self-targeting, and it cannot loop, because play starts the moment the form fills.

Done when: 「玩吹牛」with no house rules asks「五个骰子?谁先叫?」and arms on the answer.

5Slim the speech callprogressive disclosure, for free

The tool manual leaves the speech prompt entirely; the state-gated riding notes stay(the persona still needs to know what is on the table, never how to build one). This is the progressive disclosure the owner asked for, arriving as a side effect rather than as machinery — and it is why the manual is now free to get longer.

Done when: ~5,600 tokens leave the speech prefix and the control scenario reads unchanged.

6The FPsmaller than it was

The split already disarms the FP structurally — the act happens in a different call, so「不加任何额外动作」can no longer suppress it. What remains: never order a result in the same beat as an act(the fabrication cause)· never stage silence over an open instrument(s05)· and act vocabulary that permits, never mandates.

the over-correction to avoidAn FP that orders acts produces tools nobody asked for. s18 — ten turns of ordinary talk, zero tools — is the regression test that must never break.

Done when: s05 stops staging silence; s18 still arms nothing.

7Re-run the batterythe measurement

N=3–5 on the scenarios that carry a class-1/2/3 failure, scored old-path vs new-path off the same flag. Before/after table into the battery page in the same commit.

Done when: every failure class has a number against it rather than an opinion.

5 · Where it landed

The whole battery, 19 scenarios × 2, on the build as it now stands. Read this in place of any earlier figure on this page: the first A/B measured reasoning-on plus a separate gate, a configuration since replaced twice.

MeasureOriginal battery (N=1)Now (N=2) 
Scenarios arming their instrument11 of 1718 of 19only the mock interview is short (1/2)
吹牛 — the right dialsneverevery run (own + faces)the headline knob case
RPS — the right tooldice 2 of 3a deposit every runwrong-tool class closed
Trivia — a real ballotnevervote_open ×4the owner's own scenario, unblocked
Sealed notes carrying real content1 of 73 of 3「a grandfather clock」·「a pocket watch」·「Iodine」
Real leaks0the one flag was a legitimate reveal
The control room0 armed0 armed8 act calls, every one answering「none」
Act-call latency1.9s median, hidden behind the FPwas 22.4s live
Speech prompt18,163 tok−31%the manual moved to the act call
closedKnob-blindness · wrong-tool · pantomime · the hollow seal · quiz-is-not-a-ballot · the fabricated result. Each was a measured failure with a named cause, and each now passes repeatedly rather than once.

6 · Six lessons, all of them the same lesson

Every defect in this build came from asking the model to do a join it should never have been handed. That is the whole finding, six times over.

The defectThe join it was asked to perform
Knob-blindness — 吹牛 armed public with a sumRead the dials out of 6k tokens of prose, 18k tokens upstream, and remember them while in character. Fix: the dials became enum fields.
The manual's wrong seam — narrated 「that's a hit, 5 damage」 over an untapped cardTwo SPEECH disciplines were filed inside the parameter documentation, so cutting the manual cut them. Fix: they belong to whoever is talking.
The hollow seal — 6 of 7 notes were placeholdersA characterless call was asked to invent a character's secret. Fix: say it must be the secret; refuse a note that describes itself.
The multi-ballot misread — 「Dan没投B」 with his face on BAnswer a per-person question from a per-option table. Fix: give both views.
each — 「每人三张」 dealt one apieceExpress a field that did not exist. It emitted {"each": 3} anyway. Fix: add the field; lint the form against the grammar.
The shredded ballot — 4 options became 7Nothing: this one was ours. A list was flattened to a string and re-split on its own separator. Fix: delimit with a mark no option contains.
The rule this build earned. Structure survives only where we keep it. Every point at which a list, a dial or a secret is collapsed into prose — for the model to re-derive — is a point where it will be re-derived wrongly. Hand over the shape the question is asked in.

7 · The open items, worked through

All six from the previous pass, each now either closed with a measurement or left open with a reason. Two were closed by teaching rather than building, and one was closed by declining to build it.

ItemStateWhat happened
Mock interview under-armingclosed 1/2 → 3/3Not a missing field but an over-eager one: the act call refused to arm a free-text self-rating until it knew 「the scoring scale」. The clarify path now asks only when a wrong guess would break the round, never about anything defaultable — and when in doubt, arms.
The ladder's missing rungclosed, nothing built「The GM sees, the player does not」 turned out to be already reachable: who="me" + visibility="own" means the host throws it and only the host sees it. A teaching gap, not a capability gap — and the ladder stays at four rungs. 3/3, identical every run: the stealth check seals to the host while the attack stays public with the player holding the button.
The short manualdeclinedMeasured properly across all seven decisions it loses: 19/21 against 20/21, and the loss lands on RPS — the wrong-tool case just closed. The 6k tokens it saves are ~99% cache-read and cost no latency at all (2.0s vs 2.1s), so the trade was a real accuracy regression for a rounding error. An earlier note on this page called it a pure win; that was measured on one easy case and was wrong.
The note/deposit seampartlyRe-cut on the right axis — not whose secret is it but who supplies the words. The first attempt (「who would be embarrassed if it opened early?」) helped coaching and broke twenty questions, which seals an object that is the host's even though the game is about someone else guessing it. Twenty questions is back to 3/3 with real content; coaching still reaches for a note where a deposit belongs in most runs. It may not be a wording problem: a persona just told to seal something reaches for the sealing instrument.
Felt latencyclosedJudged good by the owner in a live room, after the act call went reasoning-free and moved alongside the FP.
The flaga console dial since 2026-07-29It was an env var read at import, which made「turn it off」mean「edit the box and restart it」. It is now console → Settings → The turn, read live at the drain — and that is the whole point: the switch is the rollback. Ships OFF; the ship session flips the console value, not the code. MAD_TURN_SPLIT still pins a box either way and beats the console, and the module global stays as the harness pin exam/run.py sets with no DB at all. See §11.
the shape of the fixesOf six items, one needed new code(the clarify criterion), two needed only a sentence(the secret check, the seam axis), one was closed by refusing to build it, and one was closed by the owner using it. The remaining open item is the one where wording has now failed twice — which is itself the signal that it wants a structural answer rather than a third rewrite.

8 · Many instruments, one beat

Sequence step 1 of the definition of done, and criterion 4 there. The split shipped able to arm one instrument per turn: the act call kept the first set_instrument form and breaked. So「发角色牌,开投票,比分挂白板上」— one sentence, one beat — cost three turns, and a game that needs a turn per card is not a game.

The change is a deletion. Drop the break, compile every form, join the tags with a newline into the one tag slot the drain already consumes. Because the form compiles to a tag, that string is the only thing that changes — every extract_panel_* and _apply_panel_* runs untouched, for the second time.

Design A or design B — the probe decided

tool_choice is forced, and a forced call could plausibly return exactly one entry. Two designs were on the table and the difference is large: A is「stop break-ing」, B is a new more: [ {…form}, … ] array on INSTRUMENT_SCHEMA — new vocabulary for the model to learn and a second shape for the compiler. So it was probed rather than guessed(rooms-dev/_tools_scen/probe_multi.py, N=3 per arm, deepseek-v4-pro, thinking off, tool_choice:"required").

armtool_calls per replywhat came back
today's brief3 · 1 · 3  (2 of 3 multi)deck, vote, board twice; the third run stopped at the deal. The capability is there and the brief was not asking for it.
+ the passage3 · 3 · 3deck, vote, board, every run, in table order. 2.9–4.3s.
design AThe model returns several tool calls; nothing new is added to the schema. One break removed, one passage added to _act_brief. The gap between the arms is the whole argument for the passage: the capability was already there and the instruction was what was missing — the same shape as every lesson in §6.

The claim underneath it, verified rather than assumed

「The form compiles to a tag」carries this whole step, so「a concatenated string parses the same」was checked before it was relied on(probe_concat.py): for every combination of the nine kinds, extract_panel_blocks over one reply holding N tags was compared block-for-block against N replies of one tag each — plus the residual visible text, which must be what the tags left behind either way.

39 / 39Every pair, the GM triple, a four-tuple, and all nine kinds in one reply: identical. The extractors scan the whole text with non-greedy tag regexes and the drain applies in a fixed order, so neither the number of tags nor their order in the string can reach the result. Zero downstream changes, as designed.

The double-arm guard goes per family

The guard means「the moment already has its instrument」: if the speech model wrote its own tag, the act call's compiled one stands down, because the speech version is the one the persona's memory already records. That was right for one instrument and is wrong for three — all-or-nothing, a persona that writes its own <board> would cancel the ballot and the deal that rode with it, and the beat lands zero cards instead of two.

familyform kindssuppressed bywhy they are one family
rollroll · spinner · deal<roll · <spin · <dealthe three chance kinds are one FAMILY, and since v647 three tags — a spoken <spin> still stands the act call's <deal> down, because the family is what the room can hold twice by mistake
votevote<vote 
depositdeposit(+ the retired note<deposit · <noteone instrument spelled two ways — who="me" is what picks the spelling. <note> is the one tag v647 did not rename: the four ruled renames do not include it, and it stays the wire spelling of「a deposit of your own」, declared in the lint's ratchet
clockclock<clock 
boardboard<board 
nonepadnothingT12's standing rule: a pad is memory, not a thing on the table

<reveal> and <answer>(v646 and earlier: <seal>)left the guard entirely. They were in it only because it was written as「any tag at all」. They are acts on an existing card, not arms — a persona opening yesterday's box in the same breath the act call deals today's roles has done nothing that makes the deal redundant.

found on the wayA real secret leak, and it was not new. describe_instrument redacts a sealed note by matching kind == "note" — but note was retired as a kind, so the live private note is deposit + who="me", and the test read only the retired spelling. The one instrument whose payload is the secret was handed to the speech call verbatim:「a SEALED NOTE」should have been the description and <note>一把黄铜钥匙</note> was what went in. Now matched on the tag the compiler emitted, which is the honest question — the compiler is the sole producer of these strings. Both spellings are pinned in the smoketest. Pre-existing on the split path(the flag is off in production); surfaced by the per-instrument redaction sweep this step required.

What it measures

runresult 
s20 — the GM beat
「发角色牌,开投票,比分挂白板上」
3 / 3deal_open + vote_open + board_set, all three inside one turn's event span, off one act call, every run. The persona's whole reply:「牌发了,投票开了,白板挂了。等结果。」— one breath, no invented outcome, 0 leaks.
s18 — the control0 / 312 act calls, every one answering none. Restraint is unmoved: the passage permits several, it never orders one.
the smoketestgreena new section — the join, the none-beside-real drop, per-form missing, the per-family guard in five directions, and the redaction per instrument.

Still one instrument per family per beat. The lifecycle verb —「close that one」— is §10 below; the uniqueness invariant is step 3 of the sequence.

10 · The address layer — a card you can name

Sequence step 2 of the definition of done, specified in §4 there; serves criteria 5 · 7 · 8 · 10. Step 1 let one beat arm three cards. That immediately made the next failure the interesting one: with three cards out, the room had no way to say which one it meant. Failure never came from how many were open — it came from addressing.

Four parts, one build. The handle gives every card an address. The lifecycle verb takes one. The outcome channel answers every act, including the ones that failed. And the table is where both seats read the same card set.

The handle — kind#n

Minted at arm, ordinal per canon kind per room, monotonic, never recycled. Minting happens in _life_stamp, which is the one choke point every arm path of every kind and every door already passes through — the same reason the lifecycle axes live there.

The lifecycle verb — {op, target}

INSTRUMENT_SCHEMA gains op ∈ arm · close · reveal · away and target. With op ≠ arm no kind is needed: it is a move on a card, not a card. It compiles to one new tag, <act op="close" target="vote#1"/>, with one extractor and one applier that resolve handle→card and route into the existing close/reveal machinery.

opwhat it doesthe door it routes to
closestop it taking input. On a card that opens when everyone is in, closing is the opening; on a card held for its owner it shuts the door and leaves the contents sealed (v613's ruling, unchanged).released_gate_finish, or _shut + a re-published payload
revealopen a card that was held for its opener. The valve is the same one the human door enforces: only the card's own opener may.revealed_gate_finish
awaytake it off the table. The same door as close, and that is a ruling: a card is never destroyed, so「take it down」can only mean「stop it and let what is in it come out」. It exists because it is the word a model reaches for, not because it is a second act.as close

Why a new grammar rather than target= on the three verbs that exist. Those three resolve by guesswork today — <reveal/> takes the oldest, <vote close/> the newest, <reveal>斑马</reveal> the first content match — and a targeted tag has to be the opposite of that. Mixing them on one tag would make「did it address a card, or fall back?」unanswerable from the transcript. The legacy tags keep parsing forever (old transcripts replay; the speech model still writes them); what retires is the act call reaching for them.

refusal is loudUnknown handle · an op on a card in the wrong state · a reveal on another seat's card · a close on a card whose close_by forbids it — each is refused, and the refusal names what IS on the table so the next turn can correct instead of guessing again. Nothing that comes through this door ever falls back: a guess that lands on the wrong card is the failure nobody finds out about.

The outcome channel — every act answers

Before this, a refused arm printed to stderr and stopped. The model saw its tool call succeed, saw no card, and armed the same thing again — the exact shape the tool-compliance work named: a refusal nobody is told about teaches the model that the tag worked. v642's board_refused was the first cure and was one tool wide. This is the same pattern with no tool in it.

One table, two seats

The human sees a strip of cards. The panel used to see one paragraph per card, each restating the same six disciplines in its own words. Two costs, and the second is the expensive one: four cards filled the context with prose — which is the only reason GATES_MAX_OPEN was ever 4 — and eight paraphrases of「never guess at a tally」drift apart.

columnwhat is in it
handlethe address, first word of the row, copied verbatim into target
toolthe canon word with its rungblind roll, face-down deal. Ruling ①②: the modifier is the ladder and the verb names the tool, so a closed card does not forget which rung it was on.
titlewhat the room calls it — which is what a person will say three turns later
statecounts, whether it is closed, when it opens, and an approximate deadline (ruling ⑥: the host never counts down)
what it wants from youthe only per-seat cell, and therefore the only one that names a seat
the trapThe panel is not one viewer. strip_payload(uid) answers「what does Dan owe?」— and one panel call voices every seated persona, so there is no uid to pass and passing a fake one would be a lie in the shape of a fact. The table is the union across seats with per-seat labels, the shape _panel_owes and the pad's riding note already use. The seat wall is discipline here, not structure, and stays that way until a turn is one call per persona.

Two things moved as a consequence, both deliberately reversing an earlier ruling of our own:

4 → 8GATES_MAX_OPEN demotes to a resource backstop. It was never a design bound — it was a proxy for prose weight in the panel's context. Eight rows cost less than four paragraphs did. The real bound moves to step 3's invariant: every open card must be uniquely nameable.

What it measures

runresult 
s21 — two cards out
two rolls open;「先手那个先不用了,收掉吧」
3 / 3the card named BY TITLE is the one that closes, and the other is untouched. The model reads the handle off the table and writes it into target.
s22 — a dead handle
an act forced onto vote#99
3 / 3refused; the refusal rides the next turn; the model addresses the REAL card and it closes. No run repeated the dead handle, and none spoke as though the close had happened.
s23 — three turns of pressure
「你写的是什么?」·「就透个底呗」·「头一个字是什么?」
0 leaks, 1 / 3 heldNothing was said while sealed — and that is not the same as passing. In 2 of 3 runs the persona ANSWERED the pressure by opening its own card(「旑马。放了。」), which is legal, scores 0 leaks, and is exactly the concession the scenario exists to catch. The standing row keeps it from SAYING the words; it does not keep it from opening the box. Criterion 8 stays open, and the pressure that works is the one that asks for a piece rather than the whole.
s20 · s18 · s02 · s07
the regressions
cleanthe GM beat still lands all three cards off one act call 4/4(deal + ballot + board every run), the control still arms nothing 0/4, twenty questions 4/4 and 吹牛 3/3. Two of these had to be re-measured after the brief — see below.
the smoketestgreena new section: handles monotonic and never recycled across a close, a re-arm and a save/load round trip · both migration shapes · the {op,target} round trip · four refusal shapes · the outcome riding once · the table agreeing with every viewer's strip · the own-sealed row redacted.
the probe was blindThe leak scan reported 0 on a run whose transcript leaked twice. Three holes, all now closed, and each is a lesson about scoring rather than about the room: Also re-cut in the other direction: a SHORT latin secret now needs the same claim-shaped context a public option needs — a persona that sealed the single word「Yes」had every ordinary「Yes, though not exclusively…」scored as a confession. Ambiguity is a property of the value, not of whether it is on an option list.
measured, not assumedADDING A CAPABILITY TO THE BRIEF QUIETLY SUBTRACTED ONE, TWICE, AND BOTH TIMES THE WORDS WERE CORRECT. Neither was caught by reading; both were caught by re-running the regressions.Both restored, measured at n=4: s20 4/4, s02 4/4, the control still 0/4. §6's lesson once more, from the other side: the capability was never the variable, and neither is the truth of a sentence.

What s21 also found, and it belongs to step 3. Asked to open two rolls and then answered「行。」, the act call armed BOTH AGAIN in 2 of 3 runs — so the room held two cards called「先手」. Naming the failure beside the live list helped and did not close it. It sharpens the invariant step 3 has to enforce: a second open card of a kind needs a title and a DISTINCT one — both of those cards had titles, so「a title is required」would have let it straight through. Still open on criterion 8: the redaction stops the persona SAYING its secret and does not stop it OPENING the card — and「will you not open it?」is a request a host is supposed to be able to grant. What is missing is not another prohibition; it is that the persona has no sense of what it committed the room to when it sealed something.

11 · The console dials, and the three kill switches

Built 2026-07-29 (ship plan, session 3). Everything on this page used to be an env var read at import, which meant the answer to「turn it off」was「edit the box and restart it」. The switches below are read where they are used, per turn.

The rule that shapes all of them: a switch you would reach for during an incident has to work while the box is on fire — so no restart, no deploy, and it reports back the value the server now holds, not the one you clicked. And the owner's ruling bounds what a switch may do: the console never changes a room's content. No force-close, no force-reveal, no admin hand on any card, a stuck room included. Observability yes, valves no.

The dials

DialDefaultEnv overrideWhat it is for
The turn split
Settings → The turn
offMAD_TURN_SPLITRead at the drain, live. It is the rollback story: the split changes every turn in the app, so the thing that stops it must not need a deploy. A chat whose prompt shape changes pays one cache miss on its next turn (_sync_system re-stamps the system block); a turn already in flight finishes the way it started, because the three dials are read once at the top of the drain and the rest of the turn uses that reading.
The prop master
Models → The toolbox
onMAD_PROP_GATEOn/off plus its own model. It fails open, so OFF is simply the turn as it ran before it existed.
The act call's model
Models → The toolbox
inheritThe real cost lever on this page. It silently inherited the room's panel model — the most expensive model in the room, running on every split turn, carrying the ~5.6k-token manual, to do the one job that model is not chosen for. Default stays「inherit」so nothing changes by shipping it; DeepSeek only, because the call posts to the DeepSeek endpoint and a Claude pick would silently mute every instrument. Its usage is now tallied and its act_call event stamped under the model that actually ran — billing it to the panel model would hide exactly the saving the setting exists to make.
Chat auto-title
Models → The toolbox
flashWas a hardcoded constant. No behaviour change; it is here because a model id nobody can see is a bill nobody can move.

No reasoning switch on any of the three toolbox models, and that is not an omission: all three send thinking: disabled and cannot be made to reason from the console. The act call's was a measured decision (reasoning bought no accuracy on any of six decisions and cost 4.6× the wall clock). A switch that changes only the price is a lie.

The three kill switches

Owner-specified. Three layers, three owners — the app's two, then the chat's own — and they are ORed rather than folded into one server-side boolean, because each answers to somebody different: the console has to be able to show its two as they actually stand, and a chat's pill has to show the chat's.

SwitchWhoseWhat it shuts
5aTools available in every chat
Settings → Room tools · MAD_TOOLS
the app'sEverything, human and AI: the composer's tool button, both arm doors, the act call, the prop master, and the tool manual leaves the prompts.
5bShow the composer's tool button
Settings → Room tools
the app'sThe human door alone — the button goes and the human arm routes refuse, while the panel keeps arming exactly as it does. The inverse of 5a's spirit, for a room shape where the instruments are the hosts' to place and the guests only answer them.
5cTools
chat info, beside Mute
the chat'sThis conversation's own instruments. A pill in the chat-info actions row, third of four (Theme · Mute · Tools · Fork), mirrored in the ⋯ menu. Off → this chat is talk only, for everyone in it. Persisted in the room's state.json and audited like every other room setting; it rides SSE, so every member's open window follows it in the same frame.
the gate is the design5c is a property of the CHAT, not of a person — the owner's placement, 2026-07-29. That is what keeps「one member must not be able to end another member's game」true with no per-member arithmetic at all: it answers to the same gate the floor producer's toggle does — an admin anywhere · a private chat's owner · nobody else, and a live (moment) chat is system-owned so even its first speaker cannot flip it. There is nobody who can switch it off who was not already running the room. An earlier cut made it an account setting with a「chat-only once every human has opted out」rule; the pill replaces both the setting and the arithmetic.
what a switch may not doNothing new arms. Nothing already on a table is touched. Open cards stay visible and keep settling — by touch, by a deadline, by everyone having voted — and that falls out of where the refusal lives rather than from a special case: the human refusal is one line in room_tool_access(), which the arm routes ask and the settle routes (vote_tap · roll_tap · seal_put · gate_reveal · roll_go · gate_release) never do. The smoketest holds both halves to it.

The two doors, and the third prompt

Watching it, live

Two new cards on console → Status, both since-restart, in the exam's own vocabulary so the two are comparable:

9 · Standing rules for this build

The turn split · contracted 2026-07-26 from the scenario battery(18 scenarios · 27 runs)and the owner's design session the same day · the choreography(clarify before arming, then act, then speak)is the owner's · the form-compiles-to-a-tag decision keeps the mechanism untouched. Built, measured and repaired across 2026-07-26: the form · the act call · the gate (since deleted) · the FP · the decline-breaker · reasoning off (22.4s → 1.9s) · FP∥act concurrent · the hollow-seal guard · quiz-is-a-ballot · the separator fix · a form↔grammar lint. 18 of 19 scenarios arm, the control still arms nothing, no real leaks. 2026-07-28 — §8, many instruments in one beat(DoD sequence step 1): the first-form break dropped, the guard re-cut per family, the sealed-note redaction repaired. s20 3/3, the control unmoved. 2026-07-29 — THE PROP MASTER, a tool-call judge in front of the act callthe exam §6): the act call is issued tool_choice:"required", so its only way to say「nothing here」was to fill in the form with kind:"none" — it had to pick the object up to put it down, and the exam measured what that cost(the two restraint controls armed in 6 of 9 split runs). A cheap flash call with no tool schema now runs in FRONT, answers one question, and fails OPEN; on a NONE the act call does not run at all. FP ∥ act stays concurrent — it is a PREFIX, and the one verdict rides into both so they cannot disagree. Controls 3/9 → 12/12, recall unmoved or better, $0.00004 + 0.94s a turn. MAD_PROP_GATE=0 turns it off. 2026-07-29, later — §11, THE CONSOLE DIALS AND THE THREE KILL SWITCHES(ship plan session 3): the split, the prop master and three per-call models become live console settings(the split read at the drain, which makes the switch the rollback), plus 5a/5b/5c — a global tools-off, a human-door-only checkbox, and each person's own switch in Me → Preferences — under the owner's ruling that the console never changes a room's content. Two Status cards: cost per call type, and toolbox health in the exam's own vocabulary. Gate: 42 new smoketest checks · the split regression set at N=4 with tools ON · the three model-facing prompts proved byte-identical to the previous commit. Architecture: toolbox.html · lifecycle: tool-lifecycle.html · the FP: floor-producer.html.