Short comma-separated key hints everywhere; write down what is left to build

This commit is contained in:
Sam
2026-11-11 13:39:52 +11:00
parent f94934569d
commit 0476afcb95
14 changed files with 362 additions and 54 deletions

View File

@@ -0,0 +1,82 @@
---
type: context
id: context.paperclip-harness.each-of-those-was-a-real-defect-not-misunderstanding-on-your
title: each-of-those-was-a-real-defect-not-misunderstanding-on-your
status: active
source_of_truth: false
freshness: current
last_reviewed: 2026-11-11
tags:
- pack/paperclip-harness
- agent-memory/context
- autolearn
- rich-discovery
- repo/each-of-those-was-a-real-defect-not-misunderstanding-on-your
---
# each-of-those-was-a-real-defect-not-misunderstanding-on-your
## Rich discovery update (2026-11-11 02:28:06)
Each of those was a real defect, not misunderstanding on your part.
## Runs were not selectable — they are now
The cursor existed but nothing drew it, so the list looked inert. The chosen run is now marked and highlighted:
```
╭─ the work · 2 runs runs change facts review log merge ──·focus·╮
│ state woken by started ended took
│ ▸ succeeded automation 15:56:28 15:56:59 31s
│ succeeded assignment 15:55:40 15:56:26 47s
```
`↑`/`↓` moves, `Enter` opens that run's log. The tab title now says how many runs there are, so `2 runs` versus `1 run` is visible without guessing.
## The log no longer says "choose a run" when you cannot
When a task opens, its newest run is chosen for you, so the log tab already has something in it. `Enter` on a different run replaces it.
## The merge tab was showing another task's merge
That was wrong and misleading. It now says plainly:
```
no merge has been run from this screen for AUD-166
the last merge recorded was for another task:
merge of AUD-171 started 12:53:39
press m to merge this task
```
## Why a file-only task gets merged and rebased
Because that file is a **commit**, and the commit sits on the task's branch, not on `main`. Merging is the only step that puts it on `main`. The rebase is not a rewrite of your work — it moves the branch onto the current tip of `main` so the checks run against what would actually land. If `main` has not moved since the branch was cut, the rebase does nothing and appears as a single line in the log.
`AUD-171` created `STATUS-2.md` and merged it: that is exactly what the merge did — put that one file on `main`, at `a784e0f`. Nothing else was touched, and the review found nothing to look at because a new two-line file gives a reviewer very little.
## What is still true about AUD-166
It has two runs (`assignment`, then `automation` — Paperclip's corrective wake), no review recorded because it was never merged, and a diff. So: runs tab shows two rows, `Enter` opens either log, `change` shows the diff, `review` says none recorded, `merge` now says none has been run for it.
## Navigation you can use without the Board
In `Work>work`: `n` / `p` for the next or previous task (`task 6 of 13` in the header), `o` to pick from a list, `[` / `]` for tabs. Choosing a task there also updates `Reply>reply` and the rest, because it writes the same choice the Board writes.
Committed as `f949345`; `docs/RUNS-AND-MERGE.md` now carries the explanation of runs, the merge order, and why a one-file task goes through the same gate.
## Evidence
- Confidence: 84%
- Reason: Update component/repository context with detailed discovered structure and configuration
## Related
- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]]
## Related learned memories <!-- same-turn-2026-11-11 02:28:06 -->
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-each-of-those-was-a-real-defect-not-misunders|Rich discovery: each-of-those-was-a-real-defect-not-misunderstanding-on-your 2026-11-11 02:28:06]]
- context: [[packs/paperclip-harness/20-context/each-of-those-was-a-real-defect-not-misunderstanding-on-your|each-of-those-was-a-real-defect-not-misunderstanding-on-your]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|each-of-those-was-a-real-defect-not-misunderstanding-on-your repository structure and configuration patterns]]

View File

