Revert "Every workspace split into real panes: 19 panes across 7 workspaces, one program per panel"

This reverts commit 32fa2752af.
This commit is contained in:
2026-11-10 16:40:14 +11:00
parent 32fa2752af
commit 851a15be74
19 changed files with 58 additions and 972 deletions

View File

@@ -137,74 +137,3 @@ Committed as `351cb35`. Next, when you are back: tell me which of the six you wa
- session: [[packs/paperclip-harness/80-sessions/paperclip-harness-tui-six-workspace-build-screens-and-bug-fi|paperclip-harness TUI: six-workspace build, screens, and bug fixes (351cb35)]] - session: [[packs/paperclip-harness/80-sessions/paperclip-harness-tui-six-workspace-build-screens-and-bug-fi|paperclip-harness TUI: six-workspace build, screens, and bug fixes (351cb35)]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-what-you-will-see-when-you-open-it-2026-11-10|Rich discovery: what-you-will-see-when-you-open-it 2026-11-10 03:29:19]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-what-you-will-see-when-you-open-it-2026-11-10|Rich discovery: what-you-will-see-when-you-open-it 2026-11-10 03:29:19]]
- context: [[packs/paperclip-harness/20-context/paperclip-harness|what-you-will-see-when-you-open-it]] - context: [[packs/paperclip-harness/20-context/paperclip-harness|what-you-will-see-when-you-open-it]]
---
## Update (2026-11-10 05:13:11)
paperclip-harness is a terminal harness TUI organised as Workspace>pane. Current seven workspaces (capitalised) and their panes: Shell>shell, Board>board, Work>work, Reply>reply, New task>new (name lives in bin/board), Project>project, Repair>repair. Pane names remain the program names. Full rebuild/reload is key 'R'.
Key panels: Board>board lists tasks with status icons before the status word (todo circle, running triangle, blocked square, done tick). Reply>reply shows 'the task' panel (identifier/status/assignee/title/verify command) above a threaded conversation and a reply composer that leaves status alone. Project>project has facts/costs/settings panels; settings shows .harness/harness.toml fields then Paperclip agent limits (monthly budget cents, spend). Repair>repair has health, layout, rebuild-log panels plus an icon test row.
Data model: .harness/harness.toml is a project's only self-owned file; Paperclip owns tasks, blockers, comments, runs, cost, worktrees, and agent month budgets/spend.
Recent commit: 2640049 (workspace names capitalised, Reply task panel, Project settings/limits, icon test row). Screenshots in /tmp/mockups/: board-live.png, work-live.png, reply-live.png, new-live.png, project-live.png, repair-live.png.
## Detailed context from completed turn
"No `nixos-rebuild` on `.27`." The fonts are already there — 98 Nerd Font entries installed. What was missing was one line in its Ghostty config, which I added. Your restart of the config was the right move.
## How to tell whether the icons loaded
On `.27`, look at **Repair>repair**, first tab. There is now a row that prints the icons themselves:
```
icons · ▶ ■ ◆ if these are boxes, this terminal is not using a Nerd Font
```
If you see actual shapes, the font is working and the panels will show icons everywhere. If those are boxes or blanks, the terminal is still on its default font and one more thing needs checking.
The second place to look is **Board>board**: each task's status should have a small icon before the word (todo circle, running triangle, blocked square, done tick).
## Names, the way you asked
Workspaces are now capitalised and say what they are. Pane names stay as the program names, so everything is written **Workspace>pane**:
| Workspace>pane | was |
|---|---|
| Shell>shell | shell |
| Board>board | board |
| Work>work | work |
| Reply>reply | reply |
| **New task**>new | new |
| Project>project | project |
| Repair>repair | repair |
`New task` is the workspace name now; if you would prefer `What` or `Author`, it is one word in `bin/board`.
## Missing panels built
- **Reply>reply** gained a *the task* panel above the thread: identifier, status, assignee by name, the title, and the verify command. You no longer have to go back to Board>board to remember what you are replying about.
- **Project>project** settings is now a real panel: the six things this project keeps in its own file (session, machine, folder, Paperclip id, watched task, verify command with the task it came from), then the limits Paperclip holds — every agent with its monthly budget and spend.
- **Repair>repair** has the icon row, and its three panels are health, layout and rebuild log.
Everything rebuilt through `R` and verified: seven workspaces, one pane each, all drawing. Screenshots refreshed in `/tmp/mockups/` — `board-live.png`, `work-live.png`, `reply-live.png`, `new-live.png`, `project-live.png`, `repair-live.png`.
Committed as `2640049`.
Next by Workspace>pane, in the order I would do them: **Board>board** needs-you cards that act on the spot, **Work>work** a diff panel visible beside the runs at wide widths, **Project>project** a settings panel that writes the verify command, and **Repair>repair** a one-key check of the whole session.
## Evidence
- Confidence: 90%
- Reason: Matches the component so future turns can update this context rather than creating transcript noise.; enriched with final-answer detail because persistence should be reusable
## Related learned memories <!-- same-turn-2026-11-10 05:13:11 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- observation: [[packs/paperclip-harness/60-observations/paperclip-harness-tui-editor-pitfall-illegal-rune-literal-af|Nerd Font icon troubleshooting on .27 host]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Committed paperclip-harness workspace rename + Reply/Project/Repair panels (2640049)]]
- session: [[packs/paperclip-harness/80-sessions/paperclip-harness-tui-workspace-rename-new-panels-commit-264|paperclip-harness TUI workspace rename + new panels (commit 2640049)]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-how-to-tell-whether-the-icons-loaded-2026-11-|Rich discovery: how-to-tell-whether-the-icons-loaded 2026-11-10 05:13:11]]
- context: [[packs/paperclip-harness/20-context/tmp|how-to-tell-whether-the-icons-loaded]]

View File

@@ -385,70 +385,3 @@ Tell me which of those you want first, and whether the icons appear after reopen
- session: [[packs/paperclip-harness/80-sessions/paperclip-harness-tui-icon-font-fix-help-filter-unblock-safe|paperclip-harness TUI: icon font fix, help/filter/unblock, safe rebuild (commit 09e32da)]] - session: [[packs/paperclip-harness/80-sessions/paperclip-harness-tui-icon-font-fix-help-filter-unblock-safe|paperclip-harness TUI: icon font fix, help/filter/unblock, safe rebuild (commit 09e32da)]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-config-2026-11-10-05-00-01|Rich discovery: .config 2026-11-10 05:00:01]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-config-2026-11-10-05-00-01|Rich discovery: .config 2026-11-10 05:00:01]]
- context: [[packs/paperclip-harness/20-context/tmp|.config]] - context: [[packs/paperclip-harness/20-context/tmp|.config]]
---
## Update (2026-11-10 05:13:11)
## Rich discovery update (2026-11-10 05:13:11)
"No `nixos-rebuild` on `.27`." The fonts are already there — 98 Nerd Font entries installed. What was missing was one line in its Ghostty config, which I added. Your restart of the config was the right move.
## How to tell whether the icons loaded
On `.27`, look at **Repair>repair**, first tab. There is now a row that prints the icons themselves:
```
icons · ▶ ■ ◆ if these are boxes, this terminal is not using a Nerd Font
```
If you see actual shapes, the font is working and the panels will show icons everywhere. If those are boxes or blanks, the terminal is still on its default font and one more thing needs checking.
The second place to look is **Board>board**: each task's status should have a small icon before the word (todo circle, running triangle, blocked square, done tick).
## Names, the way you asked
Workspaces are now capitalised and say what they are. Pane names stay as the program names, so everything is written **Workspace>pane**:
| Workspace>pane | was |
|---|---|
| Shell>shell | shell |
| Board>board | board |
| Work>work | work |
| Reply>reply | reply |
| **New task**>new | new |
| Project>project | project |
| Repair>repair | repair |
`New task` is the workspace name now; if you would prefer `What` or `Author`, it is one word in `bin/board`.
## Missing panels built
- **Reply>reply** gained a *the task* panel above the thread: identifier, status, assignee by name, the title, and the verify command. You no longer have to go back to Board>board to remember what you are replying about.
- **Project>project** settings is now a real panel: the six things this project keeps in its own file (session, machine, folder, Paperclip id, watched task, verify command with the task it came from), then the limits Paperclip holds — every agent with its monthly budget and spend.
- **Repair>repair** has the icon row, and its three panels are health, layout and rebuild log.
Everything rebuilt through `R` and verified: seven workspaces, one pane each, all drawing. Screenshots refreshed in `/tmp/mockups/` — `board-live.png`, `work-live.png`, `reply-live.png`, `new-live.png`, `project-live.png`, `repair-live.png`.
Committed as `2640049`.
Next by Workspace>pane, in the order I would do them: **Board>board** needs-you cards that act on the spot, **Work>work** a diff panel visible beside the runs at wide widths, **Project>project** a settings panel that writes the verify command, and **Repair>repair** a one-key check of the whole session.
## Source paths observed
- `etc/profiles/per-user/sam/bin/hunk`
## Evidence
- Confidence: 84%
- Reason: Update component/repository context with detailed discovered structure and configuration
## Related learned memories <!-- same-turn-2026-11-10 05:13:11 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- observation: [[packs/paperclip-harness/60-observations/paperclip-harness-tui-editor-pitfall-illegal-rune-literal-af|Nerd Font icon troubleshooting on .27 host]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Committed paperclip-harness workspace rename + Reply/Project/Repair panels (2640049)]]
- session: [[packs/paperclip-harness/80-sessions/paperclip-harness-tui-workspace-rename-new-panels-commit-264|paperclip-harness TUI workspace rename + new panels (commit 2640049)]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-how-to-tell-whether-the-icons-loaded-2026-11-|Rich discovery: how-to-tell-whether-the-icons-loaded 2026-11-10 05:13:11]]
- context: [[packs/paperclip-harness/20-context/tmp|how-to-tell-whether-the-icons-loaded]]

