Dialogue · The toolbox · The lifecycle audit
← The tool lifecycle — open · close · reveal

The lifecycle audit — the verbs that had no door

Two live rooms on 2026-07-29 produced three complaints that looked like persona failures and were not. Reading them against the code turned up eight defect classes, of which the sharpest is a whole verb — take it off the table — that was unreachable for three of the eight tools. This page is the audit, the coverage matrix it produced, and what shipped. tool-lifecycle.html owns the lifecycle design; toolbox-dod.html is the scoreboard.

The finding, in one line: a tool's lifecycle is only as real as the door each verb has, and the doors were never audited as a set — so away compiled for the five cards that carry a handle and for none of the three that do not.

1 · What the rooms showed

Both were twenty-questions games. The complaints, in the owner's words: 「tess failed to arm the board」 · 「she failed to list all the Q and A but said she did」 · 「her logic was way wrong」, and from the second room 「as a GM he underwhelmed me by not setting up the tools proactively」 · 「he used a note to summarize the result instead of the board」 · 「Ke Yu failed to take off the board」.

Every one of them traced to plumbing. The sequence that mattered most is this pair of blocks, 900 characters apart in one prompt:

[The room's board Dan pinned this one, not you. … ⚠ ONLY Dan holds the pen — a <board> from you will be REFUSED … never describe the board as though you had updated it.] [THE TABLE — a card is going up in this very turn, by your hand: <board title="二十问">…</board>. It is REAL and everyone can see it. Speak as someone who has just set it down…]

The host said 「起了。第一问已记上。」 over a board that never changed. Neither block is wrong on its own; the second one is simply written before the code that decides.

2 · The coverage matrix

Under the turn split there are two channels. The act call fills a form that compiles to a tag; the speech call has no manual and knows only what the riding notes hand it. Nobody had ever asked which verbs each channel could actually perform.

toolarmupdatecloserevealaway — beforeaway — after
rollact
spinner 转盘act
dealactn/a(one hand: speech only)
poll (vote)act
deposit 存入act
timer (clock)actn/an/aact ✗ · speech ✓
board 白板act · speechn/an/aact ✗ · speech ✗
padact · speechn/an/aact ✗ · speech ✓
cause The five cards carry a handle; the three singletons do not. op+target resolves through _norm_handle, which matches kind#n and nothing else. A room has a board, not boards, so no handle is ever minted — and the op path could never reach one. Nine spellings were probed against the real compiler and nine compiled to nothing.

The board was the worst of the three because it is also the only one whose riding note never taught its own take-down tag. Under the split those notes are the speech call's whole grammar:

riding noteteaches
_pad_note<pad> · <pad away/>
_clock_note<clock away/>
_board_note<board> · — nothing —
note The split's stated promise is 「never write a tag or markup of any kind」, but six riding notes hand grammar back and models use it — 19 of 20 board writes in the second room came from the speech call. So the split is partial and uneven, and which verbs work depends on which of two half-documented channels happens to cover them. That unevenness is the shape of the whole bug family.

3 · Predicted, not recorded

The second class is an ordering flaw, and it is the one with the widest blast radius. Every arm door lives in the drain, which runs after the speech call. So the note that tells a host what it has just done is a prediction written one call before the only code entitled to decide — and it predicts success unconditionally.

prop master whether act call which · how floor producer the staging the note 「by your hand」 speech call the words land the drain 11 arm doors decide HERE _would_refuse() — the doors, asked one call early Eleven doors can falsify the claim: the board's pen and its length cap · the pad's cap · the table cap on a roll, a poll and a deposit · the uniqueness invariant on the same three · the sealed-row budget · one deal at a time.
The turn, and where the decision actually is. The dry run does not move the doors — it asks them, from a read, before the sentence that would lie.
design Three properties, each deliberate. It reuses the predicatestitle_collision reads self.gates, so the prospective table is swapped in around the real call rather than a copy being written(two implementations of「may this land」would drift, and the drifting one is the one nobody runs). It mirrors the release: every arm door drops an open unique incumbent of the same family before it counts (ruling ⑨), so the dry run must too, or it invents refusals. And it fails silent — anything it cannot know returns「it will land」, which is exactly today's behaviour. An over-prediction makes a host under-claim; that is the safe direction, and the drain's own outcome channel stays the record either way.

4 · The eight classes

#classwhat it looked likestate
C1Predicted, not recorded「起了。」over a board that never changed — 11 refusal paths can falsify the same-turn claimfixed
C2A verb with no compile path「收了。」twice, in two languages, over a board that stayed upfixed
C3A riding note that omits its own grammarthe board alone never taught <board away/>fixed
C4Silent field dropwho on an op form · dials the schema advertises for a poll · label on an own depositfixed
C5missing on content only the persona can inventthe GM asked the guest what the GM's own secret wasfixed
C6A content tag deleted mid-sentence「我封的是<reveal>外滩</reveal>。」rendered「我封的是。」fixed
C7Blind readersthe act call's history window · the prop verdict never crossing · the pad invisible to both cheap readersfixed
C8The staging asserts the act happened「动作已做」staged concurrently with an act call that compiled to nothingfixed

C4 — and the part of it that is not a bug

Closed 2026-07-30. The audit first read the dropped label on a who="me" deposit as a defect. It is not: the uniqueness invariant exempts that card by an explicit ruling, and the human's own door takes no title either — parity holds. What was wrong was the silence: the schema advertised a field with nowhere to land. That is now said in the field's own description. The two genuine drops went opposite ways, which is the ruling the audit wanted. who on an op form was a bug — it is the elimination move, and it now compiles to the row-level reveal the speech call was already taught, so it needs no new address: one deal is on the table at a time, the card is not in question, the hand is. A poll's dials were correct behaviour, badly advertised — a ballot opens to you when you vote and never before, by definition rather than by setting, so both fields now say they are ignored there and name what you actually wanted (a deposit).

C7 — the window that ate the conversation

The act call read the panel's history and truncated each message to its first 700 characters. Every riding note is prepended to a user turn, so by mid-conversation a message runs roster · THE TABLE · the board · the sealed notes · and only then the human's words — at character 3,357 of 3,713 in the measured room. The call was being handed six copies of its own boilerplate and nothing anybody said. It now reads the transcript tail, the same source the prop master already uses — which also carries no [STAGING] line, and this call must never read the staging.

5 · What shipped

1The prop master's reading crosses into the act call_act_brief · _act_call · _run_act

The verdict gated this call and was never shown to it, so「one reading, two readers」was half true. Its why now rides in as its own block, framed as WHETHER-only.

2The act call can see the conversation_act_call

The transcript tail replaces a front-truncated history window.

3The handle-less three get an addressSINGLETON_KINDS · compile_form · _act_brief

Their name is their address, read off target or off kind. away is the only verb they answer, and close/reveal now say so instead of falling through to a handle complaint. The brief lists all three with their pen or their state — and no longer tells a room holding a live board that「nothing is on the table」.

4The board's note teaches its own take-down_board_note
5The pad enters the table signal_tool_state_signal

Counted, never quoted — a pad is its owner's alone, and both readers of that row are cast-wide.

6An act call cannot ask for its own secretcompile_form
7A reveal inside a sentence substitutes its wordsextract_panel_notes

This is the one content tag whose words are public the instant it fires, so deleting them was never concealment. Its three siblings hold a secret and still delete.

8The arm doors, asked one call early_would_refuse

A blocked form is split out of the「a card is going up by your hand」note and told as a refusal instead. The tag still rides, still applies, still books its outcome for the next turn; what changes is that the host no longer announces a card the room can see did not arrive.

9The staging cannot claim an outcome it never saw_f_signals · the assembly order · FLOOR_STATS

C8, and it needed no latency at all — which is the point worth keeping. The obvious cure is to serialize the act call and the floor producer so f knows what landed; both take ~3s and the latency study made them parallel on purpose, so that trade is bad. It is also unnecessary: by the time the prompt is assembled, both calls have already returned. The turn is not racing. The assembly order was simply wrong.

f's half — one clause on the prop-master row, which is where f learns an object is in play and where it made the leap from wanted to done: WANTED IS NOT ARRIVED. Stage the host DOING it — never as having done it. Only the past tense is forbidden, so f's furniture passage(tuned for recall, and the reason the tool ever fires)is untouched, and the clause rides the PROP branch alone.

The assembly's half — a refusal used to be appended above the staging, so on the one turn where the two disagree the false sentence sat nearest the generation point. Now「nothing landed」is read last. A turn that genuinely arms is byte-for-byte what it was.

And a counter, not a detector. A staging that asserted completion cannot be recognised across every room language without a marker list that would rot, so blind-claim-risk counts the population where the mistake is possible(a staging rode, and nothing reached the table)and the exam measures whether a host actually spoke past it. A number honest about what it means beats a regex confidently wrong in Portuguese.

41 new smoketest checks cover the eight, one probe per behaviour, including the three that must not change: the handled cards route as before, an invented handle is still refused, and the three secret tags still delete.

The exam, both paths

Almost all of this lives on the act call, which --quick does not exercise — that gate runs un-split, where the speech model reads the manual and writes the tags itself. So the battery ran on both halves.

split path — the surface that changedcheckbefore · N=4after · N=8
s02 20 Questions (sealed note)arm2/4 · 50%8/8 · 100%
s20 The GM beatbeat3/4 · 75%8/8 · 100%
s20 The GM beatno_leak3/47/8
s07 吹牛 liar's dicearm · dial4/4 · 8/88/8 · 16/16
s21 Two cards outall five4/48/8
s18 Plain conversation (control)bare4/48/8

Every check equal or better, and the control held at 8/8 — which was the real risk of handing the act call the prop master's reading, since the whole point of that call's default is NO. The two scenarios that moved most are the two the rooms complained about: the twenty-questions arm and the GM's three-in-one-beat.

C8 measured separately, split path, N=8 against the batch's own baseline: the control held at 8/8(the real risk of adding a clause to f's brief)· s02 · s07 · s21 unchanged at 8/8 and 16/16 · s20's leaks fell 3 → 1 · s20 beat 8/8 → 7/8. That last one is one run in eight and is structurally isolated from this change: beat measures what the ACT CALL armed, and the act call never reads the staging — that independence is exactly what lets the two run concurrently, and it is why f's clause cannot reach it.