@@ -2488,3 +2488,69 @@ Choosing a task here writes the same choice the Board writes, so `Reply>reply` a
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|navigate tasks and runs inside Work>work and merge safely (paperclip-harness)]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-home-2026-11-11-02-17-44|Rich discovery: home 2026-11-11 02:17:44]]
- context: [[packs/paperclip-harness/20-context/home|home]]
---
## Update (2026-11-11 02:28:06)
## Structural/configuration observations (2026-11-11 02:28:06)
Each of those was a real defect, not misunderstanding on your part.
## Runs were not selectable — they are now
The cursor existed but nothing drew it, so the list looked inert. The chosen run is now marked and highlighted:
```
╭─ the work · 2 runs runs change facts review log merge ──·focus·╮
│ state woken by started ended took
│ ▸ succeeded automation 15:56:28 15:56:59 31s
│ succeeded assignment 15:55:40 15:56:26 47s
```
`↑`/`↓` moves, `Enter` opens that run's log. The tab title now says how many runs there are, so `2 runs` versus `1 run` is visible without guessing.
## The log no longer says "choose a run" when you cannot
When a task opens, its newest run is chosen for you, so the log tab already has something in it. `Enter` on a different run replaces it.
## The merge tab was showing another task's merge
That was wrong and misleading. It now says plainly:
```
no merge has been run from this screen for AUD-166
the last merge recorded was for another task:
merge of AUD-171 started 12:53:39
press m to merge this task
```
## Why a file-only task gets merged and rebased
Because that file is a **commit**, and the commit sits on the task's branch, not on `main`. Merging is the only step that puts it on `main`. The rebase is not a rewrite of your work — it moves the branch onto the current tip of `main` so the checks run against what would actually land. If `main` has not moved since the branch was cut, the rebase does nothing and appears as a single line in the log.
`AUD-171` created `STATUS-2.md` and merged it: that is exactly what the merge did — put that one file on `main`, at `a784e0f`. Nothing else was touched, and the review found nothing to look at because a new two-line file gives a reviewer very little.
## What is still true about AUD-166
It has two runs (`assignment`, then `automation` — Paperclip's corrective wake), no review recorded because it was never merged, and a diff. So: runs tab shows two rows, `Enter` opens either log, `change` shows the diff, `review` says none recorded, `merge` now says none has been run for it.
## Navigation you can use without the Board
In `Work>work`: `n` / `p` for the next or previous task (`task 6 of 13` in the header), `o` to pick from a list, `[` / `]` for tabs. Choosing a task there also updates `Reply>reply` and the rest, because it writes the same choice the Board writes.
Committed as `f949345`; `docs/RUNS-AND-MERGE.md` now carries the explanation of runs, the merge order, and why a one-file task goes through the same gate.
## Evidence
- Confidence: 82%
- Reason: Captured factual repo structure/configuration patterns from rich discovery
## Related learned memories <!-- same-turn-2026-11-11 02:28:06 -->
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-each-of-those-was-a-real-defect-not-misunders|Rich discovery: each-of-those-was-a-real-defect-not-misunderstanding-on-your 2026-11-11 02:28:06]]
- context: [[packs/paperclip-harness/20-context/each-of-those-was-a-real-defect-not-misunderstanding-on-your|each-of-those-was-a-real-defect-not-misunderstanding-on-your]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|each-of-those-was-a-real-defect-not-misunderstanding-on-your repository structure and configuration patterns]]

View File

