Interface spec: what each page must show, the source of every fact, and three layouts

This commit is contained in:
Sam
2026-11-10 12:46:34 +11:00
parent d03636ead3
commit ae7fc00e5b
2 changed files with 169 additions and 0 deletions

View File

@@ -19,6 +19,7 @@ it is a pointer and should be deleted or replaced.
| `docs/OPEN-ISSUES.md` | this project | what is broken or unfinished in the closed project | | `docs/OPEN-ISSUES.md` | this project | what is broken or unfinished in the closed project |
| `docs/FALLBACK-OMA.md` | this project | what OMA provides, kept in case the pivot is reversed | | `docs/FALLBACK-OMA.md` | this project | what OMA provides, kept in case the pivot is reversed |
| `docs/WHAT-WE-ARE-BUILDING.md` | this project | the person's own statement of the product | | `docs/WHAT-WE-ARE-BUILDING.md` | this project | the person's own statement of the product |
| `docs/UI-LAYOUT.md` | this project | what each page must show, where each fact comes from, and the three candidate layouts |
## What is not a document of this project ## What is not a document of this project

168
docs/UI-LAYOUT.md Normal file
View File

@@ -0,0 +1,168 @@
# 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
1. **What is the state of the work?** todo, in progress, blocked, done — for one project.
2. **What is happening right now?** the run, what the agent changed, what it cost.
3. **What needs me?** blocked tasks, a merge that was refused, a task waiting for a person.
4. **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 `enter` on a task writes
`watching = AUD-163` into the project's `harness.toml` and 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.