0.60.2
The ally base’s card says what its units are doing
Section titled “The ally base’s card says what its units are doing”Boss, on seeing the report of a Straw Link box: “I want that report to be better to look at.” The card of an unattended device’s base (Standard and Geek; Zen has no such card) was the unit’s name, “working · 2 monitors running”, and the report as raw text, with the box’s own tabs listed above it.
- A state word and a time. Each unit says standing by, running, or crashed (待命, 執行中, 當掉) in a colour of its own, and “updated 40s ago” (the page’s clock keeps it current). A device whose card names no workspace says each unit’s name; otherwise the title is the unit.
- The report is Markdown, folded. The last report shows its first line; a tap opens it as Markdown (headings, lists, code, bold), about 24 lines tall at most, scrolling inside. The Markdown is built when it first opens, so a computer with no such device loads none of it.
- Monitors on one line. “2 monitors” and their commands, cut with an ellipsis; the whole list is the line’s tooltip.
- A crash says so. A unit reads crashed while a crash notice for it is under 30 minutes old and it is not running, and the page has not seen it run since: a unit the boss re-ran and that stands by again no longer reads crashed. The notice’s text sits behind a fold (“What happened”, with its age) instead of in a notice you must clear. An older crash stays in the Link notices.
- A trust question is not a crash. A box that waited ten minutes on a project-trust prompt nobody can answer reads needs trust (待信任) in amber, with the fold “Why it stopped”, the notice and a line saying to add the project to the box’s
trust.json. - Held and closing. A unit the box holds after repeated crashes reads held after crashes (當掉後暫停) in the crash colour; a closing one is said in words, not as the box sends it.
- Stale and gone. Three minutes without a status dims the card in Standard and adds a “no status lately” pill with how long ago the last one came; after thirty minutes the units are gone.
- Not an unattended box’s own mains. The card of an unattended box never lists the tabs the box shares in its presence. While the box reports no units (an older box, a team that shares presence only, the minute after a restart) one line says “3 units online” (3 個單位在線). The subtitle names the box and whether it is there; the units carry the state. A teammate’s own computer (Mei’s Mac) lists its mains and workers as before: the controller’s summary marks an unattended box’s bases (
unattendedonAllyBase, from the roster). - The folded line keeps your characters. The report’s first line takes off only Markdown marks that are markup (
**bold**,_em_,~~gone~~, code ticks);ledger.rounding_mode,~/projects/app/__init__.py,2 * 3andMP_4219stay as written. - Phone. Each fold is a 44 px row. A press on a fold in a row that waits on a question opens the fold and not the question panel.
- Demo.
?ally=idle|crashed|stale|long|gone|unitson the demo’s ci-box base.
Wire: none. The holder’s summary now carries when it took a unit status (DeviceUnits.at) and each notice’s time (data.at), and marks an unattended box’s bases (AllyBase.unattended, from the roster the holder already has); no message changed.
The drawer’s Quit button and the current-row chevron
Section titled “The drawer’s Quit button and the current-row chevron”- Quit no longer sits under the
›. In the phone’s all-units drawer, the row of the unit you are on draws a chevron at its right edge; on an idle main the card also holds a Quit button (關掉 in zh-TW), and the chevron was drawn on top of its label. The current row now keeps the chevron’s strip clear of the button (the card’s right padding is 30 px there, only while a Quit button shows), in English and Traditional Chinese, Standard and Geek. The button keeps its 44 px tap target; no other row or page changes.
Zen: the head tower’s strip starts at rest
Section titled “Zen: the head tower’s strip starts at rest”Boss ruling 2026-11-06, on seeing three of the head tower’s last lines under the breathing circle the moment Zen opened: the head tower is the main tower, so Zen should default to the breath, and the conversation should show when the input is clicked.
- Every visit starts at the breath. On a page load and on every visit, Zen shows the breathing circle, its line on mindfulness, and the folded input, with none of the head’s lines (they were the last three, and “on it…” while it worked).
- You call the lines up. A click or tap on the input, a focus on it, Enter, or H brings the head’s last lines up, clear for 20 seconds from then (the first H; the second opens the whole conversation, and the first H with no lines opens it at once). Esc, a tap on the blank space, or the focus leaving the input (an empty input that loses it) rests the strip again. Closing the whole conversation (Esc, H, Back to the breath, a gesture down) returns to the rest, not to the lines.
- What still comes by itself. A reply to a message you sent in this visit. A wait or question for you does not: it is a card, counted in the line above the input (below), and Enter calls it up. The head’s own lines (a tower’s answer, a report it starts itself) never wake the strip, and what the page already held when the visit began does not either.
- Unchanged. Standard and Geek; the fade of a line; the history; the palette entries; the keys.
- Demo.
?zen=headshows the rest,?zen=head-openthe lines and the input,?zen=head-historypresses H twice;?zen=head-restno longer presses Esc.
Design: docs/specs/2026-11-04-head-tower.md, Rest is the default.
Zen: cards wait for Enter
Section titled “Zen: cards wait for Enter”Boss decision 2026-11-06 (Z-D19), the same principle one step on: 「如果 user 還在呼吸,下一張卡片也可以先 hold on,除非 user enter 願意去接卡片」, and for every kind of card, 「全部都等 Enter」.
- No card takes the screen by itself while Zen rests on the breathing circle: a unit’s dialog, a trust question, a question a box forwarded, a teammate’s confirm, a tower’s wait, a pair request, a proposal. They queue, blocking questions first, and make no sound and no motion.
- One quiet line above the input says how many wait: 「1 張卡等 Enter。不急,等你準備好。」 / “1 card waits for Enter. No rush, when you’re ready.” (Zen’s one count; the head tower’s own waits are counted in it and no longer wake the strip). Enter, or a tap on the line, takes the next card. When you answer or put it aside, Zen breathes again and the next card waits for Enter. While a card waits, the input’s hint says to click, since Enter takes the card first.
- A card on the screen is never interrupted. Z-D18’s preemption (a blocking question taking the screen from an untouched wait, the 20 s touched window, the parked card, the 500 ms look, and
gui/zentouch.ts) is gone. With a session for company a card still takes the screen by itself. - A page nobody is looking at (hidden, or in front without the focus) gets one system notification for each card, once for as long as the server lists it: a card that was on the screen, put aside, or left behind a hidden page does not get a second one.
- Nothing moves when a card comes or goes. The input’s strip keeps two rows under the lines, the link’s and the cue’s, in every state; the circle gives way (down to 80 px) when the circle, its line, and the strip are taller than the page, and starts under the Working · Idle line at the top, so a three-line maxim no longer puts the circle behind it at 1280 × 800, 1280 × 720, or on a phone. The cue’s words are written only when they change, so a screen reader is not told again.
- An ally base’s unit-status report and a Link notice are not cards in Zen (they never were; a test now pins it, and the demo’s
?ally=reportshows it). A question a box forwards is one, and waits for Enter. - Demo. The card states press Enter two seconds in so they open on their card;
&queuedleaves the cards waiting.
Design: docs/specs/2026-11-01-zen-view.md, Cards wait for Enter.
Opening a base for the desk app: one workspace per opening, a wait it can set
Section titled “Opening a base for the desk app: one workspace per opening, a wait it can set”- A retry finds the opening it already made, even after the server restarts.
open/basewith a deploy id now records that opening in the server’s state folder (openings.json, mode 600, the last 64): the workspace, its tab and pane, the folder, and whether the first message was typed. A retry with the same id answers with that workspace instead of opening a second one for the same folder, as long as the folder matches and the recorded pane is still there in it; otherwise the record is dropped and a new base opens. Records of closed workspaces are pruned. - The first message is typed once. The record tracks it as pending, sending or sent. A retry types a message that was never typed, never types one that was or may have been, and its reply says which:
deliveryissent,unknownornot_sent(absent when no message was involved). A caller that ignores the field works as before. - One opening at a time per server, across a GUI that stops and starts in the process: a retry while the first opening is still joining is told
429 busyinstead of typing the message a second time. joinWaitMs.open/basetakes an optional wait for the new pi to join, a whole number of milliseconds from 10,000 to 300,000 (anything else is refused with400before anything opens); without it the wait stays 60 s. Slow Windows machines after a restart can ask for longer.
-
A flaky restore test is steady.
tests/restore.test.ts(“a restored main whose claim times out still opens no tower…”) could count the first main’s own tower tab when Herdr answered it late; the test now waits for that tab before the restart, and a second case runs it with a slow Herdr. -
A question’s notice names the unit the way the card does. The notice for a question a Straw Link box forwarded read
main _ trial13while the card, the Link row and the box’s own log readmain · trial13: the controller’s label filter turned the middle dot of a tab’s session name into_, and then the server’s own notice filter (ASCII only) did it again. The notice now keeps that one dot at both steps (restrictOwnerandrestrictNoticeinsrc/link/text.ts; the notice’s text is ASCII and U+00B7, nothing else), so every place saysmain · trial13. The rest of the name is filtered as before (Mei's MacreadsMei_s Macin the notice), and the labels in a message header keeprestrictLabel.