View File

@@ -248,74 +248,3 @@ Deliverables: built six (plus shell = seven) styled TUI workspaces in the paperc
- session: [[packs/paperclip-harness/80-sessions/paperclip-harness-tui-six-workspace-build-screens-and-bug-fi|paperclip-harness TUI: six-workspace build, screens, and bug fixes (351cb35)]] - session: [[packs/paperclip-harness/80-sessions/paperclip-harness-tui-six-workspace-build-screens-and-bug-fi|paperclip-harness TUI: six-workspace build, screens, and bug fixes (351cb35)]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-what-you-will-see-when-you-open-it-2026-11-10|Rich discovery: what-you-will-see-when-you-open-it 2026-11-10 03:29:19]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-what-you-will-see-when-you-open-it-2026-11-10|Rich discovery: what-you-will-see-when-you-open-it 2026-11-10 03:29:19]]
- context: [[packs/paperclip-harness/20-context/paperclip-harness|what-you-will-see-when-you-open-it]] - context: [[packs/paperclip-harness/20-context/paperclip-harness|what-you-will-see-when-you-open-it]]
---
## Update (2026-11-10 05:13:11)
Deliverable: commit 2640049 in paperclip-harness — capitalised Workspace>pane naming (Shell, Board, Work, Reply, New task, Project, Repair), Reply>reply 'the task' panel, Project>project settings+limits panel, Repair>repair icon test row and 3-panel layout.
Artifacts: bin/board (workspace name word), refreshed screenshots in /tmp/mockups/ (board-live.png, work-live.png, reply-live.png, new-live.png, project-live.png, repair-live.png).
Validation: build=0; rebuilt via 'R'; seven workspaces, one pane each, all drawing; verified Reply shows AUD-166 done with assignee resolved to a name after a test reply; Repair icon row renders glyphs.
Follow-ups suggested in order: Board needs-you cards that act on the spot; Work diff panel beside runs at wide widths; Project settings that writes verify command; Repair one-key whole-session check.
## Detailed deliverable from completed turn
"No `nixos-rebuild` on `.27`." The fonts are already there — 98 Nerd Font entries installed. What was missing was one line in its Ghostty config, which I added. Your restart of the config was the right move.
## How to tell whether the icons loaded
On `.27`, look at **Repair>repair**, first tab. There is now a row that prints the icons themselves:
```
icons · ▶ ■ ◆ if these are boxes, this terminal is not using a Nerd Font
```
If you see actual shapes, the font is working and the panels will show icons everywhere. If those are boxes or blanks, the terminal is still on its default font and one more thing needs checking.
The second place to look is **Board>board**: each task's status should have a small icon before the word (todo circle, running triangle, blocked square, done tick).
## Names, the way you asked
Workspaces are now capitalised and say what they are. Pane names stay as the program names, so everything is written **Workspace>pane**:
| Workspace>pane | was |
|---|---|
| Shell>shell | shell |
| Board>board | board |
| Work>work | work |
| Reply>reply | reply |
| **New task**>new | new |
| Project>project | project |
| Repair>repair | repair |
`New task` is the workspace name now; if you would prefer `What` or `Author`, it is one word in `bin/board`.
## Missing panels built
- **Reply>reply** gained a *the task* panel above the thread: identifier, status, assignee by name, the title, and the verify command. You no longer have to go back to Board>board to remember what you are replying about.
- **Project>project** settings is now a real panel: the six things this project keeps in its own file (session, machine, folder, Paperclip id, watched task, verify command with the task it came from), then the limits Paperclip holds — every agent with its monthly budget and spend.
- **Repair>repair** has the icon row, and its three panels are health, layout and rebuild log.
Everything rebuilt through `R` and verified: seven workspaces, one pane each, all drawing. Screenshots refreshed in `/tmp/mockups/` — `board-live.png`, `work-live.png`, `reply-live.png`, `new-live.png`, `project-live.png`, `repair-live.png`.
Committed as `2640049`.
Next by Workspace>pane, in the order I would do them: **Board>board** needs-you cards that act on the spot, **Work>work** a diff panel visible beside the runs at wide widths, **Project>project** a settings panel that writes the verify command, and **Repair>repair** a one-key check of the whole session.
## Evidence
- Confidence: 90%
- Reason: Completed work with concrete artifacts and validation, useful for future context.; enriched with final-answer detail because persistence should be reusable
## Related learned memories <!-- same-turn-2026-11-10 05:13:11 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- observation: [[packs/paperclip-harness/60-observations/paperclip-harness-tui-editor-pitfall-illegal-rune-literal-af|Nerd Font icon troubleshooting on .27 host]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Committed paperclip-harness workspace rename + Reply/Project/Repair panels (2640049)]]
- session: [[packs/paperclip-harness/80-sessions/paperclip-harness-tui-workspace-rename-new-panels-commit-264|paperclip-harness TUI workspace rename + new panels (commit 2640049)]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-how-to-tell-whether-the-icons-loaded-2026-11-|Rich discovery: how-to-tell-whether-the-icons-loaded 2026-11-10 05:13:11]]
- context: [[packs/paperclip-harness/20-context/tmp|how-to-tell-whether-the-icons-loaded]]

View File

