跳到內容

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-sender seq guard stops a replay). Its plaintext is stopId, handle, mode (quit or interrupt) and reason (one line, at most 200 bytes: control, bidi and invisible characters become spaces). A 0.61 server lists unit-stop in its caps, and a controller sends a stop only to an unattended box that lists it; a 0.60 box lists neither it nor mains, and /boss link units and stop say 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 as unknown-kind, which the controller reports as “the relay does not take stop requests yet”.
  • unit-status gains an optional second field, mains, sent only to a controller whose caps list unit-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 of sha256(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 dispatch now, 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: quit is boss_close_main exactly; interrupt aborts the turn in progress first (subagents, monitors and open dialogs still refuse; an older main treats it as quit). Nothing kills a pane. The dispatcher and the team’s admins get main-stop-pending (once), main-stopped or main-stop-manual, naming the requester and the reason; link.log records ids and outcomes, never the reason.
  • An ally run closes only its own device’s mains. boss_close_main from 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 posts link/unit-stop. --interrupt counts 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-stop is in REMOTE_LINK). A box without unit-stop in 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 link screens (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 -w finds 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 a tower-event with 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 commands head-retry, head-deliver, and head-drop sit beside head-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 with answers, and the server marks the wait answered only once the tower took the message (tower-event and head-event now take answers and reply answered: 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 /quit or /new, as the page refuses them. Before, nothing in pi sent answers, 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>/input and sessions/<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 carries guarded: 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 open link_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 with confirmed.

  • d in the /boss gui lan screen (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. n still denies all.

  • /boss send against 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 confirms and link 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 posts link/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), and docs/how-it-works.md said the confirm was answered in the list.
  • /boss link asks and link 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’s remoteAnswer does ({choice}, {answers}, {cancel: true} with --cancel, only where the question takes one); with no text pi asks, one question at a time. A hereOnly confirm shows the question and the answer before link/dialog-answer is posted; the device re-checks device, digest, and level.
  • /boss link invite show|cancel <hash>, link join cancel, link leave <team> [--force] (G7). invite show draws the invite text on a screen of its own until Enter or Esc: not as a notice, not in the chat. leave asks first and takes the team by name or id; --force is the forked, stopped team’s (and the only attended admin’s) leave. link status now 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> and link join <invite | @file> (G3). link_request create|join only prefilled the page; these finish in pi. A hereOnly confirm names the relay, the folder, and the team an invite names. create asks for the relay’s token on a masked screen (src/secretscreen.ts): pi’s ctx.ui.input draws what is typed in clear, so it is not used. The token goes into the link/create body and nowhere else (no notice, dialog, session entry, or link_request argument), and a server refusal that echoes it is scrubbed. join posts link/join-start and link/join-project (the folder is this session’s unless --project says otherwise, and the confirm says so) and prints the four words for the inviter to pick. Use @file for 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 \x1b and \u202e escapes, 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. hereOnly kept the page’s relay from carrying a confirm to a paired device, but its key pad could still send Enter or y to the pane’s own dialog. While a /boss link screen waits (confirm, select, token screen, invite screen), and for link decide, gui rotate, gui unpair all, and server stop|restart, the pane now holds the server’s overlay mark (src/heremark.ts, the /boss gui lan overlay’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 cancel now 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 answer reaches the link path. Its text may have line breaks; boss.ts used 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).

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=chain has three units ask the Robot Hand select; &queued leaves 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).