@@ -0,0 +1,85 @@
---
type: session
id: session.paperclip-harness.rich-discovery-each-of-those-was-a-real-defect-not-misunders
title: Rich discovery: each-of-those-was-a-real-defect-not-misunderstanding-on-your 2026-11-11 02:28:06
status: active
source_of_truth: false
freshness: current
last_reviewed: 2026-11-11
tags:
- pack/paperclip-harness
- agent-memory/session
- autolearn
- rich-discovery
- repo/each-of-those-was-a-real-defect-not-misunderstanding-on-your
---
# Rich discovery: each-of-those-was-a-real-defect-not-misunderstanding-on-your 2026-11-11 02:28:06
Sanitized rich discovery snapshot captured automatically from the completed turn.
## Final answer snapshot
Each of those was a real defect, not misunderstanding on your part.
## Runs were not selectable — they are now
The cursor existed but nothing drew it, so the list looked inert. The chosen run is now marked and highlighted:
```
╭─ the work · 2 runs runs change facts review log merge ──·focus·╮
│ state woken by started ended took
│ ▸ succeeded automation 15:56:28 15:56:59 31s
│ succeeded assignment 15:55:40 15:56:26 47s
```
`↑`/`↓` moves, `Enter` opens that run's log. The tab title now says how many runs there are, so `2 runs` versus `1 run` is visible without guessing.
## The log no longer says "choose a run" when you cannot
When a task opens, its newest run is chosen for you, so the log tab already has something in it. `Enter` on a different run replaces it.
## The merge tab was showing another task's merge
That was wrong and misleading. It now says plainly:
```
no merge has been run from this screen for AUD-166
the last merge recorded was for another task:
merge of AUD-171 started 12:53:39
press m to merge this task
```
## Why a file-only task gets merged and rebased
Because that file is a **commit**, and the commit sits on the task's branch, not on `main`. Merging is the only step that puts it on `main`. The rebase is not a rewrite of your work — it moves the branch onto the current tip of `main` so the checks run against what would actually land. If `main` has not moved since the branch was cut, the rebase does nothing and appears as a single line in the log.
`AUD-171` created `STATUS-2.md` and merged it: that is exactly what the merge did — put that one file on `main`, at `a784e0f`. Nothing else was touched, and the review found nothing to look at because a new two-line file gives a reviewer very little.
## What is still true about AUD-166
It has two runs (`assignment`, then `automation` — Paperclip's corrective wake), no review recorded because it was never merged, and a diff. So: runs tab shows two rows, `Enter` opens either log, `change` shows the diff, `review` says none recorded, `merge` now says none has been run for it.
## Navigation you can use without the Board
In `Work>work`: `n` / `p` for the next or previous task (`task 6 of 13` in the header), `o` to pick from a list, `[` / `]` for tabs. Choosing a task there also updates `Reply>reply` and the rest, because it writes the same choice the Board writes.
Committed as `f949345`; `docs/RUNS-AND-MERGE.md` now carries the explanation of runs, the merge order, and why a one-file task goes through the same gate.
## Evidence
- Confidence: 86%
- Reason: Preserve full sanitized discovery detail from a rich investigation turn
## Related
- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]]
## Related learned memories <!-- same-turn-2026-11-11 02:28:06 -->
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-each-of-those-was-a-real-defect-not-misunders|Rich discovery: each-of-those-was-a-real-defect-not-misunderstanding-on-your 2026-11-11 02:28:06]]
- context: [[packs/paperclip-harness/20-context/each-of-those-was-a-real-defect-not-misunderstanding-on-your|each-of-those-was-a-real-defect-not-misunderstanding-on-your]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|each-of-those-was-a-real-defect-not-misunderstanding-on-your repository structure and configuration patterns]]

Binary file not shown.

View File

@@ -24,6 +24,7 @@ it is a pointer and should be deleted or replaced.
| `docs/SESSIONS.md` | this project | the two kinds of session, where Pi's conversations are, and the plan for the junk TUIOS sessions |
| `docs/TEST-PLAN.md` | this project | the test plan: every step, every key, every expected result |
| `docs/RUNS-AND-MERGE.md` | this project | what a task, a run and a merge are, with the measured run counts |
| `docs/NEXT.md` | this project | what is left to build, in order, and what is still unproven |
## What is not a document of this project

73
docs/NEXT.md Normal file
View File

