11 KiB
What the interface must show, and three layouts to choose from
Written 2026-11-10. This is the design document the screens are built against. It says what each page must show, where every fact comes from, what the keys are, and then three layouts at three different shapes. Nothing here is decoration: each line exists because a person needs it to make a decision or to know what is happening.
The four questions the interface answers
- What is the state of the work? todo, in progress, blocked, done — for one project.
- What is happening right now? the run, what the agent changed, what it cost.
- What needs me? blocked tasks, a merge that was refused, a task waiting for a person.
- What do I do about it? reply, unblock, assign, create, merge.
Anything that does not answer one of those four is not on a screen.
Where each fact comes from
One owner per fact. The middle column is the only place the interface may read it.
| What is shown | Read from | Never |
|---|---|---|
| task list, status, title, assignee | GET /api/companies/{id}/issues |
a local cache or folder name |
| blockers ("blocked by AUD-163") | GET /api/issues/{id}/diagnostics/blockers, per task |
the list endpoint — it carries no blockers |
| thread | GET /api/issues/{id}/comments |
a copy we keep |
| run state, start and end | GET /api/issues/{id}/runs |
a log file we write |
| cost | GET /api/issues/{id}/cost-summary |
our own arithmetic |
| what changed | git in currentExecutionWorkspace.cwd |
a diff we store |
| which folder, machine, session | <project>/.harness/harness.toml |
Paperclip (it does not know) |
| which task a viewer is on | the same file, watching |
a hidden second copy |
The rule that follows: the interface is allowed to be wrong for one refresh, and is never allowed to be right for the wrong reason. Every screen shows the time of its last read, so a stale screen says it is stale.
Pages and panes
A page is a workspace. A pane is one program in it. TUIOS keeps part of the
keyboard, so no screen may use h j k l z n for its own actions, and a bare Esc must be
free to close a box.
| Page | Pane | Must show | Keys |
|---|---|---|---|
| shell | shell | a prompt in the project folder | — |
| tree | task tree | groups todo / in progress / blocked / done; identifier, status, assignee, title, blocker; a count per group | ↑↓ Tab Shift-Tab move · a assign to Builder · enter open this task in reply and log · r re-read · q quit |
| reply | conversation | the thread with author and time; a box to type in; the status that will be sent with the reply | tab focus the box · enter send · s cycle status · esc leave the box · r re-read |
| log | run log | run state, start and end, then activity events, newest last | ↑↓ scroll · g/G top/bottom · r re-read · q quit |
| diff | change | worktree path and branch; commits on the branch; changed files with counts; the diff itself | ↑↓ scroll · r re-read |
| new | new task form | title, what it must do, must finish first, proves it is done, assign | tab/↑↓ next field · enter next, and create on the last · esc cancel |
Two decisions to settle with you, because they change the shape:
- Is the diff its own pane or the bottom half of the log? Its own pane is honest (a long diff never squeezes the log) and costs a workspace. One pane split in two is tighter and hides the diff while the log scrolls.
- Does the tree drive the other panes? My proposal: pressing
enteron a task writeswatching = AUD-163into the project'sharness.tomland asks TUIOS to show the reply page. The reply, log and diff panes watch that file and reload when it changes. One fact, one owner, no message bus.
What must never happen
- A pane that lies: showing a task as in progress after the run ended.
- A key that does nothing visible, or two keys that do the same thing differently.
- A layout that breaks below some size instead of saying "too small".
- A second copy of anything Paperclip owns, on disk or in memory across refreshes.
- An action with no visible result: every write must show what came back, or the error.
The layouts
A — one page each (what exists today, and the safe default)
Every page is its own workspace, one pane, the whole width. Best at 80 columns and on a phone-sized terminal. Worst for watching two things at once, because switching costs a keystroke and hides the tree.
┌ task tree ── workspace 2 of 5 ────────────────────────────────────────────────┐
│ spike-2026-11-10 · read 11:42:07 · 6 tasks │
│ │
│ TODO (2) │
│ ▸ AUD-166 todo — Add the weekly cost report │
│ AUD-167 todo — Document the merge gate [AUD-166 todo] │
│ IN PROGRESS (1) │
│ AUD-165 in_progress Builder Review gate probe │
│ BLOCKED (1) │
│ AUD-162 blocked — Tree render check [AUD-161 todo] │
│ DONE (2) │
│ AUD-163 done Builder Write NOTES.md │
│ │
│ ↑↓ move · a assign · enter open · r re-read · q quit │
└──────────────────────────────────────────────────────────────────────────────┘
1 shell 2 tree* 3 reply 4 log 5 diff 6 new
B — cockpit: tree and conversation side by side
One workspace holds the work; the tree stays visible while you read and reply. The log and diff live one workspace away, because during a conversation the change matters less than what the agent said.
┌ tree 34% ─────────────────┐┌ reply ────────────────────────────────────────────┐
│ spike-2026-11-10 ││ AUD-165 in_progress · Builder │
│ read 11:42:07 ││ │
│ ││ agent 11:38 │
│ TODO (2) ││ Workspace Ready — branch AUD-165-… │
│ ▸ AUD-166 Add weekly cost ││ │
│ AUD-167 Document merge ││ you 11:40 │
│ ⤷ waits on AUD-166 ││ Please also guard against HOME being unset. │
│ IN PROGRESS (1) ││ │
│ AUD-165 Review probe ││ agent 11:41 │
│ BLOCKED (1) ││ Done — guard added, tests pass. │
│ AUD-162 Tree render ││ │
│ ⤷ waits on AUD-161 ││ │
│ DONE (2) ││ │
│ AUD-163 NOTES.md ││ ┌───────────────────────────────────────────────┐ │
│ AUD-164 List lines ││ │ > _ │ │
│ ││ └───────────────────────────────────────────────┘ │
│ ↑↓ move · a assign ││ status → in_progress · s status · enter send │
└───────────────────────────┘└───────────────────────────────────────────────────┘
tab focus · esc leave box · r re-read
C — cockpit with the change always visible
Tree on the left, conversation top right, diff bottom right. The most information at once, and the only layout where a person can see a claim and the code that backs it at the same time. It needs width: below about 120 columns the diff half is unreadable and the layout must fall back to B or A.
┌ tree 28% ──────────┐┌ reply 72% ──────────────────────────────────────────────┐
│ TODO (2) ││ AUD-165 in_progress · Builder read 11:42:07 │
│ ▸ AUD-166 cost ││ agent 11:38 Workspace Ready — branch AUD-165-… │
│ AUD-167 docs ││ you 11:40 Please also guard against HOME being unset.│
│ ⤷ AUD-166 ││ agent 11:41 Done — guard added, tests pass. │
│ IN PROGRESS (1) ││ ┌─────────────────────────────────────────────────────┐ │
│ AUD-165 probe ││ │ > _ │ │
│ BLOCKED (1) ││ └─────────────────────────────────────────────────────┘ │
│ AUD-162 tree │├─ diff ──────────────────────────────────────────────────┤
│ ⤷ AUD-161 ││ cleanup.sh · branch AUD-165-… · 1 commit · +4 −0 │
│ DONE (2) ││ @@ -1,2 +1,3 @@ │
│ AUD-163 NOTES.md ││ -rm -rf "$HOME/tmp" || true │
│ ││ +[ -n "$HOME" ] && rm -rf -- "$HOME/tmp" || true │
│ ↑↓ · a · enter ││ ↑↓ scroll · r re-read │
└────────────────────┘└────────────────────────────────────────────────────────┘
Recommendation
C, falling back to B, falling back to A, chosen by the width the pane actually has, not by a setting. The person gets the most information their terminal can carry, and never a layout that pretends to have room it does not.
Concretely:
- 150 columns and wider: C. Tree 28%, right split 55/45 between reply and diff.
- 110–149 columns: B, with the diff one workspace away.
- 80–109 columns: A, one page each.
- Below 80×24: the "too small" screen with the numbers, on every page.
Two things to settle before building: whether the tree drives the other panes through
harness.toml, and whether the four screens stay four workspaces or become two.