@@ -77,186 +77,3 @@ Tell me which of those you want first, and whether the icons appear after reopen
- session: [[packs/paperclip-harness/80-sessions/paperclip-harness-tui-icon-font-fix-help-filter-unblock-safe|paperclip-harness TUI: icon font fix, help/filter/unblock, safe rebuild (commit 09e32da)]] - session: [[packs/paperclip-harness/80-sessions/paperclip-harness-tui-icon-font-fix-help-filter-unblock-safe|paperclip-harness TUI: icon font fix, help/filter/unblock, safe rebuild (commit 09e32da)]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-config-2026-11-10-05-00-01|Rich discovery: .config 2026-11-10 05:00:01]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-config-2026-11-10-05-00-01|Rich discovery: .config 2026-11-10 05:00:01]]
- context: [[packs/paperclip-harness/20-context/tmp|.config]] - context: [[packs/paperclip-harness/20-context/tmp|.config]]
---
## Update (2026-11-10 05:13:11)
Icons showing as boxes in the harness TUI on host .27 was caused by a missing font setting in that host's Ghostty config, not missing fonts: 98 Nerd Font entries were already installed. Adding the one Ghostty config line and restarting the config fixed it. Note: no nixos-rebuild exists on .27.
How to verify icon rendering:
- Repair>repair, first tab: an 'icons' row prints the glyphs. Real shapes = Nerd Font working; boxes/blanks = terminal still on default font.
- Board>board: each task status should have a small icon before the word (todo circle, running triangle, blocked square, done tick).
## Detailed observations from completed turn
"No `nixos-rebuild` on `.27`." The fonts are already there — 98 Nerd Font entries installed. What was missing was one line in its Ghostty config, which I added. Your restart of the config was the right move.
## How to tell whether the icons loaded
On `.27`, look at **Repair>repair**, first tab. There is now a row that prints the icons themselves:
```
icons · ▶ ■ ◆ if these are boxes, this terminal is not using a Nerd Font
```
If you see actual shapes, the font is working and the panels will show icons everywhere. If those are boxes or blanks, the terminal is still on its default font and one more thing needs checking.
The second place to look is **Board>board**: each task's status should have a small icon before the word (todo circle, running triangle, blocked square, done tick).
## Names, the way you asked
Workspaces are now capitalised and say what they are. Pane names stay as the program names, so everything is written **Workspace>pane**:
| Workspace>pane | was |
|---|---|
| Shell>shell | shell |
| Board>board | board |
| Work>work | work |
| Reply>reply | reply |
| **New task**>new | new |
| Project>project | project |
| Repair>repair | repair |
`New task` is the workspace name now; if you would prefer `What` or `Author`, it is one word in `bin/board`.
## Missing panels built
- **Reply>reply** gained a *the task* panel above the thread: identifier, status, assignee by name, the title, and the verify command. You no longer have to go back to Board>board to remember what you are replying about.
- **Project>project** settings is now a real panel: the six things this project keeps in its own file (session, machine, folder, Paperclip id, watched task, verify command with the task it came from), then the limits Paperclip holds — every agent with its monthly budget and spend.
- **Repair>repair** has the icon row, and its three panels are health, layout and rebuild log.
Everything rebuilt through `R` and verified: seven workspaces, one pane each, all drawing. Screenshots refreshed in `/tmp/mockups/` — `board-live.png`, `work-live.png`, `reply-live.png`, `new-live.png`, `project-live.png`, `repair-live.png`.
Committed as `2640049`.
Next by Workspace>pane, in the order I would do them: **Board>board** needs-you cards that act on the spot, **Work>work** a diff panel visible beside the runs at wide widths, **Project>project** a settings panel that writes the verify command, and **Repair>repair** a one-key check of the whole session.
## Evidence
- Confidence: 90%
- Reason: Concrete operational discovery about font/icon issue and verification method.; enriched with final-answer detail because persistence should be reusable
---
## Update (2026-11-10 05:13:11)
In paperclip-harness, a project's only self-owned file is .harness/harness.toml. It holds: session, machine, folder, paperclip id, watching (task identifier, e.g. AUD-166), and verify command (shown with the task it was derived from, e.g. 'test -s CHANGELOG.md (from AUD-166)'). Paperclip owns everything else: tasks, blockers, comments, runs, cost and worktrees. Paperclip-held agent limits are monthly budgets in cents (researcher 2000, System 3000, Hub 0, Chief of Staff 5000, Documenter 2000, Builder 5000) with spend tracked. Example sandbox project spike-2026-11-10 used folder /tmp/pc-sandbox-2026-11-10 on machine nixos-desktop, session paperclip-harness.
## Detailed observations from completed turn
"No `nixos-rebuild` on `.27`." The fonts are already there — 98 Nerd Font entries installed. What was missing was one line in its Ghostty config, which I added. Your restart of the config was the right move.
## How to tell whether the icons loaded
On `.27`, look at **Repair>repair**, first tab. There is now a row that prints the icons themselves:
```
icons · ▶ ■ ◆ if these are boxes, this terminal is not using a Nerd Font
```
If you see actual shapes, the font is working and the panels will show icons everywhere. If those are boxes or blanks, the terminal is still on its default font and one more thing needs checking.
The second place to look is **Board>board**: each task's status should have a small icon before the word (todo circle, running triangle, blocked square, done tick).
## Names, the way you asked
Workspaces are now capitalised and say what they are. Pane names stay as the program names, so everything is written **Workspace>pane**:
| Workspace>pane | was |
|---|---|
| Shell>shell | shell |
| Board>board | board |
| Work>work | work |
| Reply>reply | reply |
| **New task**>new | new |
| Project>project | project |
| Repair>repair | repair |
`New task` is the workspace name now; if you would prefer `What` or `Author`, it is one word in `bin/board`.
## Missing panels built
- **Reply>reply** gained a *the task* panel above the thread: identifier, status, assignee by name, the title, and the verify command. You no longer have to go back to Board>board to remember what you are replying about.
- **Project>project** settings is now a real panel: the six things this project keeps in its own file (session, machine, folder, Paperclip id, watched task, verify command with the task it came from), then the limits Paperclip holds — every agent with its monthly budget and spend.
- **Repair>repair** has the icon row, and its three panels are health, layout and rebuild log.
Everything rebuilt through `R` and verified: seven workspaces, one pane each, all drawing. Screenshots refreshed in `/tmp/mockups/` — `board-live.png`, `work-live.png`, `reply-live.png`, `new-live.png`, `project-live.png`, `repair-live.png`.
Committed as `2640049`.
Next by Workspace>pane, in the order I would do them: **Board>board** needs-you cards that act on the spot, **Work>work** a diff panel visible beside the runs at wide widths, **Project>project** a settings panel that writes the verify command, and **Repair>repair** a one-key check of the whole session.
## Evidence
- Confidence: 90%
- Reason: Evidence-backed architecture/configuration details useful to future agents.; enriched with final-answer detail because persistence should be reusable
---
## Update (2026-11-10 05:13:11)
## Structural/configuration observations (2026-11-10 05:13:11)
"No `nixos-rebuild` on `.27`." The fonts are already there — 98 Nerd Font entries installed. What was missing was one line in its Ghostty config, which I added. Your restart of the config was the right move.
## How to tell whether the icons loaded
On `.27`, look at **Repair>repair**, first tab. There is now a row that prints the icons themselves:
```
icons · ▶ ■ ◆ if these are boxes, this terminal is not using a Nerd Font
```
If you see actual shapes, the font is working and the panels will show icons everywhere. If those are boxes or blanks, the terminal is still on its default font and one more thing needs checking.
The second place to look is **Board>board**: each task's status should have a small icon before the word (todo circle, running triangle, blocked square, done tick).
## Names, the way you asked
Workspaces are now capitalised and say what they are. Pane names stay as the program names, so everything is written **Workspace>pane**:
| Workspace>pane | was |
|---|---|
| Shell>shell | shell |
| Board>board | board |
| Work>work | work |
| Reply>reply | reply |
| **New task**>new | new |
| Project>project | project |
| Repair>repair | repair |
`New task` is the workspace name now; if you would prefer `What` or `Author`, it is one word in `bin/board`.
## Missing panels built
- **Reply>reply** gained a *the task* panel above the thread: identifier, status, assignee by name, the title, and the verify command. You no longer have to go back to Board>board to remember what you are replying about.
- **Project>project** settings is now a real panel: the six things this project keeps in its own file (session, machine, folder, Paperclip id, watched task, verify command with the task it came from), then the limits Paperclip holds — every agent with its monthly budget and spend.
- **Repair>repair** has the icon row, and its three panels are health, layout and rebuild log.
Everything rebuilt through `R` and verified: seven workspaces, one pane each, all drawing. Screenshots refreshed in `/tmp/mockups/` — `board-live.png`, `work-live.png`, `reply-live.png`, `new-live.png`, `project-live.png`, `repair-live.png`.
Committed as `2640049`.
Next by Workspace>pane, in the order I would do them: **Board>board** needs-you cards that act on the spot, **Work>work** a diff panel visible beside the runs at wide widths, **Project>project** a settings panel that writes the verify command, and **Repair>repair** a one-key check of the whole session.
## Source paths observed
- `etc/profiles/per-user/sam/bin/hunk`
## Evidence
- Confidence: 82%
- Reason: Captured factual repo structure/configuration patterns from rich discovery
## Related learned memories <!-- same-turn-2026-11-10 05:13:11 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- observation: [[packs/paperclip-harness/60-observations/paperclip-harness-tui-editor-pitfall-illegal-rune-literal-af|Nerd Font icon troubleshooting on .27 host]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Committed paperclip-harness workspace rename + Reply/Project/Repair panels (2640049)]]
- session: [[packs/paperclip-harness/80-sessions/paperclip-harness-tui-workspace-rename-new-panels-commit-264|paperclip-harness TUI workspace rename + new panels (commit 2640049)]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-how-to-tell-whether-the-icons-loaded-2026-11-|Rich discovery: how-to-tell-whether-the-icons-loaded 2026-11-10 05:13:11]]
- context: [[packs/paperclip-harness/20-context/tmp|how-to-tell-whether-the-icons-loaded]]