@@ -0,0 +1,73 @@
# What is left to build
Written 2026-11-11. The interface has eight workspaces and 23 panels; the work below is what is
still missing or unproven. Order is by usefulness, not by size.
## 1. Repair>cleanup — the pane you asked for
A fourth tab in `Repair>repair` that lists TUIOS sessions with their pane count and last-active
time, marks the ones that are attached, and kills only what you tick. Two rules it must keep:
- **never touch `~/.pi/paperclips/`** — those are the Pi conversations, and they are the history;
- refuse any session that is attached, and refuse `paperclip-harness` by name.
Measured today: 37 sessions, 21 of them from the closed project's tests (`harness-demo-card-*`,
`office-test-1*`). Nothing has been killed.
## 2. A Pi session browser
The cheap version first: a `t` entry that opens `~/.pi/paperclips` in Yazi, and one that runs
`pi --resume`. Then, if that feels like a detour after a week, a read-only transcript tab in
`Agents>agents` that reads the JSONL directly. `GET /api/agents/{id}/task-sessions` maps a task
to its session file, so neither version has to guess.
## 3. Project>projects — switching project
A tab listing every adopted project from a small local registry
(`~/.config/paperclip-harness/projects.toml`: id, folder, host, session), filled by `bin/office`,
with a key to switch. A project on another machine is shown as `on .27` and not offered, because
this terminal cannot open a folder that is not here.
## 4. Run a review from the interface
`Work>review` shows the verdicts already recorded. It should also be able to *start* one:
`v` runs `bin/review <task> --post` in the background and the tab follows its log, the way
`Work>merge` already does.
## 5. The old `office` scaffold
`bin/office` writes `.harness/harness.toml` and builds the panes. The previous project also
wrote a scaffold: `settings.toml`, one file per role, `verify.sh`, the per-page notes — writing
only what is missing. That is the cheapest remaining gap in the project layer.
## 6. Board>needs: act on a card
The needs-you panel lists what is waiting but the action still happens elsewhere. Each card
should carry its own key: unblock, assign, open in Reply.
## 7. Costs as a chart
`Project>overview` already draws a sparkline of runs in Go. Spend-per-day, using the same block
characters or `tuios.dev/plot`, is the natural second chart — no TML, no web view.
## 8. Memory per agent
`engram` is installed and `headroom` is running as a compression proxy, but nothing in the
interface shows which agent uses which. A read-only row in `Agents>settings` is the honest first
step; a switch is only worth adding after one agent has been run both ways and measured.
## 9. MOUSE-1
The key is in the environment; the software is not installed. The old project's policy stands:
triage only, never a gate, never a permit. It would order `Board>needs`, never decide a merge.
## Unproven, and worth a test
- opening a project **on another machine** (`bin/office --host`): written, never exercised;
- clicking a task row with the mouse in `Board>board`: the zones are wired, nobody has clicked;
- `R` with a pane that is not at its expected size;
- a task whose blocker is still open: does the agent stay away until it closes?
## Open Paperclip tasks in this project
One: `AUD-162`, a leftover test task in `todo` from the first day. Everything else is `done`.

View File

