0.61.0
An admin stops one main on a shared box (the box’s side, the wire, and the relay)
Section titled “An admin stops one main on a shared box (the box’s side, the wire, and the relay)”The box half of the remote stop (spec, todo #100), which the controller’s side below drives.
- A new Link kind,
unit-stop(attended sender, addressed to one box, queued: the per-senderseqguard stops a replay). Its plaintext isstopId,handle,mode(quitorinterrupt) andreason(one line, at most 200 bytes: control, bidi and invisible characters become spaces). A 0.61 server listsunit-stopin itscaps, and a controller sends a stop only to an unattended box that lists it; a 0.60 box lists neither it normains, and/boss link unitsandstopsay it needs an upgrade. The relay must know the kind: straw-relay 0.2.3 (deployed at relay.moldplan.app on 2026-11-07) does; an older relay refuses it asunknown-kind, which the controller reports as “the relay does not take stop requests yet”. unit-statusgains an optional second field,mains, sent only to a controller whose caps listunit-status.mains, and filled per controller: the dispatcher sees its own mains, a team admin every main of the team. A main is named by a handle (the first 8 bytes ofsha256(box device id ‖ session id)), so no session id leaves the box, and a main restarted under the same name gets a new handle.- The box decides. The holder checks that the box is unattended, the sender attended and at
dispatchnow, the request no older than 15 minutes, and the mode known. The server then needs the handle to match exactly one main an ally run opened for the envelope’s team, the sender to be its dispatcher or a roster admin of that team, and the pane token not to name another device. A refusal writes and spends nothing and is told to the sender (a not-recipient refusal to the requester’s scope, so the team’s admins see the attempt). - The revoker carries it out. An accepted stop is written on the route row (it survives a server restart) and swept at once:
quitisboss_close_mainexactly;interruptaborts the turn in progress first (subagents, monitors and open dialogs still refuse; an older main treats it asquit). Nothing kills a pane. The dispatcher and the team’s admins getmain-stop-pending(once),main-stoppedormain-stop-manual, naming the requester and the reason;link.logrecords ids and outcomes, never the reason. - An ally run closes only its own device’s mains.
boss_close_mainfrom an ally run is refused before the gate, with one message and no Allow spent, for a main that is not routed to the run’s device, a main no ally route holds, an ambiguous name, or a pane whose token names another device (decision 7). Before, a dev’s ally run could ask a shared box to close another dev’s main.
Wire: new kind unit-stop, optional mains field of unit-status, caps features unit-stop and unit-status.mains. Needs straw-relay 0.2.3.
A tower’s ask reaches its reply again (todo #119)
Section titled “A tower’s ask reaches its reply again (todo #119)”A reply to an ask a workspace tower made for a main is stored with the Herdr id of the workspace the ask came from, but delivery and the tower opener matched workspace labels only, so such replies waited in the inbox (“no tower is open on …”) until they were purged after 7 days. Both now take a workspace id as well as a label; an id names its workspace before any label does, so a label equal to another workspace’s id cannot pull a message there. Replies already waiting are delivered on the next drain after the upgrade.
An admin stops one main on a shared box (the controller’s side)
Section titled “An admin stops one main on a shared box (the controller’s side)”A controller that is an admin of a team can ask an unattended box to stop one of its mains (spec). The box’s side (the unit-stop kind, the authority check, the mains of unit-status, the interrupt mode) came first; this is what the boss sees and types. The box decides and tells the result in a notice.
/boss link units [box]lists the units each box reported and, under each, the mains this computer may stop: name, state, who dispatched it (when it was not this computer), and the first six characters of its handle. A box that takes no stop says so./boss link stop <box> <main | handle start> [--interrupt] [reason]resolves the main by name (any case) or the start of its handle, asks a confirm on this computer only (hereOnly) that names the box (label and short id), the main, its unit, its dispatcher and the mode, and postslink/unit-stop.--interruptcounts as a whole word anywhere after the main. A reason over 200 bytes is refused before the confirm. A box in two teams is read from its freshest status row, as the page does. Both commands have Tab completion.- The Mains fold on an ally card. Each unit’s fold lists its mains with a Stop button (44 px each way on a phone). The sheet has Cancel (first, focused), Stop (quit when idle) and Interrupt and stop, which is drawn heavier (a solid fill and a 2 px border) because it aborts the turn in progress. The row then reads stopping… until the box says so. A paired phone may stop (
link/unit-stopis inREMOTE_LINK). A box withoutunit-stopin its caps, a stale status, or a main already stopping shows no button. - What a teammate wrote is shown safely. Labels, names, units, states and handles in
link units, the candidate lists, the stop confirm and the sent note go through the same escaping as the other/boss linkscreens (src/safetext.ts: control, bidi and invisible characters as\x1b,\u202e; a line break as\n), so a box cannot repaint the confirm the boss approves or write a terminal sequence such as OSC 52.
Wire: link/unit-stop {team, device, handle, mode, reason} is a new control route (a paired device may post it). Nothing else changes for this side.
pi first: the tower, the head, a wait’s answer, a session’s input, one Wi-Fi denial
Section titled “pi first: the tower, the head, a wait’s answer, a session’s input, one Wi-Fi denial”The GUI-only audit (todo #34, gui-only-audit.md, gaps G4, G5, G6, G8, G9) found six things the page did that pi could not. Each is now a /boss subcommand that posts to the route or control command the page uses, so the server keeps every check; each asks in pi where it acts on something the boss did not name before, is refused in a tower run or an ally run, and is no tool.
-
-w <workspace id | label | head>on/boss tower(G4).status,parked,retry,deliver <id>,drop <id>,leave,new, and a message now reach another workspace’s tower. The workspace is found as/boss memory -wfinds it (a shared label is refused with the ids; the head’s own Herdr workspace is the head). A write at another tower asks first and names it; a message is atower-eventwith no opening and no focus change. Before, only the workspace the typing session stood in could be reached. -
/boss head retry | parked | deliver <id> | drop <id>(G5). A held head tower (a crash loop) had a Retry button on its page and nothing in pi. New control commandshead-retry,head-deliver, andhead-dropsit besidehead-open,head-event, and the rest; a run token is refused them. This is the canonical spelling;/boss tower -w head …sends the same commands, after a confirm that says “the head tower”. -
--answers W3(G9). On/boss tower [-w]and/boss head: the message goes withanswers, and the server marks the wait answered only once the tower took the message (tower-eventandhead-eventnow takeanswersand replyanswered: the id, or false when the memory no longer has the wait). A bad id is refused before anything is queued, and so is exactly/quitor/new, as the page refuses them. Before, nothing in pi sentanswers, so the wait never showed as answered. -
/boss send <name|key> [--after] <text>and/boss stop <name|key>(G6). The composer’s Send, After this, and Stop, typed:sessions/<key>/inputandsessions/<key>/keys {keys: ["esc"]}. A name that fits more than one session lists them (a name with spaces is quoted); this session itself is refused; Stop asks first, as the page does. The post carriesguarded: true, so the server holds it to the guards a paired device’s own write meets (a local-only command, the Wi-Fi screen’s mark, an openlink_request) though it comes from this computer, and to the usual ones (a blocked dialog, the 16 KB limit); a command that ends or replaces the session asks in pi before it is sent again withconfirmed. -
din the/boss gui lanscreen (G8). Denies one pending Wi-Fi request (lan/approve {deny: id}): the only one, or the one marked with Up and Down when several wait; it says “Denied” only when the server denied one.nstill denies all. -
/boss sendagainst an older server, and its refusals. A send is refused (nothing posted) when the sb server’s recorded version predates 0.61.0, which would type it as a plain local write; the message names the version and says/boss server restart. A guarded post from this computer that a guard refuses now says so in terms of a /boss send, not “from a paired device”.
Wire: sessions/<key>/input takes an optional guarded: true; tower-event and head-event take an optional answers (W<n>) and answer answered; head-retry, head-deliver {id}, and head-drop {id} are new control commands. Nothing else changes.
pi first: confirms, forwarded questions, invites, create and join
Section titled “pi first: confirms, forwarded questions, invites, create and join”The GUI-only audit (todo #34, gui-only-audit.md, gaps G1, G2, G7, G3) found Straw Link operations that only the page could finish. Each is now a /boss link subcommand (src/bosslink.ts) that posts to the route the page posts to, so the holder keeps every check; each asks in pi, on this computer only (hereOnly), where it changes who may command this computer or answers for it. They are the boss’s typed input: a tower run or an ally run takes none of them, no tool reaches them, and a paired device’s composer cannot type them (BOSS_LINK_LOCAL, a slip guard like link decide’s).
/boss link confirmsandlink confirm <id> allow|deny(G1). The list shows each waiting confirm’s short id, who asked, the team, the tool, its age, and the arguments in full. An allow asks first with the whole call and postslink/confirm-answer {dialogId, digest, allow}with the digest of the request as it was read and shown, so a request replaced meanwhile is refused by the holder; a deny asks nothing. Before, the only notice in pi was one line (holder.ts), anddocs/how-it-works.mdsaid the confirm was answered in the list./boss link asksandlink answer <id> <choice number | text>(G2). A forwarded question from an unattended device is listed with its choices numbered. The answer is built as the page’sremoteAnswerdoes ({choice},{answers},{cancel: true}with--cancel, only where the question takes one); with no text pi asks, one question at a time. AhereOnlyconfirm shows the question and the answer beforelink/dialog-answeris posted; the device re-checks device, digest, and level./boss link invite show|cancel <hash>,link join cancel,link leave <team> [--force](G7).invite showdraws the invite text on a screen of its own until Enter or Esc: not as a notice, not in the chat.leaveasks first and takes the team by name or id;--forceis the forked, stopped team’s (and the only attended admin’s) leave.link statusnow also lists the open invites, the join in progress with its four words, and how many confirms and questions wait./boss link create <relay> <dir>andlink join <invite | @file>(G3).link_request create|joinonly prefilled the page; these finish in pi. AhereOnlyconfirm names the relay, the folder, and the team an invite names.createasks for the relay’s token on a masked screen (src/secretscreen.ts): pi’sctx.ui.inputdraws what is typed in clear, so it is not used. The token goes into thelink/createbody and nowhere else (no notice, dialog, session entry, orlink_requestargument), and a server refusal that echoes it is scrubbed.joinpostslink/join-startandlink/join-project(the folder is this session’s unless--projectsays otherwise, and the confirm says so) and prints the four words for the inviter to pick. Use@filefor an invite rather than typing it into the command line.- What a teammate’s device wrote is shown safely. Arguments, questions, labels, and dialog ids appear with control, bidi, and invisible characters (U+061C, U+180E, U+FFF9 to U+FFFB included) as
\x1band\u202eescapes, so they cannot repaint what the boss approves. A field that is one line of a screen (label, tool, team, id) also shows its line breaks as\n, so a teammate cannot lay out the allow confirm; the arguments keep their lines, set in from the question. - On this computer only holds in the terminal too.
hereOnlykept the page’s relay from carrying a confirm to a paired device, but its key pad could still send Enter oryto the pane’s own dialog. While a/boss linkscreen waits (confirm, select, token screen, invite screen), and forlink decide,gui rotate,gui unpair all, andserver stop|restart, the pane now holds the server’s overlay mark (src/heremark.ts, the/boss gui lanoverlay’s own): a paired device’s key pad, composer, and take-backs send Escape only to it, and the mark is dropped on every way out (Enter, Esc, an error, a throw). A pane the server cannot mark refuses the screen and asks nothing; a GUI that is not running takes no paired keys, so the screen opens as before.link invite cancelnow asks first (it had no screen to hold). - An id names one request. An id the boss types is an exact key or the start of an id (four or more characters); an id that is exactly one request’s and also the start of another’s is ambiguous, so nothing is done and the candidates are listed in full. A listing shows more than eight characters where eight name two requests.
- A multi-line
/boss link answerreaches the link path. Its text may have line breaks;boss.tsused to let such a line fall through and make the session the tab’s main agent.
Known limits: a secondary slot’s sb server answers the mark with an error, so /boss link screens, gui rotate, gui unpair all, and server stop|restart refuse there (paired keys cannot be held to Escape) rather than ask where a paired device could answer. A terminal tile of this pane that a paired device has open shows these screens as it shows any pi screen (the /boss gui remote QR is the same). A pi with no ctx.ui.custom (print and RPC modes) refuses create and invite show rather than echo.
Wire: none. control.ts exports localOnlyText (tested directly).
Zen: the next card follows your answer
Section titled “Zen: the next card follows your answer”Boss ruling 2026-11-07 (Z-D22): 「如果有多張卡片 user 點了第一次 enter 以後,回答完不用再回到呼吸」. In 0.60.4 every card ended in its outcome, the 3 s pause, and the breath, and the next queued card needed another Enter on the cue. Now, once you have taken a card and your own answer to it is accepted (an answer or a cancel), the outcome shows 1.2 s as before and the next card that was already waiting at that moment, blocking questions first, takes the screen at once: the bowl, the focus on its main control, no pause and no breath. The cue’s count drops by one for each card; with none left the pause and the breath come as before.
- Only your own accepted answer chains. Maybe later (Esc), closing a card whose question ended elsewhere (Back to the breath), and an empty queue still return to the breath, with the cards that wait counted in the cue. A card that resolved while it queued is never drawn. A card that arrives after your answer is accepted (during the 1.2 s outcome) waits for Enter (boss ruling 2026-11-07: 「只接你送出答案當下已在排的卡」). The unit’s own next question under the card you just answered waits for Enter. No chain on a page that is hidden or in front without the focus, while the terminal layer, the palette, or a menu is open over the card (the pause runs under it), or into a visit to Zen that begins after the answer (you left within the outcome, or the page was hidden and shown while Standard had it): every visit starts at the breath.
- The key that answered cannot press the next card. For 400 ms after it is drawn the chained card ignores any Enter, any tap (a click you made, or a press that began in the window), and Esc (a cancel is an answer); it ignores a repeating Enter and a keydown that began before it was drawn for as long as it is on the screen. A fresh key or tap after the 400 ms is the card’s own, and a press inside the window that never became a click (dragged off, cancelled, or followed by a key) does not hold back the next real click. The window is the key pad confirm’s, 400 ms. A card taken with Enter or the cue is not guarded.
- Sound. The answer’s knock, then the next card’s bowl as for any taken card; no second knock. A card the page already told a hidden window of while it queued is not told of again.
- Demo.
?zen=chainhas three units ask the Robot Hand select;&queuedleaves them waiting with the cue counting them.
Standard and Geek are unchanged. Wire: none. Design: docs/specs/2026-11-01-zen-view.md, The next card follows the boss’s answer (Z-D22).