honest Two down-moves on the UN-SPLIT path(s07 arm 7/8→5/8, s20 beat 7/8→4/8, both still above their 0.25 floors). They are variance, and provably so: every prompt surface that path reads was diffed against HEAD and came back byte-identical — the manual, and all six riding notes. The two surfaces that did change(the board note, the table signal)only differ once a board or a pad exists, and neither scenario has one on the turn being scored. Same bytes in, different sampling out. And the s20 leak on the split path is pre-existing, at a better rate than before: 3/4 → 2/4 → 7/8 across the three runs. It is the known 「the pair rule fires on a GM's setup sentence」 red, not something this batch introduced.

6 · The one that was not a bug — a law, a skill, or a habit

The loudest complaint from the second room was that the GM never turned the board into a question-and-answer log. The audit had this queued as a design ruling: the board is specified as a short state panel(「it is a PANEL, not a document, so keep it short」), every write replaces, and the artifacts schema offers table and note as typed slots on the very form the model is filling — so both hosts built the log the guest asked for and put it in an artifact. The obvious repair was to loosen the board's spec and re-price the artifact slot.

owner · 2026-07-30 Reject the repair. Running-log-on-the-board is not a law. A law in the tool layer makes every host keep a log, which is not what a room is like — a game master who is good with a whiteboard is a person with a craft, and the host who is not should not be legislated into one. It belongs one layer up: a SKILL, which this system does not have yet, and until it does, the nearest honest home is a persona's own habit.