@@ -853,18 +853,18 @@ func (m agentsModel) View() string {
to := min(len(body), from+visible)
hints := []string{
keyHint("[ ]", "roster, runs, log, settings, skills, extensions"),
keyHint("enter", "open a run"),
keyHint("p P w", "pause, resume, wake"),
keyHint("r", "read again"),
keyHint("[ ]", "tab"),
keyHint("enter", "open"),
keyHint("p P w", "pause/resume/wake"),
keyHint("r", "refresh"),
keyHint("?", "keys"),
}
if m.tab == 4 || m.tab == 5 {
hints = []string{
keyHint("[ ]", "switch tab"),
keyHint("space", "turn this skill or extension on or off"),
keyHint("↑↓ j k", "move"),
keyHint("r", "read again"),
keyHint("[ ]", "tab"),
keyHint("space", "toggle"),
keyHint("↑↓", "move"),
keyHint("r", "refresh"),
}
}
footer := hintLine(hints, m.width)

View File

@@ -463,32 +463,32 @@ func (m boardModel) footerLine() string {
switch m.focus {
case focusTree:
return hintLine([]string{
keyHint("↑↓ or j k", "choose a task"),
keyHint("enter", "show it everywhere"),
keyHint("↑↓", "move"),
keyHint("enter", "open"),
keyHint("a", "assign"),
keyHint("u", "unblock"),
keyHint("t", "tools"),
keyHint("/", "filter"),
keyHint("?", "keys"),
keyHint("tab", "next panel"),
keyHint("r", "read again"),
keyHint("R", "rebuild every pane"),
keyHint("q", "leave"),
keyHint("tab", "panel"),
keyHint("r", "refresh"),
keyHint("R", "rebuild"),
keyHint("q", "quit"),
}, m.width)
case focusSummary:
return hintLine([]string{
keyHint("↑↓ or j k", "choose a task"),
keyHint("tab", "next panel"),
keyHint("r", "read again"),
keyHint("q", "leave"),
keyHint("↑↓", "move"),
keyHint("tab", "panel"),
keyHint("r", "refresh"),
keyHint("q", "quit"),
}, m.width)
default:
return hintLine([]string{
keyHint("tab", "next panel"),
keyHint("[ ]", "switch tab"),
keyHint("tab", "panel"),
keyHint("[ ]", "tab"),
keyHint("enter", "show this task everywhere"),
keyHint("r", "read again"),
keyHint("q", "leave"),
keyHint("r", "refresh"),
keyHint("q", "quit"),
}, m.width)
}
}

View File

@@ -214,9 +214,10 @@ func keyHint(keys, what string) string {
return k + " " + w
}
// hintLine joins key hints with a dot, dropping the ones that do not apply.
// hintLine joins key hints with a comma, because a footer is a list of keys and
// not a sentence.
func hintLine(hints []string, width int) string {
joined := strings.Join(hints, dimStyle().Render(" · "))
joined := strings.Join(hints, dimStyle().Render(", "))
if lipgloss.Width(joined) > width {
return truncateVisible(joined, width)
}

View File

@@ -288,9 +288,9 @@ func (m newTaskModel) View() string {
formLines := m.formLines(m.width - 4)
footer := hintLine([]string{
keyHint("tab", "next field"),
keyHint("enter", "next, and create on the last"),
keyHint("ctrl+n", "form, guidance, preview"),
keyHint("tab", "field"),
keyHint("enter", "next/create"),
keyHint("ctrl+n", "tab"),
keyHint("esc", "cancel"),
}, m.width)

View File

@@ -254,11 +254,11 @@ func (m projectModel) View() string {
to := min(len(body), from+visible)
footer := hintLine([]string{
keyHint("[ ]", "switch tab"),
keyHint("↑↓ or j k", "scroll"),
keyHint("r", "read again"),
keyHint("R", "rebuild every pane"),
keyHint("q", "leave"),
keyHint("[ ]", "tab"),
keyHint("↑↓", "scroll"),
keyHint("r", "refresh"),
keyHint("R", "rebuild"),
keyHint("q", "quit"),
}, m.width)
gap := max(2, m.width-lipgloss.Width(head)-lipgloss.Width(state)-1)

View File

@@ -302,11 +302,11 @@ func (m repairModel) View() string {
to := min(len(body), from+visible)
footer := hintLine([]string{
keyHint("[ ]", "switch tab"),
keyHint("R", "rebuild every pane"),
keyHint("↑↓ or j k", "scroll"),
keyHint("r", "check again"),
keyHint("q", "leave"),
keyHint("[ ]", "tab"),
keyHint("R", "rebuild"),
keyHint("↑↓", "scroll"),
keyHint("r", "recheck"),
keyHint("q", "quit"),
}, m.width)
gap := max(2, m.width-lipgloss.Width(head)-lipgloss.Width(state)-1)

View File

@@ -291,18 +291,18 @@ func (m replyModel) View() string {
boxLines := m.composerLines(m.width-4, composer.bodyHeight())
footer := hintLine([]string{
keyHint("enter or tab", "write a reply"),
keyHint("s", "choose a status to send"),
keyHint("↑↓ or j k", "read the thread"),
keyHint("r", "read again"),
keyHint("R", "rebuild every pane"),
keyHint("q", "leave"),
keyHint("enter", "reply"),
keyHint("s", "status"),
keyHint("↑↓", "scroll"),
keyHint("r", "refresh"),
keyHint("R", "rebuild"),
keyHint("q", "quit"),
}, m.width)
if m.inBox {
footer = hintLine([]string{
keyHint("enter", "send"),
keyHint("alt+enter", "new line"),
keyHint("esc", "close the box"),
keyHint("alt+enter", "newline"),
keyHint("esc", "close"),
}, m.width)
}
if m.err != nil {

View File

@@ -518,15 +518,15 @@ func (m workModel) View() string {
to := min(len(body), from+visible)
footer := hintLine([]string{
keyHint("[ ]", "switch tab"),
keyHint("↑↓ or j k", "scroll"),
keyHint("t", "open a tool pane"),
keyHint("o", "choose a task"),
keyHint("m", "merge this task (asks first)"),
keyHint("]", "tabs"),
keyHint("r", "read again"),
keyHint("R", "rebuild every pane"),
keyHint("q", "leave"),
keyHint("n p", "task"),
keyHint("[ ]", "tab"),
keyHint("↑↓", "scroll"),
keyHint("enter", "log"),
keyHint("o", "pick"),
keyHint("m", "merge"),
keyHint("t", "tools"),
keyHint("r", "refresh"),
keyHint("R", "rebuild"),
}, m.width)
return strings.Join([]string{