Dialogue· The toolbox· The timer card
‹ the definition of done

The timer card

The one tool with no card gets one — but only at the ring. Setting a timer stays a capsule; time running out becomes a card, in the same grammar as every other tool bubble: band · setter · watermark. Every specimen below carries room-ui.html's own CSS, verbatim, in both themes.

shipped v652 Owner picked ①A · ②A · ③drop, and added a fourth: a human-set timer must wear the person's avatar and name, like every other card. That fourth is the reason this touched the server at all — _clock_fire read only by/by_slug off the clock, so a persona's face arrived and a human's did not.
One correction the build made to the mockup: the band says「Clock」/计时, not「Timer」. t("Clock") is the ONE WORD the drawer tile and the ◷ panel already use for this tool, and the v647 naming batch's rule is one door per concept — three surfaces naming the same object may not disagree. Every specimen below has been re-rendered with the shipped word.
The rule this follows: a move is a capsule, an outcome is a card. That is already true everywhere else — a poll opening is a card because you have to act on it, a die being thrown settles into a card, a deposit revealed is a card. The clock is the only tool whose outcome arrives as a one-line grey pill you scroll past. The ring is the moment the phase ends and the host must move — it is the loudest thing the clock ever does, and today it is the quietest thing on screen.

1 · What is there today

Three capsules, one builder (buildClockCap): set · cancelled · rang. They are the same object at three volumes.

light · today

叶文洁 set a clock · 5 min · 自由讨论
Time's up · 自由讨论

dark · today

叶文洁 set a clock · 5 min · 自由讨论
Time's up · 自由讨论

2 · The card

The second capsule becomes this. The first one does not change — setting a timer is a move, and a card for it would be a card nobody is asked to do anything with.

light · the ring

叶文洁 set a clock · 5 min · 自由讨论
Clock Time's up
set by 叶文洁
自由讨论
ran 5:00 · rang at 14:32

dark · the ring

叶文洁 set a clock · 5 min · 自由讨论
Clock Time's up
set by 叶文洁
自由讨论
ran 5:00 · rang at 14:32

What each part is, and where it comes from

partwhat it sayssource
band — icon + title + badgeTimer, and the state beside ittoolBand(), accent --clay — the timer's palette colour since v651
watermarkthe clock glyph, 150px, 10%, −18°toolWatermark("clock") — unchanged, one line
setteravatar + name in the seat colourgateOpener(). ⚠ the ring event does not carry by_uid today — a human setter would lose their face. Server fix required.
the label.gc-q「自由讨论」— what the phase was forthe sheet's What is it for? field, ≤30 chars, optional
the digits.gc-ranran 5:00 · rang at 14:32the ring event's sec + ts. One new CSS rule; everything above is existing classes.

3 · The three forks, and how they were settled

Each was a real fork. Both sides are still rendered — the losing option is kept as the record of what the choice was made against.

① The badge: does the ring want the eye?

AAccent fill + the arrival burst

The card wears .reveal: one glow burst as it lands, ✦ twinkling three times, then it settles. It is the revealed treatment — the same one a poll's result and a deposit's opening get. Why I lean here: the ring is an interrupt. The phase is over and the host has to move; a record that arrives silently is the problem this card exists to fix. And the burst is one-shot per card (v610), so scrollback stays quiet.

light

Clock Time's up
set by 叶文洁
自由讨论
ran 5:00 · rang at 14:32

dark

Clock Time's up
set by 叶文洁
自由讨论
ran 5:00 · rang at 14:32
BInert grey — a record, not an alarm

The closed badge: muted on --surface-2, no burst, no ✦. The argument for it: the ring already has three other channels — the top bar's ◷ flips to「Time's up」for six seconds, the panel gets a cue turn and speaks, and (installed) a push notification fires. A fourth shout in the stream is the fourth time you are told. This card would then be pure scrollback: what the phase was, who set it, how long it ran.

light

Clock Time's up
set by 叶文洁
自由讨论
ran 5:00 · rang at 14:32

dark

Clock Time's up
set by 叶文洁
自由讨论
ran 5:00 · rang at 14:32

② The title line: whose name goes in the band?

ABand = the tool. Label = the headline.

Exactly the poll's grammar: the band says Timer and nothing else (v610 — the title is the tool's NAME, the badge beside it says the state), and 「自由讨论」 sits where a poll's question sits, at 16px. Why: the strip chip, the band and the card must not disagree about which object this is, and「Timer」is the only string that is true for every timer.

BBand = the label. It is the address.

The DoD's address ruling says the title IS the address, and the strip already renders a clock chip with its label as its name (e.tool === "clock" ? e.title : …). Under B the band reads「自由讨论 · TIME'S UP」and the 16px line is dropped. Against it: a timer with no label — which is the default, the field is optional — leaves the band empty. And the timer is a singleton: there is only ever one, so it never needs an address to disambiguate it from a second one.

light · B, and B with no label

自由讨论 Time's up
set by 叶文洁
ran 5:00 · rang at 14:32
Time's up
set by 叶文洁
ran 5:00 · rang at 14:32