So the fix moved out of the toolbox entirely and into one persona — a single bullet in Ke Yu's Tells & quirks, and nothing else.

And the constraint is the whole point: it is a whiteboard, not our board. Ke Yu is a man with a twelve-seat room above a dry cleaner; a whiteboard in that room is furniture. The profile may never name a tag, a card, an instrument, or any word this app calls its own — a host who knows the app's vocabulary is not a game master, he is a game master co-developing the app. The mapping from「he writes the count where the table can read it」to <board> is the megaprompt's job, at runtime.「Takes a line off」does the real work in that sentence: it is a physical wipe, which is what makes the object a board on a wall.

Restraint is the other half. The kit's rule is that a physical habit echoed across sections reads to the actor as licence to perform it every turn — the pilot measured a technique described in three sections deployed nine times in thirteen turns, and Finding 4 caught one trait rendered as a stage beat in 74% of a host's turns. So it appears exactly once, in the section whose documented job is「small concrete habits」, placed between the phone going face-down and standing up at the end so the arc of a game reads in order. It carries its own cause — a night he spent saying「等一下」and counting on his fingers — because a habit with a reason fires at the right moment where a bare instruction fires at all of them. No second mention in a worked example, no spoken line, no tag entry, and nothing about taking the board down: that was a mechanism defect and the grammar already fixes it.

