0.26.0
- The sessions column opens from a spine on the edge (0.25.0)
A bar in the header was not it. The control belongs to the frame: the leftmost
thing on the screen and the first thing in the document, the full height of the
window, with the word running bottom to top the way a word runs on a spine.
Closed it is the word; open it is the word next to the list; and it is the only
way in or out, because a second control that opens the same column is a second
thing to keep in step with the first.
The accent runs the whole height and stays whether the column is open or shut.
A line that appears and disappears is a state indicator competing with the
background; a line that is always there is the edge of the room, and the open
state has the background and the ink to say it.
⛔ The text turns, not the button. transform on the button rotates the whole
box with it, so the border and the accent would be drawn on the edge away from
the column instead of the one beside it - correct in the element's own
coordinates and backwards on the screen.
And the column's own header lost its title: with the spine beside it, the two
sat twenty pixels apart saying the same word. The row keeps its height from its
padding, so it still lines up with the app header.
Three known-bads on the new gate: the control moved after the conversation pane,
where it reads as one of that pane's buttons; a second control carrying
aria-controls="rail"; and the vertical writing dropped, which leaves the word
running across a column 34px wide.
- A word on a wall is not a button, and a list needs two edges
At 34px with nothing but letters the spine read as a label somebody had printed
on the frame, which is the one thing it must not read as. Wider, and with the
same chevron the step rows use, drawn from borders rather than an icon: it
points into the room when the column is shut and back out when it is open, so
the thing you press also says which way it goes. The chevron and the word are
two rows of one grid, centred together - pinned to the top the arrow sat 450px
from the word and they read as separate things on the same strip.
And a list is set apart on BOTH sides. Only stepping the left is why it still
read as prose with dots in it: measured on a real answer, the items began 26px
in from the paragraph above and ended at 548, the same pixel the paragraph
ended on, so the block was indented on one edge and flush on the other. The eye
reads that as the same column, slightly ragged, rather than as something set
inside it. 2.6em in and 1.4em back gives it two edges of its own: 36px and 20px
on the running page.
- One screen, or two, or four (0.26.0)
⛔ THE CONSTRAINT THIS PANE WAS BUILT AROUND IS FOUR TIMES SMALLER THAN THE
FILE SAID. It assumed a frame costs 22 ms of pipe, and everything followed from
that: one live pane, seven previews taking turns at one every 400 ms, and a
comment explaining that eight panes would want 2.3 seconds of pipe per second.
Measured again on 2026-09-09 with four real browsers over the same Link the
interface uses, a frame costs 5 to 6 ms - the capture already runs inside the
engine, and the server hands over the latest picture rather than taking one.
Four panes polled flat out delivered 80 frames a second in total, about 20
each, and an action still landed in 49 ms against 40 with a single pane. The
pane was rationing something that had stopped being scarce.
So the stage is a grid of 1, 2 or 4 live screens, chosen from the chrome and
remembered per browser. The vocabulary is a control room's because the job is a
control room's: one screen when you are watching the work, four when you are
keeping an eye on it. The budget is still spent deliberately rather than to the
limit - 25 frames a second at one-up, 20 each at two, 10 each at four, so 40
requests a second at most against the 80 measured as affordable.
Clicking a screen brings that browser to the front, which at one-up means it
fills the stage: that is what "click it and go to another screen" means. The
strip below carries whatever the stage does not, so at four-up with four
browsers it is empty and at one-up with eight it holds seven.
⛔ And every screen says how old its picture is. On a healthy stage each one is
refreshed every 40 to 100 ms, so anything past two seconds means that browser
has stopped answering while its last frame sits there looking alive. Stale has
to look stale: it is the first rule in a control room and the fifth state a
real-time dashboard needs after empty, loading, error and partial.
⛔ And the pump can no longer be killed by a bad pass, because it was, the first
time this ran. ageAll reached for the age label on the placeholder cell, which
has no caption, threw, and the throw was outside the fetch's try - so the timer
at the bottom was never reached and the loop stopped for good, in silence. A
dead pump reads as a server that has stopped answering rather than as a page
with a bug in it. The body is a separate function now and the scheduler only
schedules.
The picture is anchored rather than sized in percent: height:100% on a grid
item whose parent takes its height from a flex row does not resolve, so it fell
back on its own aspect ratio and came out 23px taller than its box - measured -
sitting over the caption underneath.
Six known-bads on the new gates, all killed, plus the pace gate rewritten: it
asserted a literal pause that no longer exists, and now reads the table of
frames a second and checks both edges - never below what the engine is told to
produce, never above what the pipe was measured to carry.