Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Board

One project’s task board, opened as a takeover at /board/{org}/{project}: a slim bar, the rows waiting on the reader, the project’s cards in lanes by state, and the line that cuts a new row. The fleet stays the glance; this is the world. Nothing of any other project appears here. The drawing it is held against is web-board.html, beside this page.

It is reached from two doors: open board on the project’s row on the home, and the tasks strip in a session’s chat, which carries open board first in its list. Both open the same page. back and done return the reader where they came from, and a deep link with nothing to go back to lands on the home.

The page draws from the board subject: a project’s rows, schedules and connector sets ride in that subject’s entry, and it hears the updates that move them. The seat chips and the fleet’s waiting on you count come from the home snapshot beside it; the row and running counts are the board’s own. So a seat that claims a row, a row that is cut, or a move made in another view arrives as an update, and the page re-reads without the reader doing anything. While it is mounted it also re-reads on a slow 30 s beat: a row’s age, its worked time and the marks read off them move with the clock, and no update announces that.

What it draws

RegionShowsRead from
Top barback, the project’s name with its own read (N waiting on you, N rows, N running), donethe route and the snapshot
Seatsone chip per seat this project holds, linking to its session page; a card dropped on one assigns itagents filtered to the project
Waiting on youthe rows whose wait is a decision, each card leading with the wait’s ask: the verify gate (approve, a words box, send back) on a row minted verify: user, or a worker’s question (an answer box), with the row’s links and a look’s artifact beside it. A root whose children are not all done draws the count and the send-back instead of approve, because a row closes only when its children dowaiting_on with kind: decision (a verification park only where the row’s own verify is user), the wait’s detail, the row’s links, and the row’s rollup
Lanesevery card, in its state’s lane, with the grown groups folded - the finished ones to their five most recent, Backlog to its tenthe board’s projects[].rows
Createa subject box, an epic picker and + add, which files the row at the queue’s endtask_create

The lanes

Six lanes, always drawn in this order, because a missing lane would read as a missing state: Backlog (pending), Waiting, In progress, Completed, Failed, Canceled. The pool of everything not yet started leads, then the lane that wants the reader, then the work. Backlog is the pending status: work kept to look at later, not by itself an assignment and not a promise to start. Its ready, unowned cards are the dispatch queue, not a promise to run. A lane is a name, its count and a rule, with its cards under it - no box around the column - and the lanes wrap to a second row rather than scroll sideways.

The grown groups fold. A finished row accumulates for the life of the project and a queue accumulates for the life of the work, so Completed, Failed, Canceled and Backlog are the lanes that grow without bound. The three finished groups draw their five most recently finished rows - by when they last moved, whatever their place in the lane - and the Backlog draws its top ten, larger than the finished groups’ five because the queue’s order is the lead’s dispatch order and the head of it is what stays in view. The rest of each group waits behind a show all (N) control under the last card. N is the lane’s true count, which the head carries whether or not the group is open; pressing the control draws every row and it reads show less, and a group at or under its limit draws whole with no control. A card the reader is dropping into a folded group stays drawn while the core’s answer is in flight, and a jump aimed at a folded row - an epic pill, a row page’s task list - opens the fold and lands on it. In progress and Waiting never fold: they are the live work.

A card

Three lines, top to bottom:

  • the mark, then the subject. A row in progress displays its active_form where it has one, with the subject under it, because that is what the reader wants to know - subjects are what a row is, active forms are what it is doing. A completed row’s subject is struck through.
  • the owner (their initial in a disc, linking to their session page, or unclaimed), the epic it belongs under where it has one, its links, and its chips - with a done/total rollup beside them on a parent.
  • worked time against the estimate with the measure under it, and how long since the row last moved (wrote 12s ago, breathing while the row is running and recently wrote).

The tree reads in two marks. A parent’s card wears an epic pill and a solid left spine; each child wears the epic’s name in a pill with a return arrow and a dimmer spine, so a tree reads across lanes at a glance and the board still works without colour. The child’s card pill is a door: it brings the epic’s own card into view and flashes it. On the row’s page the same pill is a label, and the doors there are the tasks it lists - what a parent’s tasks are, and how to reach one, lives on that page.

The mark carries shape as well as colour, so the board reads without colour:

StatusMarkThe state
Pendinga ringqueued, not started
In progressa filled accent dota seat is on it
Waitinga warning squarea wait, a question or the verify gate
Completeda tickdone
Faileda crossit ended in failure
Canceleda dim barwithdrawn, kept for the record

The chips are the server’s derived marks, named in words:

ChipToneMeans
waiting on youwarna decision wait, or a look park on a row minted verify: user
on <row>warnwaiting on another row’s completion
waiting on a resourcewarnwaiting on something outside the board
waitingwarna verification wait whose row carries no own gate: an inherited park, left behind by the old rule
waiting <3h>badthe wait has outlived the threshold
overduebadworked time has crossed the estimate
no movement <5h>dimuntouched, and nothing is waiting
owner gonebadits owner is no longer a live seat
in reviewbluecompleted with the verify gate up
lead attestedbluethe lead completed the look, citing the user’s own approval
to closebluea root whose children are all done, waiting for its close
reserved for youdimthe user reserved this pick: the sweep leaves it, it never reads ready, and no claim takes it
attempt Ndimsent back with words N times