View File

@@ -1,51 +0,0 @@
---
type: session
id: session.paperclip-harness.paperclip-harness-tui-workspace-rename-new-panels-commit-264
title: paperclip-harness TUI workspace rename + new panels (commit 2640049)
status: active
source_of_truth: false
freshness: current
last_reviewed: 2026-11-10
tags:
- pack/paperclip-harness
- agent-memory/session
- paperclip-harness
- tui
- workspaces
- panels
- commit-2640049
---
# paperclip-harness TUI workspace rename + new panels (commit 2640049)
Turn outcome on the paperclip-harness TUI (commit 2640049, message: "Workspaces named for what they are, capitalised; Reply gains a task panel; Project settings and limits; icon test row").
Workspaces renamed/capitalised, pane names stay program names, format is Workspace>pane:
Shell>shell, Board>board, Work>work, Reply>reply, New task>new (workspace name configurable by one word in bin/board), Project>project, Repair>repair. Seven workspaces, one pane each, all drawing; rebuilt via 'R' and verified.
Panels added:
- Reply>reply: 'the task' panel above the thread — identifier, status, assignee by name, title, verify command.
- Project>project settings panel: the project's own .harness/harness.toml fields (session, machine, folder, Paperclip id, watched task, verify command + its source task), then Paperclip-held limits (each agent's monthly budget in cents and spend, e.g. Hub budget 0 spent 1, Builder budget 5000 spent 10).
- Repair>repair: icon test row printing icon glyphs, with text 'if these are boxes, this terminal is not using a Nerd Font'; plus health, layout and rebuild-log panels.
Screenshots refreshed in /tmp/mockups/: board-live.png, work-live.png, reply-live.png, new-live.png, project-live.png, repair-live.png.
Suggested next work in order: Board>board needs-you cards that act on the spot; Work>work diff panel beside runs at wide widths; Project>project settings that write the verify command; Repair>repair one-key whole-session check.
## Evidence
- Confidence: 90%
- Reason: Rich implementation snapshot from final answer with concrete names, panels, commit, artifacts, and next steps.
## Related
- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]]
## Related learned memories <!-- same-turn-2026-11-10 05:13:11 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- observation: [[packs/paperclip-harness/60-observations/paperclip-harness-tui-editor-pitfall-illegal-rune-literal-af|Nerd Font icon troubleshooting on .27 host]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Committed paperclip-harness workspace rename + Reply/Project/Repair panels (2640049)]]
- session: [[packs/paperclip-harness/80-sessions/paperclip-harness-tui-workspace-rename-new-panels-commit-264|paperclip-harness TUI workspace rename + new panels (commit 2640049)]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-how-to-tell-whether-the-icons-loaded-2026-11-|Rich discovery: how-to-tell-whether-the-icons-loaded 2026-11-10 05:13:11]]
- context: [[packs/paperclip-harness/20-context/tmp|how-to-tell-whether-the-icons-loaded]]

View File

@@ -1,86 +0,0 @@
---
type: session
id: session.paperclip-harness.rich-discovery-how-to-tell-whether-the-icons-loaded-2026-11-
title: Rich discovery: how-to-tell-whether-the-icons-loaded 2026-11-10 05:13:11
status: active
source_of_truth: false
freshness: current
last_reviewed: 2026-11-10
tags:
- pack/paperclip-harness
- agent-memory/session
- autolearn
- rich-discovery
- repo/how-to-tell-whether-the-icons-loaded
---
# Rich discovery: how-to-tell-whether-the-icons-loaded 2026-11-10 05:13:11
Sanitized rich discovery snapshot captured automatically from the completed turn.
## Source paths observed
- `etc/profiles/per-user/sam/bin/hunk`
## Final answer snapshot
"No `nixos-rebuild` on `.27`." The fonts are already there — 98 Nerd Font entries installed. What was missing was one line in its Ghostty config, which I added. Your restart of the config was the right move.
## How to tell whether the icons loaded
On `.27`, look at **Repair>repair**, first tab. There is now a row that prints the icons themselves:
```
icons · ▶ ■ ◆ if these are boxes, this terminal is not using a Nerd Font
```
If you see actual shapes, the font is working and the panels will show icons everywhere. If those are boxes or blanks, the terminal is still on its default font and one more thing needs checking.
The second place to look is **Board>board**: each task's status should have a small icon before the word (todo circle, running triangle, blocked square, done tick).
## Names, the way you asked
Workspaces are now capitalised and say what they are. Pane names stay as the program names, so everything is written **Workspace>pane**:
| Workspace>pane | was |
|---|---|
| Shell>shell | shell |
| Board>board | board |
| Work>work | work |
| Reply>reply | reply |
| **New task**>new | new |
| Project>project | project |
| Repair>repair | repair |
`New task` is the workspace name now; if you would prefer `What` or `Author`, it is one word in `bin/board`.
## Missing panels built
- **Reply>reply** gained a *the task* panel above the thread: identifier, status, assignee by name, the title, and the verify command. You no longer have to go back to Board>board to remember what you are replying about.
- **Project>project** settings is now a real panel: the six things this project keeps in its own file (session, machine, folder, Paperclip id, watched task, verify command with the task it came from), then the limits Paperclip holds — every agent with its monthly budget and spend.
- **Repair>repair** has the icon row, and its three panels are health, layout and rebuild log.
Everything rebuilt through `R` and verified: seven workspaces, one pane each, all drawing. Screenshots refreshed in `/tmp/mockups/` — `board-live.png`, `work-live.png`, `reply-live.png`, `new-live.png`, `project-live.png`, `repair-live.png`.
Committed as `2640049`.
Next by Workspace>pane, in the order I would do them: **Board>board** needs-you cards that act on the spot, **Work>work** a diff panel visible beside the runs at wide widths, **Project>project** a settings panel that writes the verify command, and **Repair>repair** a one-key check of the whole session.
## 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-10 05:13:11 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- observation: [[packs/paperclip-harness/60-observations/paperclip-harness-tui-editor-pitfall-illegal-rune-literal-af|Nerd Font icon troubleshooting on .27 host]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Committed paperclip-harness workspace rename + Reply/Project/Repair panels (2640049)]]
- session: [[packs/paperclip-harness/80-sessions/paperclip-harness-tui-workspace-rename-new-panels-commit-264|paperclip-harness TUI workspace rename + new panels (commit 2640049)]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-how-to-tell-whether-the-icons-loaded-2026-11-|Rich discovery: how-to-tell-whether-the-icons-loaded 2026-11-10 05:13:11]]
- context: [[packs/paperclip-harness/20-context/tmp|how-to-tell-whether-the-icons-loaded]]