dark · B, and B with no label

自由讨论 Time's up
set by 叶文洁
ran 5:00 · rang at 14:32
Time's up
set by 叶文洁
ran 5:00 · rang at 14:32

③ A timer with no label — does the 16px line go dark?

The field is optional, so this is the common case, not the edge one. Left: the line is simply dropped and the card is three rows tall. Right: it falls back to「Time's up」— which then says it twice, once in the badge and once as the headline.

light · drop it (picked) vs repeat it

Clock Time's up
Y set by You
ran 10:00 · rang at 14:41
Clock Time's up
Y set by You
Time's up
ran 10:00 · rang at 14:41

dark · drop it (picked) vs repeat it

Clock Time's up
Y set by You
ran 10:00 · rang at 14:41
Clock Time's up
Y set by You
Time's up
ran 10:00 · rang at 14:41

4 · Every case the builder has to draw

Four setters × label / no label. These are the ones that exist — there is no「server-set」timer, because a clock is always set by somebody(the panel via <clock/>, or a human through the sheet).

light · the four

Clock Time's up
set by 叶文洁
自由讨论
ran 5:00 · rang at 14:32
Clock Time's up
M set by Mei
Everyone writes their bid
ran 0:45 · rang at 14:38
Clock Time's up
Y set by You
ran 10:00 · rang at 14:41
Clock Time's up
set by 狼人主持
天黑请闭眼,狼人行动,剩下的人不要睁眼看牌
ran 1:30 · rang at 14:44

dark · the four

Clock Time's up
set by 叶文洁
自由讨论
ran 5:00 · rang at 14:32
Clock Time's up
M set by Mei
Everyone writes their bid
ran 0:45 · rang at 14:38
Clock Time's up
Y set by You
ran 10:00 · rang at 14:41
Clock Time's up
set by 狼人主持
天黑请闭眼,狼人行动,剩下的人不要睁眼看牌
ran 1:30 · rang at 14:44

The fourth is a long label — the sheet caps the field at 30 characters, and anything past ~19 CJK glyphs takes a second line. It wraps rather than truncating, which is the poll question's behaviour and the right one here too: a phase name you cannot read is not a record. The card grows; nothing else moves.

5 · What it cost, as built

wherechangeas shipped
run_room.py · _clock_firethe clock_ring event and its SSE payload carry by_uid — they carried only by / by_slug, so a human setter reached the card with no face and no colour while a persona's was fine3 lines ✓
run_room.py · replaythe clock_ring branch passes by_uid through. A pre-v652 ring has none stored, and gateOpener degrades to the tool's own glyph with the name intact — never to a blank1 line ✓
room-ui.html · buildClockCapkind === "ring" returns a card (buildClockRingCard); set / clear keep the capsule. The card is toolWatermark + toolBand + gateOpener + the label + the digits — every one an existing builder~20 lines ✓
room-ui.html · renderClockCapbranches on which object it built: a card gets watchReveal(), a capsule keeps pulseIfVisible(…, "pulse2"). ⚠ pulse2 is a .joincap rule — leaving it on the card would have been a silent no-op and the card would have landed with no arrival cue at all2 lines ✓
room-ui.html · toolBanda new badgeTx — a badge-word override for any state, because the ring wears the revealed treatment while saying「Time's up」. A ring is not a reveal. (closedTx stays as it was; it is the closed-only case and has its own callers.)1 line ✓
CSS.gate-card .gc-ran — the one new rule3 lines ✓
i18ntwo new keys: ran {dur} · rang at {time} and its no-legible-duration fallback rang at {time}. Clock, Time's up and set by {name} were all already in the dict — which is the naming batch paying for itself2 keys ✓
not changedthe top bar's ◷ six-second「Time's up」beat, the panel's cue turn, the push notice, the set / cancelled capsules
the reasoning A · A · drop it — the burst, the tool name in the band, and no headline when there is no label. All three taken.
The one I would argue about is . The case for the quiet B is real: the ring already fires on three other channels, and a fourth shout is a fourth interrupt for one event. But those three are all transient — the ◷ beat lasts six seconds, the push is gone when you swipe it, and the panel's reply is a message that scrolls. The card is the only one that is still there when you come back to the room, and it is the one a person scrolling history reads to find out what ended the phase. Making the durable record the quiet one gets it backwards.
I would not argue about: the strip's clock chip carrying its label is a different surface with a different job (it identifies a running thing among other running things). A card names its tool, and the badge names its state. That is the law everywhere else.
The timer card · mocked and shipped 2026-07-29 (v652) · the toolbox's definition of done · the grammar: tool bubbles, every state · the accent: the tool palette · the tool: T1, the clock · the strip's six-second beat: the top bar. BUILT: ①A burst · ②A the tool's name in the band · ③ drop the headline when there is no label · ④ a human setter wears their own avatar and name. Verified against the shipped builders: eight payloads through the real buildClockCap — persona / human / no-label / pre-v652 / long label / no legible duration, plus both capsule controls, which stayed capsules.