The owner's state × trigger × setting matrix(2026-07-25)and the design session it produced. Every instrument turns out to have up to three phases, not two — and the axis that was missing is when the contents become visible, which is separate from when the collecting stops. This page is the design record and the work breakdown; toolbox.html keeps the architecture, toolbox-build.html tracks execution.
Three are shipped(T8's presence properties). Three are new — and the first of them, visibility, is the one that was hiding inside the「Open」column of the matrix: it isn't a trigger at all, it is who sees others' contributions while collecting.
| axis | values(default first) | status |
|---|---|---|
| visibility(while collecting) | all · contributors-only · none | NEW — today it is hard-coded per kind: roll=all, poll=contributors-only, deposit=none |
| close trigger | all-in · creator · any player · timer | partly — all-in/creator/any exist; close_at(timer)is unbuilt |
| reveal trigger | on close · creator · player(own row)· timer | built for seal only(T11)— reveal_at exists |
| blocking | none · panel · all | shipped(T8/v578) |
| place | inline · pinned | shipped(T8/v578) |
| unique | no · yes | shipped(T8) |
Why naming visibility matters: that single rung unifies three behaviours we built separately — the poll's reveal gate(contributors-only), the roll's public rows(all), the deposit's sealed rows(none). The middle rung is a general pay-to-see device — commit before you may look — that today only the poll enjoys and any tool could use. Your own contribution is always visible to you; that is a constant, not a setting.
visibility=all ⟹ reveal=on close. If everything is public as it lands there is nothing left to reveal; a reveal control on an open roll is meaningless(the v585 lesson formalized — the roll dropped its 🔒 precisely because it was public the whole way).roll and the face stays dice(keep the kind aligned with the mental model, or every future dice feature gets special-cased inside a kind called deposit). But do not mimic the deposit's state machine — promote it. Copying is how v586 happened(textGate() matched「any gate that is not a vote」, true until a third kind existed); two copies agree today and diverge at the first fix.CLOCK_SKEW already solved). Then the card is its own clock and binding buys nothing. The wall clock keeps its job(ruling ④ of toolbox §9): timing phases that aren't instruments, its ring a cue to the host. Display rule: show only the next deadline, labelled with what it does —「⏱ 4:32 后截止」then「⏱ 12:04 开封」. Deadlines are opt-in. 「The clock rings and everything open resolves」is a session concern(T5's orchestration), not a per-instrument dial.TOOL_ICONS/toolIcon()(7 keys, per-tool accents)everywhere a tool is named; tEmo() house glyphs(never OS emoji — the v556 rule)for states: 🔒 sealed · 🔓 revealed · 🔑. Both are house-owned, so a die and a lock are the same picture on every device.The matrix's「use case」column is the preset list — these six are what a person sees and picks; the axes are how they are configured underneath. That is ruling ⑩ of toolbox §9(three tiers)doing its job: adding axes must not add UI.
| preset | visibility | close | reveal | anon |
|---|---|---|---|---|
| Open Roll(掷骰) | all | all-in · creator · any · timer | on close(coerced) | n/a |
| Sealed Roll(密摇) | none | all-in · creator · any · timer | creator · player · timer · on close | n/a |
| Open Spin(转盘) | all | all-in · creator · any · timer | on close(coerced) | n/a |
| Sealed Spin(密摇) | none | all-in · creator · any · timer | creator · player · timer · on close | n/a |
| Poll(投票) | contributors-only | all-in · creator · any · timer | on close | yes |
| Deposit(密封收集) | none | all-in · creator · any · timer | on close · creator · writer · timer | yes |
| Deal · face down(暗发牌) | none(owner sees own) | no close stage | creator · player · timer | n/a |
| Deal · face up(明发牌) | all | no close stage | on deal | n/a |
Once reveal and close come apart, the card scrolls away while still owing someone an action. The reveal bubble(T11)tells the room after; nothing tells you before. That surface is the chip strip we already built(T-UI v569, ~35px under the topbar)— it just gains a second reason for a chip to exist: today a chip means you chose place=pinned(placement, by config); now it also means this instrument is waiting on something(state, automatic). If a card is both, they merge into ONE chip.
| pill | shown to | label |
|---|---|---|
| close / reveal countdown | everyone — a shared upcoming event | 「🎲 TITLE · 1:35 截止」/「· 1:35 开封」 |
| creator reveal | the creator only | 「🎲 TITLE · 开封」 |
| writer reveal(own row) | each person holding a sealed row | 「✉ TITLE · 开封」 |
Ordering: soonest clock first, then actionable-by-me. Cap the visible count with a +N overflow rather than wrapping — the 35px budget is what made the strip work. Ageing: clocks always earn a pill; a manual reveal earns one for a bounded window(the session, or a day), after which the card stays findable in the tools drawer as「my sealed cards」— otherwise a prediction sealed for a week is six days of permanent chrome. In the bubble: the countdown takes the place「Close now」occupies, but the early-close stays for whoever holds the valve — a timer must not remove agency the valve table grants.
Verified 2026-07-25 — the starting point for every task below.
self.gates)holds three kinds: vote · roll · seal(+ text, legacy replay only). Props are blocking · place · unique with the v578 coercions.trigger, revealed{}, reveal_at live on seal instances and are read through _seal_trigger(). The roll and the vote have no trigger and no visibility dial — their behaviour is hard-coded.close_at does not exist(0 references). Close-by-timer is in the matrix and unbuilt.self.deal, a single slot, riding the legacy roll_offer/dice_state() machinery with no props at all — exactly where the poll was before T8.TOOL_ICONS/toolIcon(key)(7 SVG keys, per-tool accents)where it fills a box, toolGlyph(key)/.tgl where it lands in running text — tiles, watermarks, band icons, the whole chip strip and every stream capsule. A state stays an emoji: TOOL_EMO is now lock · unlock · key and nothing else, so no tool can be drawn with an emoji anywhere(grep-provable). Task C builds on the swept strip: a pill wears the tool's own icon — toolGlyph() in a 24px box, the same picture as the card it points at(v617: the reveal pill's 🗝 was the one chip that disagreed with its card, and 「which card」 is a locator's whole job — keep the ACT in the accessible name); a state emoji beside one takes 20px, not 24(equal ink, unequal boxes). The strip still measures 35.7px at 375px. Two laws C must not break: a CTA button is pure TEXT(v617 — so a pill stays a locator and never grows a glyph-plus-verb button), and anything carrying a watermark is overflow:clip, never hidden — C's jump-to-card is exactly the scrollIntoView that shoved a card's own content up 45px when it was hidden. Traps L paid for, in the findings log: .tgl is 1.6em(the SVG ink is ~70% of its box)· a filled-accent button must invert the glyph, inline · knock-outs bind to what is behind them.Goal: ruling ⑪ made real — one picture per tool, one per state, everywhere.
tEmo() sites and split them: tool references(die · wheel · card · vote · board · clock · seal)→ toolIcon(key); state references(lock · unlock · key)→ stay tEmo() house glyphs.TOOL_EMO key that no longer has a caller; keep the state keys.Accept: no tool is drawn with an emoji anywhere(grep-provable); no state glyph is drawn with an OS character(the v556 rule holds); light/dark/e-ink all read; zero layout shift in the strip at 375px.
Goal: promote the three-phase machine out of seal and into every instrument, per ruling ⑧(extract, never copy). Ships one visible feature so it is testable: close-by-timer on the tools that exist today.
visibility(props)· close_trigger + close_at(NEW)· reveal_trigger + reveal_at + revealed{} — all promoted from seal-only to every kind. _seal_trigger() becomes the shared reader._maybe_close() / _maybe_reveal() called uniformly; one reveal route carrying the valve table(ruling ③); one timer arm/re-arm with the T1 traps verbatim(non-daemon guard · evict_guard membership · re-arm on load ≥1s fuse · a missed deadline fires once).visibility=all ⟹ reveal=on close)beside the v578 pair.revealBubble(); the countdown renderer(ruling ⑨)with the「next deadline only」rule.Accept: every existing poll/roll/deposit behaves bit-identically(re-run the T8/T9/T10 matrices); a poll and a roll each accept a close-timer and fire it, surviving a restart and a missed deadline; the mid-seal transcript proof; smoketest green.
Goal: rulings ⑩ and ⑫ — the sealed subtype, and dice/slots as separate doors.
visibility=none, close=all-in, reveal=creator(reveal later is the point — ruling ⑩). Reveal-on-close is available in Advanced for the simultaneity case.[3 + 4 = 7] format.wheel key — pick one word for the face(Slots or Wheel)and keep wheel internal.Accept: a sealed roll hides every face while collecting(wire-checked, not CSS-hidden); closes at all-in with the panel released to speak blind(ruling ①); the creator's reveal opens all rows + the reveal bubble + the cue; a sealed spin does the same for options; the open variants are unchanged; the two dialogs share one code path.
Goal: §6 — the chip strip gains automatic chips for instruments that owe someone an action.
place=pinned chip when both apply.CLOCK_SKEW); the「next deadline only」label rule.+N overflow rather than wrapping, and the ageing rule for manual reveals.Accept: a deferred-reveal card scrolled far out of view is findable in one tap; a creator-reveal pill is invisible to non-creators; two pending instruments order correctly and a third overflows; the strip still measures ~35px at 375px; no double chip for a pinned+pending card.
Goal: migrate self.deal onto the instance store and the lifecycle — the same move T8/T9/T10 made for the poll, roll and deposit.
kind="deal" on self.gates; the legacy self.deal + roll_offer path retires(legacy replay stays — old rooms must still render).Accept: a 6-card werewolf deal gives exactly the declared multiset; a devtools/wire read of another player's payload contains no card of mine; a face-up deal shows all hands at once; the creator's reveal opens everything with the reveal bubble; an old room's legacy deal capsules still replay.