View File

@@ -53,32 +53,18 @@ TUIOS it has to close a box.
## The six workspaces ## The six workspaces
Written the way we talk about them: **Workspace>pane**. `Board>tasks` means the tasks pane Written the way we talk about them: **Workspace>pane**. `Board>board` means the board
inside the Board workspace. Every workspace holds real panes — three of them, except Shell program running in the Board workspace.
— so each panel can be zoomed, resized, closed or moved on its own.
| Workspace>pane | shows | keys | | Workspace>pane | shows | keys |
|---|---|---| |---|---|---|
| Shell>shell | a prompt in the project folder | — | | Shell>shell | a prompt in the project folder | — |
| Shell>shell | a prompt in the project folder | — | | Board>board | tasks by group · the chosen task · needs you | ↑↓ j k · enter · a · u · / · tab |
| Board>tasks | the task tree, filtered with / | ↑↓ j k · enter · a · u · / · ? | | Work>work | runs · change · facts | [ ] tab · ↑↓ j k · g G |
| Board>detail | the chosen task: status, blockers, verify command, last run, cost | ↑↓ j k · r | | Reply>reply | the task · the thread · the reply box | enter · s · esc |
| Board>needs | decisions waiting on you, and the activity of the chosen task | [ ] tab | | New task>new | the form · what a task needs · what will be sent | tab · enter · ctrl+n |
| Work>runs | run timeline and activity | ↑↓ j k · g G | | Project>project | facts · costs · settings | [ ] tab · ↑↓ j k |
| Work>change | worktree, branch, commits, changed files, the diff | ↑↓ j k | | Repair>repair | health · layout · rebuild log | [ ] tab · ↑↓ j k · R · B |
| Work>facts | task, status, worktree path, branch, base, cost | ↑↓ j k |
| Reply>task | the task above the conversation | r |
| Reply>thread | every message | ↑↓ j k |
| Reply>replybox | the reply box, and the status it will send | enter · alt+enter · s |
| New task>form | the five fields | tab · enter |
| New task>guidance | what a task needs | — |
| New task>preview | exactly what the agent will receive | — |
| Project>facts-p | project, folder, machine, session, agents | ↑↓ j k |
| Project>costs | spend per task and the total | ↑↓ j k |
| Project>settings | the project file, and the limits Paperclip holds | ↑↓ j k |
| Repair>health | checks, three states, and an icon test row | ↑↓ j k |
| Repair>layout | what TUIOS actually has | ↑↓ j k |
| Repair>rebuild-log | the last rebuild's output | ↑↓ j k |
Choosing a task on the board and pressing Enter writes it into the project's Choosing a task on the board and pressing Enter writes it into the project's
`.harness/harness.toml`, and every other workspace follows within a second. `.harness/harness.toml`, and every other workspace follows within a second.

View File

@@ -41,38 +41,16 @@ def pages(cfg, folder):
proj = cfg["paperclip"]["project"] proj = cfg["paperclip"]["project"]
task = pj.watch_task(cfg) task = pj.watch_task(cfg)
# (pane name, workspace name, command). The workspace is what a person reads # (pane name, workspace name, command). The workspace is what a person reads
# on the rail; the pane keeps its own name. Written Workspace>pane. # on the rail, so it is capitalised and says what the page is for; the pane
# Each workspace holds several real panes, not one program drawing panels. # keeps the program's own name. Written Workspace>pane when talking about it.
b = shlex.quote(bin_path)
f = shlex.quote(folder)
def pane(name, panel, extra):
return "%s -screen %s -panel %s -folder %s %s" % (b, name, panel, f, extra)
return task, [ return task, [
("shell", "Shell", "exec zsh -i"), ("shell", "Shell", "exec zsh -i"),
("board", "Board", "%s -screen board -project %s -folder %s" % (shlex.quote(bin_path), proj, shlex.quote(folder))),
("tasks", "Board", pane("board", "tasks", "-project " + shlex.quote(proj))), ("work", "Work", "%s -screen work -folder %s" % (shlex.quote(bin_path), shlex.quote(folder))),
("detail", "Board", pane("board", "detail", "-project " + shlex.quote(proj))), ("reply", "Reply", "%s -screen reply -folder %s" % (shlex.quote(bin_path), shlex.quote(folder))),
("needs", "Board", pane("board", "needs", "-project " + shlex.quote(proj))), ("new", "New task", "%s -screen new -project %s" % (shlex.quote(bin_path), proj)),
("project", "Project", "%s -screen project -folder %s" % (shlex.quote(bin_path), shlex.quote(folder))),
("runs", "Work", pane("work", "runs", "")), ("repair", "Repair", "%s -screen repair -folder %s" % (shlex.quote(bin_path), shlex.quote(folder))),
("change", "Work", pane("work", "change", "")),
("facts", "Work", pane("work", "facts", "")),
("task", "Reply", pane("reply", "task", "")),
("thread", "Reply", pane("reply", "thread", "")),
("replybox", "Reply", pane("reply", "reply", "")),
("form", "New task", pane("new", "form", "-project " + shlex.quote(proj))),
("guidance", "New task", pane("new", "guidance", "-project " + shlex.quote(proj))),
("preview", "New task", pane("new", "preview", "-project " + shlex.quote(proj))),
("facts-p", "Project", pane("project", "facts", "")),
("costs", "Project", pane("project", "costs", "")),
("settings", "Project", pane("project", "settings", "")),
("health", "Repair", pane("repair", "health", "")),
("layout", "Repair", pane("repair", "layout", "")),
("rebuild-log", "Repair", pane("repair", "log", "")),
] ]
@@ -97,28 +75,15 @@ def main():
if tuios.close_pane(name, session): if tuios.close_pane(name, session):
h.say(" closed %s" % name) h.say(" closed %s" % name)
# Group the panes by the workspace they belong to, in first-seen order, so a
# workspace is claimed once and all of its panes are made inside it.
order = []
groups = {}
for name, workspace, command in page_list: for name, workspace, command in page_list:
if workspace not in groups: result, ident, ws = tuios.open_pane(
groups[workspace] = [] name, workspace, folder, command, session,
order.append(workspace) adopt_existing=(name == "shell"),
groups[workspace].append((name, command)) )
h.say(" %-9s %-8s workspace %s" % (name, result, ws))
for workspace in order:
number = tuios.claim(workspace, session)
for name, command in groups[workspace]:
if any((w.get("custom_name") or "") == name for w in tuios.windows(session)):
h.say(" %-11s adopted %s" % (name, workspace))
continue
tuios.new_window(name, number, folder, command, session)
h.say(" %-11s created %s" % (name, workspace))
# Number the workspaces in the order a person reads them.
for number, name in tuios.arrange([(p[0], p[1]) for p in page_list], session): for number, name in tuios.arrange([(p[0], p[1]) for p in page_list], session):
h.say(" placed %-11s workspace %s" % (name, number)) h.say(" placed %-8s workspace %s" % (name, number))
in_use = [w for w in tuios.workspaces(session) if (w.get("window_count") or 0) > 0] in_use = [w for w in tuios.workspaces(session) if (w.get("window_count") or 0) > 0]
h.say("workspaces in use: %d" % len(in_use)) h.say("workspaces in use: %d" % len(in_use))

Binary file not shown.

View File