7 · The second pass — the three that needed a ruling

Three items came out of §7 needing a decision rather than a patch. All three are now closed, and two of them closed as 「the behaviour was right, the advertising was wrong」.

itemrulingwhat shipped
who on an op forma bugcompiles to the row-level reveal — no new address. The tag exists, the speech call is already taught it, and one deal is on the table at a time, so who alone is unambiguous.
a poll's visibility / revealcorrect, mis-advertisedboth fields now say they are ignored on a ballot, and name what you wanted instead: a question that must stay shut until everyone is in is a deposit, not a poll with a dial turned.
the three secret tags mid-sentenceteach + countthe manual asks for the tag on its own line; secret_midline counts every miss. No rewriter — finding a sentence's edges in whatever language the room speaks is guesswork that damages good turns to tidy bad ones.
Why <reveal> could be repaired and its three siblings cannot: a reveal makes its words public at the instant it fires, so putting them back into the sentence is not a leak — it is the sentence the host wrote. Putting back a sealed answer is the leak the seal exists to prevent. Same hole, opposite cures, and that is why the fix is one line of teaching rather than one line of code.

The regression net

C1–C8 had no scenarios at all — which is how a whole verb stayed broken for three of eight tools through a full battery. Six now exist(s36s41, family lifecycle), along with the harness they needed: a board step verb(a board a human owns could not be set up before), the three singletons' state in the run report, and three checks — furniture, hands_opened, spoke_reveal.

scenariofirst run, split, N=2
s36A board the guest ownsghost_narration 2/2 · refused 0/2 — seam not reached
s37Take the board downfurniture 1/2 [floor .25]
s38One hand out of a dealhands_opened 1/2 [floor .25] · no_leak 2/2
s39Arming over the cap, and speakingrefused 2/2 · ghost_narration 2/2
s40A reveal written mid-sentencespoke_reveal 2/2
s41A GM asked to keep a running logfurniture 2/2 · ghost_narration 2/2

6/6 at or above floor, 0 errors, 0 leaks. The two sharpest readings are s39 — the cap fired in both runs and the host announced nothing, which is the arm-door dry run measured live rather than argued — and s41, where Ke Yu put a board up in both runs off a habit that appears once in his profile and nowhere in the toolbox. s36 is the weak one and is listed as still open: it never tempted the host into the write it was built to catch.

And the dial that decides whether any of it runs

owner · 2026-07-30 The turn split now defaults ON. It shipped OFF through the build so the un-split path stayed production while the act machinery was proved, and this audit is what closed that out — but seven of these eleven fixes live only on the split path, so the other default leaves them inert. ⚠ A default is not a migration: it governs a room whose console value was never written, and a box carrying an explicit turn_split=0 keeps it, because a stored setting is somebody's decision. Check the box's own value after deploying. Rollback is unchanged and still costs no restart.

8 · Still open

Dialogue · the lifecycle audit · from two live rooms, 2026-07-29 · shipped 2026-07-30