The look has two doors, and the row page draws both. The user’s own approve sits on the waiting strip; a lead who watched the user approve in chat completes the row through tasks__approve, which requires the approval quoted and records it on the row. The completed card then wears lead attested, and the row page draws the quote with a reopen button: reopening strikes the attestation, re-mints the look gate and parks the row on the user again. The page’s pick line is the other side of the sweep: reserve for you marks the row so the sweep never dispatches it, and release takes that back. A reserved row is not ready and no seat’s claim takes it: the pick is the user’s alone. The line draws only while the row is live - a completed, failed or canceled row has no pick left to reserve.

A wait leads with its ask. The waiting card and the row page both open on the wait’s own detail where it states one, else the row’s detail - a look parks with no wait detail, and its ask is the row’s own words - and a wait with neither falls back to the state’s sentence (“A decision is waiting on you”, “A look: this row is finished and waits on your approval”). That fallback is a seeded or legacy wait, or a look minted with no ask, and a blank detail counts as none. The row’s links follow the ask as references in the sheet’s own link grammar - the app’s link colour with an underline set off the text, and a fuller rule under the pointer; no bare anchor reaches the strip, so none draws the browser’s purple. In a look the artifact is its own door, look at <name>, wearing the same pill grammar as approve and send back rather than sitting in the sentence as another link; when the artifact is already one of the row’s references, the door is its only showing and the links line keeps the others, so one reference appears once. A good ask says what the question is, why it needs the user, and links the thing to look at; a bare “waiting on you” is a card with no question on it.

The links come off the row’s own list: a URL follows in a new tab under its label, and a path or a branch draws as text rather than a dead anchor.

Moving a card

The drag is the board’s interaction, and what the card lands on decides the command: another lane is a move carrying its place (task_move), a position inside its own lane is a re-rank by drop position (task_rank), and a seat chip is an assignment (task_assign). A lane’s whole column takes the drop, an empty one included, and the place the drop chose is the priority the row keeps on either path. A press that never passes six pixels is a click, which opens the row’s own page. A dropped card lands where it was put at once and the wire’s own snapshot reconciles it a moment later; it animates into place unless the reader has asked for reduced motion, and the card under the pointer tilts and lifts.

The keyboard has the same power without a row of buttons: the arrow keys on a focused card re-order it within its lane, and the owner picker on the card is a control like any other.

The row’s page

A click - or Enter or Space on a focused card - opens the row over the board: its fields (state, owner, estimate, worked, both clocks, its parent, its pick line and its links), Its tasks on a parent - each row a door that closes the page and lands on that card - Waiting on you when the wait is a decision, carrying the wait’s ask, its links and a look’s artifact, then the waiting strip’s own controls (approve, the send-back box, the answer box), while a dependency or resource wait draws the section without them, its History - the row’s own transitions, which the core already records for the worked time - and the way into the owner’s session. The read caps what a row carries at its newest twenty, so a long trail reaches the page without riding every board encode, and the section words what it withholds (showing the last 20 of 43). The button, the scrim and Escape all close it, and the card has the keyboard back when it goes; a jump hands it to the card jumped to instead. The board stays behind it, so a reader never loses the lane they were in.

Every control is one command on the socket, carrying this project on it. task_move moves a row between lanes and lands it at the drop’s place, task_rank re-orders it, task_assign changes its owner, task_verdict approves or sends back a verify row, task_answer answers a question, task_create files a row, task_reopen strikes a lead’s attestation and parks the row on the user again, and task_reserve reserves or releases the pick. A refused edit says so on the service line rather than silently reverting, and every one of them stamps the change as the user’s.

The states the page can be in

  • A board, the ordinary case.
  • Waiting on the reader: the strip draws only when at least one row’s wait is a decision. Its absence is not a state - an empty frame above the cards would read as a board with nothing for the reader, which is a different claim from the strip not being there.
  • Empty: a project with no live rows draws one line naming its two moves
    • cut a row in the create line, or tell the lead what is next - with the six lanes still drawn under it.
  • Before the first frame: a route can be addressed before the snapshot lands. The page arrives with the connection rather than drawing a second loading state of its own, and a subscription the server turns down says so in its own words instead of reading forever.
  • Unknown: a project the snapshot does not carry draws its name and one line saying this forge does not hold it, with no lanes and no create line - a row filed there would name a project forge.toml has no entry for.
  • A set of seats: the seat chips draw only when the project holds one; a project with nothing running draws none.

The two widths

Checked at 1600 and at 430, against the drawing beside it. The lanes are a grid of minmax(260px, 1fr) columns, so a wide window fits them side by side, a phone stacks them, and neither scrolls sideways. Every control a finger reaches takes a 44px target under @media (pointer: coarse), and the drag is a pointer gesture, so it is the same one under a finger.