@@ -133,51 +133,30 @@ def open_pane(name, workspace_name, cwd, command, session=None, adopt_existing=F
def arrange(order, session=None): def arrange(order, session=None):
"""Put these panes on their workspaces, in this order, and name the workspaces. """Put these panes on workspaces 1..N in this order, and name the workspaces.
`order` is a list of (pane name, workspace name). Panes that name the same `order` is a list of (pane name, workspace name) pairs. The pane keeps its
workspace are put in that same workspace, so one workspace can hold several own name; the workspace gets the name a person reads on the rail.
panes. Workspaces are numbered in the order they first appear.
TUIOS numbering follows the order panes land in, and closing panes renumbers
the rest, so a rebuild can scatter the pages. Moving each pane once, at the
end, is what makes the rail readable and stable.
""" """
names = []
for _pane, workspace in order:
if workspace not in names:
names.append(workspace)
moved = [] moved = []
for number, workspace in enumerate(names, start=1): for number, (pane, workspace) in enumerate(order, start=1):
for pane, ws in order: win = next((w for w in windows(session) if (w.get("custom_name") or "") == pane), None)
if ws != workspace: if win is None:
continue continue
win = next((w for w in windows(session) if (w.get("custom_name") or "") == pane), None) if win.get("workspace") != number:
if win is None: subprocess.run(
continue ["tuios", "move-window", str(number), "-w", win["window_id"], "-s", session or SESSION],
if win.get("workspace") != number: capture_output=True, text=True, timeout=30, check=False,
subprocess.run( )
["tuios", "move-window", str(number), "-w", win["window_id"], "-s", session or SESSION],
capture_output=True, text=True, timeout=30, check=False,
)
_name_workspace(number, workspace, session) _name_workspace(number, workspace, session)
moved.append((number, workspace)) moved.append((number, workspace))
return moved return moved
def new_window(name, workspace_number, cwd, command, session=None):
"""Create one pane in a workspace that has already been claimed."""
subprocess.run(
[
"tuios", "new-window", name,
"-s", session or SESSION,
"--workspace", str(workspace_number),
"--cwd", cwd,
"--no-focus", "--keep-open",
"--", "/bin/sh", "-c", command,
],
capture_output=True, text=True, timeout=60, check=False,
)
return name
def close_pane(name, session=None): def close_pane(name, session=None):
for w in windows(session): for w in windows(session):
if (w.get("custom_name") or "") == name: if (w.get("custom_name") or "") == name:

View File

@@ -49,8 +49,6 @@ type boardModel struct {
activity []paperclip.Activity activity []paperclip.Activity
needsTab int needsTab int
panel string
scroll int
zones *zone.Manager zones *zone.Manager
filter string filter string
typing bool typing bool
@@ -66,10 +64,10 @@ const (
focusCount focusCount
) )
func newBoardModel(c *paperclip.Client, proj paperclip.Project, folder, panel string) boardModel { func newBoardModel(c *paperclip.Client, proj paperclip.Project, folder string) boardModel {
// One click-zone manager for the whole program, created once. Wrapping each // One click-zone manager for the whole program, created once. Wrapping each
// panel separately drifts as soon as a pane is resized. // panel separately drifts as soon as a pane is resized.
return boardModel{client: c, project: proj, folder: folder, panel: panel, loading: true, zones: zone.New()} return boardModel{client: c, project: proj, folder: folder, loading: true, zones: zone.New()}
} }
func (m boardModel) Init() tea.Cmd { func (m boardModel) Init() tea.Cmd {
@@ -230,28 +228,14 @@ func (m boardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
} }
return m, tea.Quit return m, tea.Quit
case "up", "k": case "up", "k":
if m.panel == "detail" || m.panel == "needs" {
m.scroll--
return m, nil
}
if m.cursor > 0 { if m.cursor > 0 {
m.cursor-- m.cursor--
if m.panel != "" {
_ = rememberTask(m.folder, m.visible()[m.cursor].Issue.Identifier)
}
return m, m.loadDetail(m.selectedID()) return m, m.loadDetail(m.selectedID())
} }
return m, nil return m, nil
case "down", "j": case "down", "j":
if m.panel == "detail" || m.panel == "needs" {
m.scroll++
return m, nil
}
if m.cursor < len(m.visible())-1 { if m.cursor < len(m.visible())-1 {
m.cursor++ m.cursor++
if m.panel != "" {
_ = rememberTask(m.folder, m.visible()[m.cursor].Issue.Identifier)
}
return m, m.loadDetail(m.selectedID()) return m, m.loadDetail(m.selectedID())
} }
return m, nil return m, nil
@@ -280,12 +264,12 @@ func (m boardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
} }
return m, nil return m, nil
case "[", "h": case "[", "h":
if (m.panel == "needs" || m.focus == focusNeeds) && m.needsTab > 0 { if m.focus == focusNeeds && m.needsTab > 0 {
m.needsTab-- m.needsTab--
} }
return m, nil return m, nil
case "]", "l": case "]", "l":
if (m.panel == "needs" || m.focus == focusNeeds) && m.needsTab < 1 { if m.focus == focusNeeds && m.needsTab < 1 {
m.needsTab++ m.needsTab++
} }
return m, nil return m, nil
@@ -339,9 +323,6 @@ func (m boardModel) View() string {
m.width, m.height)) m.width, m.height))
} }
if m.panel != "" {
return m.singleView()
}
if m.showHelp { if m.showHelp {
return helpOverlay("keys on this panel", [][2]string{ return helpOverlay("keys on this panel", [][2]string{
{"↑ ↓ j k", "choose a task"}, {"↑ ↓ j k", "choose a task"},
@@ -414,64 +395,6 @@ func (m boardModel) View() string {
return m.zones.Scan(strings.Join([]string{header, page, footer}, "\n")) return m.zones.Scan(strings.Join([]string{header, page, footer}, "\n"))
} }
// singleView is one panel filling the pane, for when the Board workspace is
// split into real panes: tasks, the chosen task, and what needs a person.
func (m boardModel) singleView() string {
width := m.width
height := m.height
var body []string
title := "tasks"
tabs := []string(nil)
active := 0
footer := ""
switch m.panel {
case "detail":
title = "the chosen task"
body = m.summaryLines(width - 3)
footer = hintLine([]string{
keyHint("r", "read again"),
keyHint("R", "rebuild"),
keyHint("?", "keys"),
}, width)
case "needs":
title = "needs you"
tabs = []string{"decisions", "activity"}
active = m.needsTab
if m.needsTab == 0 {
body = m.needsLines(width-3, height-3)
} else {
body = m.activityLines(width-3, height-3)
}
footer = hintLine([]string{
keyHint("[ ]", "switch tab"),
keyHint("r", "read again"),
keyHint("R", "rebuild"),
}, width)
default:
body = m.treeLines(width - 3)
footer = hintLine([]string{
keyHint("↑↓ j k", "choose"),
keyHint("enter", "show everywhere"),
keyHint("a", "assign"),
keyHint("u", "unblock"),
keyHint("/", "filter"),
keyHint("?", "keys"),
}, width)
}
if m.showHelp {
return helpOverlay(title, [][2]string{
{"↑ ↓ j k", "choose a task"},
{"enter", "show it in every workspace"},
{"a / u", "assign / unblock"},
{"/", "filter"}, {"[ ]", "switch tab"},
{"r / R", "read again / rebuild"}, {"q", "leave"},
}, width, height)
}
return singlePanel(title, tabs, active, body, width, height, footer, m.scroll)
}
func (m boardModel) headerLine() string { func (m boardModel) headerLine() string {
left := panelTitleStyle().Render("Board") + mutedStyle2().Render(" · "+m.project.Name) left := panelTitleStyle().Render("Board") + mutedStyle2().Render(" · "+m.project.Name)
state := "reading…" state := "reading…"

View File

@@ -289,28 +289,3 @@ func helpOverlay(title string, entries [][2]string, w, h int) string {
p := panel{title: title, width: boxW, height: boxH, focused: true} p := panel{title: title, width: boxW, height: boxH, focused: true}
return lipgloss.Place(w, h, lipgloss.Center, lipgloss.Center, p.render(lines)) return lipgloss.Place(w, h, lipgloss.Center, lipgloss.Center, p.render(lines))
} }
// singlePanel draws one panel filling the whole pane: a title, optional tabs,
// a scrolling body, and one line of keys. It is what a pane shows when a
// workspace has been split into real panes instead of one program drawing
// several panels inside itself.
func singlePanel(title string, tabs []string, active int, body []string, w, h int, footer string, scroll int) string {
if w < 12 || h < 4 {
return fmt.Sprintf("this pane is %d × %d; it needs 12 × 4", w, h)
}
p := panel{title: title, tabs: tabs, active: active, width: w, height: h - 1, focused: true}
visible := p.bodyHeight()
maxScroll := max(0, len(body)-visible)
if scroll > maxScroll {
scroll = maxScroll
}
if scroll < 0 {
scroll = 0
}
from := scroll
to := min(len(body), from+visible)
if to < from {
to = from
}
return p.render(body[from:to]) + "\n" + truncateVisible(footer, w)
}

View File

@@ -20,7 +20,6 @@ const defaultAgent = "403de1f7-645c-4d35-bc7e-19b01d77bacc"
func main() { func main() {
screen := flag.String("screen", "board", "which screen to run: board, work, reply, new, project, repair") screen := flag.String("screen", "board", "which screen to run: board, work, reply, new, project, repair")
panelName := flag.String("panel", "", "run one panel of that screen, for a workspace split into real panes")
folder := flag.String("folder", "", "project folder, for the board screen (defaults to the current directory)") folder := flag.String("folder", "", "project folder, for the board screen (defaults to the current directory)")
light := flag.String("theme", "", "colour variant: dark, light, or empty to follow the terminal") light := flag.String("theme", "", "colour variant: dark, light, or empty to follow the terminal")
ascii := flag.Bool("ascii", false, "plain characters instead of Nerd Font icons") ascii := flag.Bool("ascii", false, "plain characters instead of Nerd Font icons")
@@ -53,21 +52,21 @@ func main() {
if dir == "" { if dir == "" {
dir, _ = os.Getwd() dir, _ = os.Getwd()
} }
m = newBoardModel(client, proj, dir, *panelName) m = newBoardModel(client, proj, dir)
case "project": case "project":
dir := *folder dir := *folder
if dir == "" { if dir == "" {
dir, _ = os.Getwd() dir, _ = os.Getwd()
} }
m = newProjectModel(client, dir, *panelName) m = newProjectModel(client, dir)
case "repair": case "repair":
dir := *folder dir := *folder
if dir == "" { if dir == "" {
dir, _ = os.Getwd() dir, _ = os.Getwd()
} }
m = newRepairModel(client, dir, *panelName) m = newRepairModel(client, dir)
case "work": case "work":
dir := *folder dir := *folder
@@ -78,7 +77,7 @@ func main() {
if issue == "" { if issue == "" {
issue = watchedTask(dir) issue = watchedTask(dir)
} }
m = newWorkModel(client, dir, issue, *panelName) m = newWorkModel(client, dir, issue)
case "tree": case "tree":
proj, err := client.ResolveProject(*project) proj, err := client.ResolveProject(*project)
@@ -101,7 +100,7 @@ func main() {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(1) os.Exit(1)
} }
m = newTaskForm(client, proj, *agent, *panelName) m = newTaskForm(client, proj, *agent)
case "log": case "log":
task, err := resolveIssue(client, *issue) task, err := resolveIssue(client, *issue)
@@ -125,7 +124,7 @@ func main() {
} }
id = task.ID id = task.ID
} }
m = newReplyModel(client, dir, id, *panelName) m = newReplyModel(client, dir, id)
default: default:
fmt.Fprintf(os.Stderr, "unknown screen %q: use board, work, reply, new, project or repair\n", *screen) fmt.Fprintf(os.Stderr, "unknown screen %q: use board, work, reply, new, project or repair\n", *screen)

View File

@@ -27,7 +27,6 @@ type createdMsg struct {
} }
type newTaskModel struct { type newTaskModel struct {
panel string
client *paperclip.Client client *paperclip.Client
proj paperclip.Project proj paperclip.Project
agent string agent string
@@ -62,8 +61,8 @@ var fieldHints = []string{
"yes, to hand it to the configured agent after the blocker is set", "yes, to hand it to the configured agent after the blocker is set",
} }
func newTaskForm(c *paperclip.Client, proj paperclip.Project, agentID, panel string) newTaskModel { func newTaskForm(c *paperclip.Client, proj paperclip.Project, agentID string) newTaskModel {
m := newTaskModel{client: c, proj: proj, agent: agentID, panel: panel} m := newTaskModel{client: c, proj: proj, agent: agentID}
for range fieldNames { for range fieldNames {
ti := textinput.New() ti := textinput.New()
ti.Placeholder = "" ti.Placeholder = ""
@@ -241,39 +240,7 @@ func (m newTaskModel) guidanceLines(width int) []string {
return out return out
} }
// singleView is one panel filling the pane: the form, the guidance, or what
// will be sent. The form keeps the keyboard, because its letters are text.
func (m newTaskModel) singleView() string {
title := "new task"
var body []string
footer := ""
switch m.panel {
case "guidance":
title = "what a task needs"
body = m.guidanceLines(m.width - 3)
footer = hintLine([]string{keyHint("r", "nothing to read here"), keyHint("R", "rebuild")}, m.width)
case "preview":
title = "what will be sent"
body = m.previewLines(m.width - 3)
footer = hintLine([]string{keyHint("tab", "back to the form"), keyHint("R", "rebuild")}, m.width)
default:
body = m.formLines(m.width - 3)
footer = hintLine([]string{
keyHint("tab", "next field"),
keyHint("enter", "next, and create on the last"),
keyHint("esc", "cancel"),
}, m.width)
}
return singlePanel(title, nil, 0, body, m.width, m.height, footer, 0)
}
func (m newTaskModel) View() string { func (m newTaskModel) View() string {
if m.panel != "" {
if m.width == 0 {
return "reading…"
}
return m.singleView()
}
if m.width == 0 || m.height == 0 { if m.width == 0 || m.height == 0 {
return "reading…" return "reading…"
} }

View File

@@ -35,7 +35,6 @@ type taskCost struct {
} }
type projectModel struct { type projectModel struct {
panel string
client *paperclip.Client client *paperclip.Client
folder string folder string
@@ -56,8 +55,8 @@ type projectModel struct {
var projectTabs = []string{"facts", "costs", "settings"} var projectTabs = []string{"facts", "costs", "settings"}
func newProjectModel(c *paperclip.Client, folder, panel string) projectModel { func newProjectModel(c *paperclip.Client, folder string) projectModel {
return projectModel{client: c, folder: folder, panel: panel} return projectModel{client: c, folder: folder}
} }
func (m projectModel) Init() tea.Cmd { func (m projectModel) Init() tea.Cmd {
@@ -194,46 +193,7 @@ func (m projectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil return m, nil
} }
// singleView is one panel filling the pane: facts, costs or settings.
func (m projectModel) singleView() string {
titles := map[string]string{"facts": "the project", "costs": "what it has cost", "settings": "settings and limits"}
title := titles[m.panel]
if title == "" {
title = "the project"
}
var body []string
switch m.panel {
case "costs":
body = m.costLines()
case "settings":
body = m.settingLines()
default:
body = m.factLines()
}
if m.err != nil {
body = append([]string{lipgloss.NewStyle().Foreground(lipgloss.Color(active.Error)).Render("error: " + m.err.Error()), ""}, body...)
}
footer := hintLine([]string{
keyHint("↑↓ j k", "scroll"),
keyHint("r", "read again"),
keyHint("R", "rebuild"),
keyHint("?", "keys"),
}, m.width)
if m.showHelp {
return helpOverlay(title, [][2]string{
{"↑ ↓ j k", "scroll"}, {"r / R", "read again / rebuild"}, {"q", "leave"},
}, m.width, m.height)
}
return singlePanel(title, nil, 0, body, m.width, m.height, footer, m.scroll)
}
func (m projectModel) View() string { func (m projectModel) View() string {
if m.panel != "" {
if m.width == 0 {
return "reading…"
}
return m.singleView()
}
if m.width == 0 || m.height == 0 { if m.width == 0 || m.height == 0 {
return "reading…" return "reading…"
} }

View File

@@ -40,7 +40,6 @@ type layoutRow struct {
} }
type repairModel struct { type repairModel struct {
panel string
client *paperclip.Client client *paperclip.Client
folder string folder string
@@ -59,8 +58,8 @@ type repairModel struct {
var repairTabs = []string{"health", "layout", "rebuild log"} var repairTabs = []string{"health", "layout", "rebuild log"}
func newRepairModel(c *paperclip.Client, folder, panel string) repairModel { func newRepairModel(c *paperclip.Client, folder string) repairModel {
return repairModel{client: c, folder: folder, panel: panel} return repairModel{client: c, folder: folder}
} }
func (m repairModel) Init() tea.Cmd { func (m repairModel) Init() tea.Cmd {
@@ -255,45 +254,7 @@ func (m repairModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil return m, nil
} }
// singleView is one panel filling the pane: health, layout or the rebuild log.
func (m repairModel) singleView() string {
titles := map[string]string{"health": "checks", "layout": "the layout as TUIOS has it", "log": "the last rebuild"}
title := titles[m.panel]
if title == "" {
title = "checks"
}
var body []string
switch m.panel {
case "layout":
body = m.layoutLines()
case "log":
body = m.logLines()
default:
body = m.healthLines()
}
footer := hintLine([]string{
keyHint("↑↓ j k", "scroll"),
keyHint("R", "rebuild every pane"),
keyHint("B", "add missing panes"),
keyHint("r", "check again"),
keyHint("?", "keys"),
}, m.width)
if m.showHelp {
return helpOverlay(title, [][2]string{
{"R", "rebuild every pane"}, {"B", "add only the missing panes"},
{"↑ ↓ j k", "scroll"}, {"r", "check again"}, {"q", "leave"},
}, m.width, m.height)
}
return singlePanel(title, nil, 0, body, m.width, m.height, footer, m.scroll)
}
func (m repairModel) View() string { func (m repairModel) View() string {
if m.panel != "" {
if m.width == 0 {
return "checking…"
}
return m.singleView()
}
if m.width == 0 || m.height == 0 { if m.width == 0 || m.height == 0 {
return "reading…" return "reading…"
} }

View File

@@ -28,7 +28,6 @@ type replyThreadMsg struct {
type replySentMsg struct{ task paperclip.Issue } type replySentMsg struct{ task paperclip.Issue }
type replyModel struct { type replyModel struct {
panel string
client *paperclip.Client client *paperclip.Client
folder string folder string
issue string issue string
@@ -51,9 +50,8 @@ type replyModel struct {
height int height int
} }
func newReplyModel(c *paperclip.Client, folder, issueID, panel string) replyModel { func newReplyModel(c *paperclip.Client, folder, issueID string) replyModel {
return replyModel{client: c, folder: folder, issue: issueID, explicit: issueID != "", return replyModel{client: c, folder: folder, issue: issueID, explicit: issueID != ""}
panel: panel, inBox: panel == "reply"}
} }
func (m replyModel) Init() tea.Cmd { func (m replyModel) Init() tea.Cmd {
@@ -229,62 +227,7 @@ func (m replyModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil return m, nil
} }
// singleView is one panel filling the pane: the task, the thread, or the box.
func (m replyModel) singleView() string {
title := "thread"
var body []string
footer := ""
switch m.panel {
case "task":
title = "the task"
body = m.headerLines(m.width - 3)
footer = hintLine([]string{keyHint("r", "read again"), keyHint("R", "rebuild")}, m.width)
case "reply":
title = "reply · " + m.statusNote()
body = m.composerLines(m.width-3, m.height-3)
footer = hintLine([]string{
keyHint("enter", "send"),
keyHint("alt+enter", "new line"),
keyHint("s", "status"),
}, m.width)
if m.notice != "" {
footer = lipgloss.NewStyle().Foreground(lipgloss.Color(active.Success)).Render(m.notice)
}
if m.err != nil {
footer = lipgloss.NewStyle().Foreground(lipgloss.Color(active.Error)).Render(truncateVisible("error: "+m.err.Error(), m.width))
}
default:
body = m.threadLines(m.width - 3)
footer = hintLine([]string{
keyHint("↑↓ j k", "scroll"),
keyHint("r", "read again"),
keyHint("R", "rebuild"),
keyHint("?", "keys"),
}, m.width)
}
if m.showHelp {
return helpOverlay(title, [][2]string{
{"enter", "write a reply"}, {"s", "status to send with it"},
{"↑ ↓ j k", "scroll"}, {"r / R", "read again / rebuild"},
}, m.width, m.height)
}
return singlePanel(title, nil, 0, body, m.width, m.height, footer, m.scroll)
}
func (m replyModel) statusNote() string {
if m.next == "" {
return "leaves the status alone"
}
return "also sets the status to " + m.next
}
func (m replyModel) View() string { func (m replyModel) View() string {
if m.panel != "" {
if m.width == 0 {
return "reading…"
}
return m.singleView()
}
if m.width == 0 || m.height == 0 { if m.width == 0 || m.height == 0 {
return "reading…" return "reading…"
} }

View File

@@ -29,7 +29,6 @@ type workMsg struct {
} }
type workModel struct { type workModel struct {
panel string
client *paperclip.Client client *paperclip.Client
folder string folder string
issue string issue string
@@ -53,8 +52,8 @@ type workModel struct {
var workTabs = []string{"runs", "change", "facts"} var workTabs = []string{"runs", "change", "facts"}
func newWorkModel(c *paperclip.Client, folder, issueID, panel string) workModel { func newWorkModel(c *paperclip.Client, folder, issueID string) workModel {
return workModel{client: c, folder: folder, issue: issueID, explicit: issueID != "", panel: panel} return workModel{client: c, folder: folder, issue: issueID, explicit: issueID != ""}
} }
func (m workModel) Init() tea.Cmd { func (m workModel) Init() tea.Cmd {
@@ -188,49 +187,7 @@ func (m workModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil return m, nil
} }
// singleView is one panel filling the pane: runs, change or facts.
func (m workModel) singleView() string {
titles := map[string]string{"runs": "runs and activity", "change": "the change", "facts": "worktree facts"}
title, ok := titles[m.panel]
if !ok {
title = "runs and activity"
}
var body []string
switch m.panel {
case "change":
body = m.diffLines(m.width - 3)
case "facts":
body = m.factsLines(m.width - 3)
default:
body = m.runsLines(m.width - 3)
}
head := m.task.Identifier
if head == "" {
head = "no task chosen"
}
footer := hintLine([]string{
keyHint("↑↓ j k", "scroll"),
keyHint("g G", "top / bottom"),
keyHint("r", "read again"),
keyHint("R", "rebuild"),
keyHint("?", "keys"),
}, m.width)
if m.showHelp {
return helpOverlay(title, [][2]string{
{"↑ ↓ j k", "scroll"}, {"g / G", "top / bottom"},
{"r / R", "read again / rebuild"}, {"q", "leave"},
}, m.width, m.height)
}
return singlePanel(title+" · "+head, nil, 0, body, m.width, m.height, footer, m.scroll)
}
func (m workModel) View() string { func (m workModel) View() string {
if m.panel != "" {
if m.width == 0 {
return "reading…"
}
return m.singleView()
}
if m.width == 0 || m.height == 0 { if m.width == 0 || m.height == 0 {
return "reading…" return "reading…"
} }