Workspaces named for what they are, capitalised; Reply gains a task panel; Project settings and limits; icon test row

This commit is contained in:
Sam
2026-11-10 16:12:54 +11:00
parent 09e32da046
commit 2640049f1f
15 changed files with 575 additions and 31 deletions

View File

@@ -316,3 +316,72 @@ Committed as `972d4c0`. Ready for you to poke at: the four things I would test f
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-tmp-2026-11-10-03-34-34|Rich discovery: tmp 2026-11-10 03:34:34]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-tmp-2026-11-10-03-34-34|Rich discovery: tmp 2026-11-10 03:34:34]]
- context: [[packs/paperclip-harness/20-context/tmp|tmp]] - context: [[packs/paperclip-harness/20-context/tmp|tmp]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|tmp repository structure and configuration patterns]] - observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|tmp repository structure and configuration patterns]]
---
## Update (2026-11-10 05:00:01)
## Rich discovery update (2026-11-10 05:00:01)
Found the real reason you see no icons, and it was not the server.
## Icons: the font was missing on `.27`, not `.13`
Your terminal on `.27` is what draws the characters. `.27` had 98 Nerd Font entries installed but its Ghostty config contained only `term = xterm-256color` — no `font-family`, so Ghostty used its own **plain** JetBrains Mono, which has no icon glyphs. The tick shows because `✓` is an ordinary character; the Nerd icons are private-use codepoints that a plain font simply does not have.
I appended to `~/.config/ghostty/config` on `.27` (backup at `config.bak-icons`):
```
font-family = "JetBrainsMono Nerd Font"
font-size = 12
```
Reopen the terminal window on `.27` (or reload Ghostty's config) and the icons should appear. If they still do not, say so and I will switch the panels back to plain characters by default.
## Built since you left
- **`?` on every panel** — an overlay listing the keys that work there, and nothing else. `esc` or `?` closes it.
- **`/` filter on the board** — type part of an identifier, a title or an assignee and the list narrows; the count and the chosen task follow the filter, so what is highlighted is always what would be acted on. The header shows `filter: CHANGE▌` while you type.
- **`u` unblock** — on a task whose blocker has finished but which still says blocked, one key sets it to todo with a comment. That is the state you had to hand-fix earlier.
- **Mouse clicks on task rows** — one click-zone manager created once and the whole view scanned through it, as the rules require. Clicking a row selects it and loads its detail. I cannot verify a click from here, so treat it as untested until you try it.
- **`B` on Repair** — add only the panes that are missing, never closing any. Safe to press from inside a pane, unlike `R`.
All seven panes rebuilt cleanly afterwards, and every screen drew.
Screenshots refreshed in `/tmp/mockups/`. Committed as `09e32da`.
## What I would build next, in this order
1. **Needs-you cards that act** — each card gets its own key: unblock, assign, open the conversation. Right now the panel tells you what needs you but you act in another panel.
2. **A settings tab that writes** — the verify command per project, edited in place, which is the one thing the Author form cannot express today.
3. **Diff beside the conversation** — the wide-screen arrangement from the spec: tree, thread, diff on one workspace, so a claim and its code sit together.
4. **Search across the project** — one key that filters tasks, comments and activity together, for when you remember a phrase but not the task.
Tell me which of those you want first, and whether the icons appear after reopening the terminal.
## Source paths observed
- `paperclip-harness/tui/paperclip`
- `home/sam/paseo/projects/paperclip-harness/bin/harness`
- `run/current-system/sw/bin/git`
- `home/sam/.nix-profile/bin/go`
- `home/sam/.local/bin/ocr`
- `etc/profiles/per-user/sam/bin/tuios`
- `run/current-system/sw/bin/lazygit`
- `etc/profiles/per-user/sam/bin/hunk`
- `.config/ghostty/config`
## Evidence
- Confidence: 84%
- Reason: Update component/repository context with detailed discovered structure and configuration
## Related learned memories <!-- same-turn-2026-11-10 05:00:01 -->
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Blank Nerd Font icons: missing font-family in Ghostty config on host .27]]
- observation: [[packs/paperclip-harness/60-observations/paperclip-harness-tui-editor-pitfall-illegal-rune-literal-af|paperclip-harness TUI editor pitfall: illegal rune literal after patching Go files]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-implemented-help-overlay-board-filter-unblock-key-row-mouse-|Implemented help overlay, board filter, unblock key, row mouse zones, safe add-only Repair rebuild]]
- 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]]
- context: [[packs/paperclip-harness/20-context/tmp|.config]]

View File

@@ -0,0 +1,53 @@
---
type: action
id: action.paperclip-harness.implemented-help-overlay-board-filter-unblock-key-row-mouse-
title: Implemented help overlay, board filter, unblock key, row mouse zones, safe add-only Repair rebuild
status: active
source_of_truth: false
freshness: current
last_reviewed: 2026-11-10
tags:
- pack/paperclip-harness
- agent-memory/action
- paperclip-harness
- tui
- commit-09e32da
- help-overlay
- filter
- unblock
- repair
---
# Implemented help overlay, board filter, unblock key, row mouse zones, safe add-only Repair rebuild
Deliverables committed in `paperclip-harness` as `09e32da` ("Help on every panel, task filter, unblock key, mouse zones, safe add-only rebuild"):
- `?` help overlay on every TUI panel, listing only keys valid for that panel; `esc`/`?` closes.
- Board `/` filter by identifier, title or assignee; header shows `filter: …▌`; count and chosen task follow the filter.
- `u` unblock on a blocked task whose blocker has finished → sets to todo with a comment.
- Mouse click zones on task rows via one click-zone manager created once and the whole view scanned through it; click selects row and loads detail (unverified interactively).
- `B` on the Repair pane adds only missing panes and never closes existing ones; safe from inside a pane (unlike `R`).
Also fixed the user's blank-icon problem by adding a Nerd Font `font-family` to Ghostty config on host `.27` (backup `config.bak-icons`).
Validation: `go test` for `paperclip-harness/tui` passed; all seven panes rebuilt cleanly; every screen drew; screenshots refreshed to `/tmp/mockups/`.
Open follow-up: user to confirm icons after reopening terminal, and to choose next build among needs-you action cards, writable settings tab, diff-beside-conversation, and cross-project search.
## Evidence
- Confidence: 85%
- Reason: Completed, committed implementation with validation and follow-ups.
## Related
- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]]
## Related learned memories <!-- same-turn-2026-11-10 05:00:01 -->
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Blank Nerd Font icons: missing font-family in Ghostty config on host .27]]
- observation: [[packs/paperclip-harness/60-observations/paperclip-harness-tui-editor-pitfall-illegal-rune-literal-af|paperclip-harness TUI editor pitfall: illegal rune literal after patching Go files]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-implemented-help-overlay-board-filter-unblock-key-row-mouse-|Implemented help overlay, board filter, unblock key, row mouse zones, safe add-only Repair rebuild]]
- 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]]
- context: [[packs/paperclip-harness/20-context/tmp|.config]]

View File

@@ -0,0 +1,79 @@
---
type: observation
id: observation.paperclip-harness.paperclip-harness-tui-editor-pitfall-illegal-rune-literal-af
title: paperclip-harness TUI editor pitfall: illegal rune literal after patching Go files
status: active
source_of_truth: false
freshness: current
last_reviewed: 2026-11-10
tags:
- pack/paperclip-harness
- agent-memory/observation
- paperclip-harness
- tui
- go
- build-errors
- rune-literal
---
# paperclip-harness TUI editor pitfall: illegal rune literal after patching Go files
While adding help overlays across TUI files, the patch step produced many Go compile errors in `project.go`, `repair.go`, `reply.go`, `work.go` around lines ~200–260: `illegal rune literal` / `more than one character in rune literal` — i.e., multi-character box-drawing fragments ended up inside single-quoted rune literals instead of double-quoted strings. A follow-up error was `m.showHelp undefined (type replyModel has no field or method showHelp)` because the overlay flag was added to some models but not `replyModel`.
Practical takeaways for this repo: after bulk-editing box-drawing/help overlay code, run `go build ./...` immediately and check both (a) that box-drawing lines use string literals not rune literals, and (b) that every model receiving a `showHelp` key binding actually has a `showHelp` field. The build was green after fixing both.
## Detailed observations from completed turn
Found the real reason you see no icons, and it was not the server.
## Icons: the font was missing on `.27`, not `.13`
Your terminal on `.27` is what draws the characters. `.27` had 98 Nerd Font entries installed but its Ghostty config contained only `term = xterm-256color` — no `font-family`, so Ghostty used its own **plain** JetBrains Mono, which has no icon glyphs. The tick shows because `✓` is an ordinary character; the Nerd icons are private-use codepoints that a plain font simply does not have.
I appended to `~/.config/ghostty/config` on `.27` (backup at `config.bak-icons`):
```
font-family = "JetBrainsMono Nerd Font"
font-size = 12
```
Reopen the terminal window on `.27` (or reload Ghostty's config) and the icons should appear. If they still do not, say so and I will switch the panels back to plain characters by default.
## Built since you left
- **`?` on every panel** — an overlay listing the keys that work there, and nothing else. `esc` or `?` closes it.
- **`/` filter on the board** — type part of an identifier, a title or an assignee and the list narrows; the count and the chosen task follow the filter, so what is highlighted is always what would be acted on. The header shows `filter: CHANGE▌` while you type.
- **`u` unblock** — on a task whose blocker has finished but which still says blocked, one key sets it to todo with a comment. That is the state you had to hand-fix earlier.
- **Mouse clicks on task rows** — one click-zone manager created once and the whole view scanned through it, as the rules require. Clicking a row selects it and loads its detail. I cannot verify a click from here, so treat it as untested until you try it.
- **`B` on Repair** — add only the panes that are missing, never closing any. Safe to press from inside a pane, unlike `R`.
All seven panes rebuilt cleanly afterwards, and every screen drew.
Screenshots refreshed in `/tmp/mockups/`. Committed as `09e32da`.
## What I would build next, in this order
1. **Needs-you cards that act** — each card gets its own key: unblock, assign, open the conversation. Right now the panel tells you what needs you but you act in another panel.
2. **A settings tab that writes** — the verify command per project, edited in place, which is the one thing the Author form cannot express today.
3. **Diff beside the conversation** — the wide-screen arrangement from the spec: tree, thread, diff on one workspace, so a claim and its code sit together.
4. **Search across the project** — one key that filters tasks, comments and activity together, for when you remember a phrase but not the task.
Tell me which of those you want first, and whether the icons appear after reopening the terminal.
## Evidence
- Confidence: 85%
- Reason: Concrete recurring build errors and their causes discovered during implementation.; enriched with final-answer detail because persistence should be reusable
## Related
- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]]
## Related learned memories <!-- same-turn-2026-11-10 05:00:01 -->
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Blank Nerd Font icons: missing font-family in Ghostty config on host .27]]
- observation: [[packs/paperclip-harness/60-observations/paperclip-harness-tui-editor-pitfall-illegal-rune-literal-af|paperclip-harness TUI editor pitfall: illegal rune literal after patching Go files]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-implemented-help-overlay-board-filter-unblock-key-row-mouse-|Implemented help overlay, board filter, unblock key, row mouse zones, safe add-only Repair rebuild]]
- 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]]
- context: [[packs/paperclip-harness/20-context/tmp|.config]]

View File

@@ -651,3 +651,130 @@ Committed as `972d4c0`. Ready for you to poke at: the four things I would test f
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-tmp-2026-11-10-03-34-34|Rich discovery: tmp 2026-11-10 03:34:34]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-tmp-2026-11-10-03-34-34|Rich discovery: tmp 2026-11-10 03:34:34]]
- context: [[packs/paperclip-harness/20-context/tmp|tmp]] - context: [[packs/paperclip-harness/20-context/tmp|tmp]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|tmp repository structure and configuration patterns]] - observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|tmp repository structure and configuration patterns]]
---
## Update (2026-11-10 05:00:01)
Observed while debugging blank icons in the paperclip-harness TUI: the server-side panes were fine; the rendering host `.27` was the problem.
- `.27` has 98 Nerd Font files installed, but `~/.config/ghostty/config` contained only `term = xterm-256color`.
- With no `font-family`, Ghostty used its bundled plain JetBrains Mono, which has no Nerd Font private-use codepoints, so icons rendered blank.
- The checkmark `✓` still appeared because it is an ordinary Unicode character present in plain fonts.
Diagnostic rule: if ordinary glyphs render but Nerd icons are blank, inspect the *terminal emulator's* font config on the display host before touching the TUI rendering code.
Fix: append `font-family = "JetBrainsMono Nerd Font"` and `font-size = 12` to `~/.config/ghostty/config` (backup `config.bak-icons`); reopen the terminal or reload config.
## Detailed observations from completed turn
Found the real reason you see no icons, and it was not the server.
## Icons: the font was missing on `.27`, not `.13`
Your terminal on `.27` is what draws the characters. `.27` had 98 Nerd Font entries installed but its Ghostty config contained only `term = xterm-256color` — no `font-family`, so Ghostty used its own **plain** JetBrains Mono, which has no icon glyphs. The tick shows because `✓` is an ordinary character; the Nerd icons are private-use codepoints that a plain font simply does not have.
I appended to `~/.config/ghostty/config` on `.27` (backup at `config.bak-icons`):
```
font-family = "JetBrainsMono Nerd Font"
font-size = 12
```
Reopen the terminal window on `.27` (or reload Ghostty's config) and the icons should appear. If they still do not, say so and I will switch the panels back to plain characters by default.
## Built since you left
- **`?` on every panel** — an overlay listing the keys that work there, and nothing else. `esc` or `?` closes it.
- **`/` filter on the board** — type part of an identifier, a title or an assignee and the list narrows; the count and the chosen task follow the filter, so what is highlighted is always what would be acted on. The header shows `filter: CHANGE▌` while you type.
- **`u` unblock** — on a task whose blocker has finished but which still says blocked, one key sets it to todo with a comment. That is the state you had to hand-fix earlier.
- **Mouse clicks on task rows** — one click-zone manager created once and the whole view scanned through it, as the rules require. Clicking a row selects it and loads its detail. I cannot verify a click from here, so treat it as untested until you try it.
- **`B` on Repair** — add only the panes that are missing, never closing any. Safe to press from inside a pane, unlike `R`.
All seven panes rebuilt cleanly afterwards, and every screen drew.
Screenshots refreshed in `/tmp/mockups/`. Committed as `09e32da`.
## What I would build next, in this order
1. **Needs-you cards that act** — each card gets its own key: unblock, assign, open the conversation. Right now the panel tells you what needs you but you act in another panel.
2. **A settings tab that writes** — the verify command per project, edited in place, which is the one thing the Author form cannot express today.
3. **Diff beside the conversation** — the wide-screen arrangement from the spec: tree, thread, diff on one workspace, so a claim and its code sit together.
4. **Search across the project** — one key that filters tasks, comments and activity together, for when you remember a phrase but not the task.
Tell me which of those you want first, and whether the icons appear after reopening the terminal.
## Evidence
- Confidence: 90%
- Reason: Evidence-backed RCA and reusable diagnostic rule.; enriched with final-answer detail because persistence should be reusable
---
## Update (2026-11-10 05:00:01)
## Structural/configuration observations (2026-11-10 05:00:01)
Found the real reason you see no icons, and it was not the server.
## Icons: the font was missing on `.27`, not `.13`
Your terminal on `.27` is what draws the characters. `.27` had 98 Nerd Font entries installed but its Ghostty config contained only `term = xterm-256color` — no `font-family`, so Ghostty used its own **plain** JetBrains Mono, which has no icon glyphs. The tick shows because `✓` is an ordinary character; the Nerd icons are private-use codepoints that a plain font simply does not have.
I appended to `~/.config/ghostty/config` on `.27` (backup at `config.bak-icons`):
```
font-family = "JetBrainsMono Nerd Font"
font-size = 12
```
Reopen the terminal window on `.27` (or reload Ghostty's config) and the icons should appear. If they still do not, say so and I will switch the panels back to plain characters by default.
## Built since you left
- **`?` on every panel** — an overlay listing the keys that work there, and nothing else. `esc` or `?` closes it.
- **`/` filter on the board** — type part of an identifier, a title or an assignee and the list narrows; the count and the chosen task follow the filter, so what is highlighted is always what would be acted on. The header shows `filter: CHANGE▌` while you type.
- **`u` unblock** — on a task whose blocker has finished but which still says blocked, one key sets it to todo with a comment. That is the state you had to hand-fix earlier.
- **Mouse clicks on task rows** — one click-zone manager created once and the whole view scanned through it, as the rules require. Clicking a row selects it and loads its detail. I cannot verify a click from here, so treat it as untested until you try it.
- **`B` on Repair** — add only the panes that are missing, never closing any. Safe to press from inside a pane, unlike `R`.
All seven panes rebuilt cleanly afterwards, and every screen drew.
Screenshots refreshed in `/tmp/mockups/`. Committed as `09e32da`.
## What I would build next, in this order
1. **Needs-you cards that act** — each card gets its own key: unblock, assign, open the conversation. Right now the panel tells you what needs you but you act in another panel.
2. **A settings tab that writes** — the verify command per project, edited in place, which is the one thing the Author form cannot express today.
3. **Diff beside the conversation** — the wide-screen arrangement from the spec: tree, thread, diff on one workspace, so a claim and its code sit together.
4. **Search across the project** — one key that filters tasks, comments and activity together, for when you remember a phrase but not the task.
Tell me which of those you want first, and whether the icons appear after reopening the terminal.
## Source paths observed
- `paperclip-harness/tui/paperclip`
- `home/sam/paseo/projects/paperclip-harness/bin/harness`
- `run/current-system/sw/bin/git`
- `home/sam/.nix-profile/bin/go`
- `home/sam/.local/bin/ocr`
- `etc/profiles/per-user/sam/bin/tuios`
- `run/current-system/sw/bin/lazygit`
- `etc/profiles/per-user/sam/bin/hunk`
- `.config/ghostty/config`
## Evidence
- Confidence: 82%
- Reason: Captured factual repo structure/configuration patterns from rich discovery
## Related learned memories <!-- same-turn-2026-11-10 05:00:01 -->
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Blank Nerd Font icons: missing font-family in Ghostty config on host .27]]
- observation: [[packs/paperclip-harness/60-observations/paperclip-harness-tui-editor-pitfall-illegal-rune-literal-af|paperclip-harness TUI editor pitfall: illegal rune literal after patching Go files]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-implemented-help-overlay-board-filter-unblock-key-row-mouse-|Implemented help overlay, board filter, unblock key, row mouse zones, safe add-only Repair rebuild]]
- 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]]
- context: [[packs/paperclip-harness/20-context/tmp|.config]]

View File

@@ -0,0 +1,64 @@
---
type: session
id: session.paperclip-harness.paperclip-harness-tui-icon-font-fix-help-filter-unblock-safe
title: paperclip-harness TUI: icon font fix, help/filter/unblock, safe rebuild (commit 09e32da)
status: active
source_of_truth: false
freshness: current
last_reviewed: 2026-11-10
tags:
- pack/paperclip-harness
- agent-memory/session
- paperclip-harness
- tui
- nerd-fonts
- ghostty
- help-overlay
- task-filter
- unblock
- repair-pane
---
# paperclip-harness TUI: icon font fix, help/filter/unblock, safe rebuild (commit 09e32da)
Discovery snapshot for the `paperclip-harness` repo, TUI component (Go module `paperclip-harness/tui`, plus `paperclip-harness/tui/paperclip` subpackage with no tests).
Main symptom investigated: Nerd Font icons rendered as blanks in the terminal, but panes were fine on the server side. Root cause was the terminal host `.27`, not `.13`: `.27` had 98 Nerd Font entries installed but its Ghostty config (`~/.config/ghostty/config`) contained only `term = xterm-256color` — no `font-family` — so Ghostty fell back to plain JetBrains Mono, which lacks private-use icon codepoints. `✓` still rendered because it is an ordinary character.
Fix applied on `.27`: appended
```
font-family = "JetBrainsMono Nerd Font"
font-size = 12
```
to `~/.config/ghostty/config`, backup at `config.bak-icons`. Requires reopening the terminal window or reloading Ghostty config. Fallback plan if icons still missing: switch panels back to plain characters by default.
Features built this turn (committed as `09e32da Help on every panel, task filter, unblock key, mouse zones, safe add-only rebuild`):
- `?` overlay on every panel listing only the keys valid there; `esc` or `?` closes.
- `/` filter on the board: narrows by identifier, title, or assignee; count and chosen task follow the filter; header shows `filter: …▌` while typing.
- `u` unblock: for a task whose blocker finished but still says blocked, one key sets it to todo with a comment.
- Mouse clicks on task rows: a single click-zone manager created once and the whole view scanned through it; clicking a row selects it and loads detail. Untested interactively from the agent side.
- `B` on Repair: adds only missing panes, never closes any; safe to press from inside a pane (unlike `R` which rebuilds every pane).
Validation: all seven panes rebuilt cleanly and every screen drew; `go test ./...` for `paperclip-harness/tui` passed (tui 0.4–0.6s). Screenshots refreshed in `/tmp/mockups/`.
Environment/tool paths confirmed by the Repair pane checks: harness binary `/home/sam/paseo/projects/paperclip-harness/bin/harness`, git `/run/current-system/sw/bin/git`, go `/home/sam/.nix-profile/bin/go`, ocr `/home/sam/.local/bin/ocr`, tuios `/etc/profiles/per-user/sam/bin/tuios`, lazygit `/run/current-system/sw/bin/lazygit`, hunk `/etc/profiles/per-user/sam/bin/hunk`, 384 Nerd Font files.
Proposed next work, in the user-facing order: (1) needs-you cards that act directly (unblock/assign/open conversation), (2) a settings tab that writes the per-project verify command, (3) diff beside the conversation on wide screens, (4) cross-project search over tasks/comments/activity.
## Evidence
- Confidence: 85%
- Reason: Rich sanitized discovery snapshot with root cause, exact config fix, feature list, commit, validation, and proposed roadmap.
## Related
- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]]
## Related learned memories <!-- same-turn-2026-11-10 05:00:01 -->
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Blank Nerd Font icons: missing font-family in Ghostty config on host .27]]
- observation: [[packs/paperclip-harness/60-observations/paperclip-harness-tui-editor-pitfall-illegal-rune-literal-af|paperclip-harness TUI editor pitfall: illegal rune literal after patching Go files]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-implemented-help-overlay-board-filter-unblock-key-row-mouse-|Implemented help overlay, board filter, unblock key, row mouse zones, safe add-only Repair rebuild]]
- 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]]
- context: [[packs/paperclip-harness/20-context/tmp|.config]]

View File

@@ -0,0 +1,88 @@
---
type: session
id: session.paperclip-harness.rich-discovery-config-2026-11-10-05-00-01
title: Rich discovery: .config 2026-11-10 05:00:01
status: active
source_of_truth: false
freshness: current
last_reviewed: 2026-11-10
tags:
- pack/paperclip-harness
- agent-memory/session
- autolearn
- rich-discovery
- repo/config
---
# Rich discovery: .config 2026-11-10 05:00:01
Sanitized rich discovery snapshot captured automatically from the completed turn.
## Source paths observed
- `paperclip-harness/tui/paperclip`
- `home/sam/paseo/projects/paperclip-harness/bin/harness`
- `run/current-system/sw/bin/git`
- `home/sam/.nix-profile/bin/go`
- `home/sam/.local/bin/ocr`
- `etc/profiles/per-user/sam/bin/tuios`
- `run/current-system/sw/bin/lazygit`
- `etc/profiles/per-user/sam/bin/hunk`
- `.config/ghostty/config`
## Final answer snapshot
Found the real reason you see no icons, and it was not the server.
## Icons: the font was missing on `.27`, not `.13`
Your terminal on `.27` is what draws the characters. `.27` had 98 Nerd Font entries installed but its Ghostty config contained only `term = xterm-256color` — no `font-family`, so Ghostty used its own **plain** JetBrains Mono, which has no icon glyphs. The tick shows because `✓` is an ordinary character; the Nerd icons are private-use codepoints that a plain font simply does not have.
I appended to `~/.config/ghostty/config` on `.27` (backup at `config.bak-icons`):
```
font-family = "JetBrainsMono Nerd Font"
font-size = 12
```
Reopen the terminal window on `.27` (or reload Ghostty's config) and the icons should appear. If they still do not, say so and I will switch the panels back to plain characters by default.
## Built since you left
- **`?` on every panel** — an overlay listing the keys that work there, and nothing else. `esc` or `?` closes it.
- **`/` filter on the board** — type part of an identifier, a title or an assignee and the list narrows; the count and the chosen task follow the filter, so what is highlighted is always what would be acted on. The header shows `filter: CHANGE▌` while you type.
- **`u` unblock** — on a task whose blocker has finished but which still says blocked, one key sets it to todo with a comment. That is the state you had to hand-fix earlier.
- **Mouse clicks on task rows** — one click-zone manager created once and the whole view scanned through it, as the rules require. Clicking a row selects it and loads its detail. I cannot verify a click from here, so treat it as untested until you try it.
- **`B` on Repair** — add only the panes that are missing, never closing any. Safe to press from inside a pane, unlike `R`.
All seven panes rebuilt cleanly afterwards, and every screen drew.
Screenshots refreshed in `/tmp/mockups/`. Committed as `09e32da`.
## What I would build next, in this order
1. **Needs-you cards that act** — each card gets its own key: unblock, assign, open the conversation. Right now the panel tells you what needs you but you act in another panel.
2. **A settings tab that writes** — the verify command per project, edited in place, which is the one thing the Author form cannot express today.
3. **Diff beside the conversation** — the wide-screen arrangement from the spec: tree, thread, diff on one workspace, so a claim and its code sit together.
4. **Search across the project** — one key that filters tasks, comments and activity together, for when you remember a phrase but not the task.
Tell me which of those you want first, and whether the icons appear after reopening the terminal.
## 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:00:01 -->
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Blank Nerd Font icons: missing font-family in Ghostty config on host .27]]
- observation: [[packs/paperclip-harness/60-observations/paperclip-harness-tui-editor-pitfall-illegal-rune-literal-af|paperclip-harness TUI editor pitfall: illegal rune literal after patching Go files]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-implemented-help-overlay-board-filter-unblock-key-row-mouse-|Implemented help overlay, board filter, unblock key, row mouse zones, safe add-only Repair rebuild]]
- 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]]
- context: [[packs/paperclip-harness/20-context/tmp|.config]]

View File

@@ -53,15 +53,18 @@ TUIOS it has to close a box.
## The six workspaces ## The six workspaces
| workspace | shows | keys | Written the way we talk about them: **Workspace>pane**. `Board>board` means the board
program running in the Board workspace.
| Workspace>pane | shows | keys |
|---|---|---| |---|---|---|
| shell | a prompt in the project folder | — | | Shell>shell | a prompt in the project folder | — |
| board | tasks by group, the chosen task, what needs you | ↑↓ j k · enter (show everywhere) · a (assign) · tab | | Board>board | tasks by group · the chosen task · needs you | ↑↓ j k · enter · a · u · / · tab |
| work | runs, change, facts — tabs | [ ] tab · ↑↓ j k · r | | Work>work | runs · change · facts | [ ] tab · ↑↓ j k · g G |
| reply | the thread and a reply box | enter (write) · s (status to send) · esc | | Reply>reply | the task · the thread · the reply box | enter · s · esc |
| new | the form that makes a task | tab · enter · esc | | New task>new | the form · what a task needs · what will be sent | tab · enter · ctrl+n |
| project | folder, machine, session, costs, verify command | [ ] tab · ↑↓ j k | | Project>project | facts · costs · settings | [ ] tab · ↑↓ j k |
| repair | health checks, the live layout, the rebuild log | [ ] tab · ↑↓ j k · R | | Repair>repair | health · layout · rebuild log | [ ] tab · ↑↓ j k · R · B |
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

@@ -40,14 +40,17 @@ def pages(cfg, folder):
bin_path = BIN bin_path = BIN
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
# on the rail, so it is capitalised and says what the page is for; the pane
# keeps the program's own name. Written Workspace>pane when talking about it.
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))), ("board", "Board", "%s -screen board -project %s -folder %s" % (shlex.quote(bin_path), proj, shlex.quote(folder))),
("work", "work", "%s -screen work -folder %s" % (shlex.quote(bin_path), shlex.quote(folder))), ("work", "Work", "%s -screen work -folder %s" % (shlex.quote(bin_path), shlex.quote(folder))),
("reply", "reply", "%s -screen reply -folder %s" % (shlex.quote(bin_path), shlex.quote(folder))), ("reply", "Reply", "%s -screen reply -folder %s" % (shlex.quote(bin_path), shlex.quote(folder))),
("new", "new", "%s -screen new -project %s" % (shlex.quote(bin_path), 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))), ("project", "Project", "%s -screen project -folder %s" % (shlex.quote(bin_path), shlex.quote(folder))),
("repair", "repair", "%s -screen repair -folder %s" % (shlex.quote(bin_path), shlex.quote(folder))), ("repair", "Repair", "%s -screen repair -folder %s" % (shlex.quote(bin_path), shlex.quote(folder))),
] ]
@@ -77,10 +80,10 @@ def main():
name, workspace, folder, command, session, name, workspace, folder, command, session,
adopt_existing=(name == "shell"), adopt_existing=(name == "shell"),
) )
h.say(" %-6s %-8s workspace %s" % (name, result, ws)) h.say(" %-9s %-8s workspace %s" % (name, result, ws))
for number, name in tuios.arrange([p[0] 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 %-6s 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,15 +133,18 @@ 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 workspaces 1..N in this order, and name them. """Put these panes on workspaces 1..N in this order, and name the workspaces.
`order` is a list of (pane name, workspace name) pairs. The pane keeps its
own name; the workspace gets the name a person reads on the rail.
TUIOS numbering follows the order panes land in, and closing panes renumbers 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 the rest, so a rebuild can scatter the pages. Moving each pane once, at the
end, is what makes the rail readable and stable. end, is what makes the rail readable and stable.
""" """
moved = [] moved = []
for number, name in enumerate(order, start=1): for number, (pane, workspace) in enumerate(order, start=1):
win = next((w for w in windows(session) if (w.get("custom_name") or "") == name), None) win = next((w for w in windows(session) if (w.get("custom_name") or "") == pane), None)
if win is None: if win is None:
continue continue
if win.get("workspace") != number: if win.get("workspace") != number:
@@ -149,8 +152,8 @@ def arrange(order, session=None):
["tuios", "move-window", str(number), "-w", win["window_id"], "-s", session or SESSION], ["tuios", "move-window", str(number), "-w", win["window_id"], "-s", session or SESSION],
capture_output=True, text=True, timeout=30, check=False, capture_output=True, text=True, timeout=30, check=False,
) )
_name_workspace(number, name, session) _name_workspace(number, workspace, session)
moved.append((number, name)) moved.append((number, workspace))
return moved return moved

View File

@@ -332,9 +332,11 @@ func (c *Client) SetStatus(issueID, status, comment string) (Issue, error) {
} }
type Agent struct { type Agent struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Status string `json:"status"` Status string `json:"status"`
BudgetCents int `json:"budgetMonthlyCents"`
SpentCents int `json:"spentMonthlyCents"`
} }
// Agents lists the company's agents, used only to put a name on a task. // Agents lists the company's agents, used only to put a name on a task.

View File

@@ -311,12 +311,24 @@ func (m projectModel) costLines() []string {
} }
func (m projectModel) settingLines() []string { func (m projectModel) settingLines() []string {
out := []string{""} out := []string{"", groupHeading("this project's own file", 6, m.width-4)}
out = append(out, m.row("session", m.cfg["session"]))
out = append(out, m.row("machine", m.cfg["machine.host"]))
out = append(out, m.row("folder", m.cfg["machine.folder"]))
out = append(out, m.row("paperclip", m.cfg["paperclip.project"]))
out = append(out, m.row("watching", m.cfg["watching"])) out = append(out, m.row("watching", m.cfg["watching"]))
out = append(out, m.row("verify command", m.verifyCommand())) out = append(out, m.row("verify command", m.verifyCommand()))
out = append(out, "") out = append(out, "")
out = append(out, dimStyle().Render(" verify command: the first task whose description has one,")) out = append(out, dimStyle().Render(" .harness/harness.toml is the only thing this project keeps for itself."))
out = append(out, dimStyle().Render(" or .harness/verify.sh when the project keeps one.")) out = append(out, dimStyle().Render(" Paperclip owns tasks, blockers, comments, runs, cost and worktrees."))
out = append(out, "")
out = append(out, groupHeading("limits held by Paperclip", len(m.agents), m.width-4))
for _, a := range m.agents {
out = append(out, m.row(a.Name, fmt.Sprintf("%s · monthly budget %d cents · spent %d",
a.Status, a.BudgetCents, a.SpentCents)))
}
out = append(out, "", dimStyle().Render(" change a budget in Paperclip, not here: it is their fact, and a second"))
out = append(out, dimStyle().Render(" copy of it is how two screens start disagreeing."))
return out return out
} }

View File

@@ -339,6 +339,13 @@ func (m repairModel) healthLines() []string {
mutedStyle2().Render(truncateVisible(c.detail, max(10, m.width-30))))) mutedStyle2().Render(truncateVisible(c.detail, max(10, m.width-30)))))
} }
out = append(out, "") out = append(out, "")
// The one way to tell whether the terminal is drawing the font: show the
// glyphs themselves. Nothing else on this screen answers that question.
out = append(out, " "+mutedStyle2().Render("icons ")+
icons.Done+" "+icons.Running+" "+icons.Blocked+" "+icons.Review+" "+
icons.Branch+" "+icons.Folder+" "+icons.Cost+" "+
dimStyle().Render("if these are boxes, this terminal is not using a Nerd Font"))
out = append(out, "")
if failed == 0 { if failed == 0 {
out = append(out, " "+lipgloss.NewStyle().Foreground(lipgloss.Color(active.Success)).Render("everything checked passes")) out = append(out, " "+lipgloss.NewStyle().Foreground(lipgloss.Color(active.Success)).Render("everything checked passes"))
} else { } else {

View File

@@ -22,6 +22,7 @@ import (
type replyThreadMsg struct { type replyThreadMsg struct {
task paperclip.Issue task paperclip.Issue
comments []paperclip.Comment comments []paperclip.Comment
names map[string]string
} }
type replySentMsg struct{ task paperclip.Issue } type replySentMsg struct{ task paperclip.Issue }
@@ -33,6 +34,7 @@ type replyModel struct {
explicit bool explicit bool
task paperclip.Issue task paperclip.Issue
names map[string]string
thread []paperclip.Comment thread []paperclip.Comment
box string box string
inBox bool inBox bool
@@ -75,7 +77,13 @@ func (m replyModel) load() tea.Cmd {
if err != nil { if err != nil {
return errMsg{err} return errMsg{err}
} }
return replyThreadMsg{task: task, comments: cs} names := map[string]string{}
if agents, err := c.Agents(); err == nil {
for _, a := range agents {
names[a.ID] = a.Name
}
}
return replyThreadMsg{task: task, comments: cs, names: names}
} }
} }
@@ -192,6 +200,7 @@ func (m replyModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case replyThreadMsg: case replyThreadMsg:
m.task = msg.task m.task = msg.task
m.names = msg.names
wasEmpty := len(m.thread) wasEmpty := len(m.thread)
m.thread = msg.comments m.thread = msg.comments
m.readAt = time.Now() m.readAt = time.Now()
@@ -251,8 +260,10 @@ func (m replyModel) View() string {
} }
composerHeight := max(5, min(9, m.height/4)) composerHeight := max(5, min(9, m.height/4))
threadHeight := m.height - composerHeight - 2 headerHeight := 5
threadHeight := m.height - composerHeight - headerHeight - 2
headerPanel := panel{title: "the task", width: m.width, height: headerHeight}
thread := panel{ thread := panel{
title: fmt.Sprintf("thread · %s", fmtCount(len(m.thread), "message", "messages")), title: fmt.Sprintf("thread · %s", fmtCount(len(m.thread), "message", "messages")),
width: m.width, height: threadHeight, focused: !m.inBox, width: m.width, height: threadHeight, focused: !m.inBox,
@@ -305,12 +316,35 @@ func (m replyModel) View() string {
gap := max(2, m.width-lipgloss.Width(head)-lipgloss.Width(state)-1) gap := max(2, m.width-lipgloss.Width(head)-lipgloss.Width(state)-1)
return strings.Join([]string{ return strings.Join([]string{
head + strings.Repeat(" ", gap) + mutedStyle2().Render(state), head + strings.Repeat(" ", gap) + mutedStyle2().Render(state),
headerPanel.render(m.headerLines(m.width - 3)),
thread.render(lines[from:to]), thread.render(lines[from:to]),
composer.render(boxLines), composer.render(boxLines),
footer, footer,
}, "\n") }, "\n")
} }
// headerLines is the panel the conversation needed: what task this is, without
// having to go back to the Board to remember.
func (m replyModel) headerLines(width int) []string {
if m.task.Identifier == "" {
return []string{"", mutedStyle2().Render("no task chosen yet")}
}
who := m.names[m.task.AssigneeAgent]
if who == "" {
who = "nobody yet"
}
status, _ := shortStatus(m.task.Status)
line1 := " " + lipgloss.NewStyle().Foreground(lipgloss.Color(active.Text)).Bold(true).
Render(m.task.Identifier) + " " + status + " " + mutedStyle2().Render("assigned to "+who)
out := []string{"", line1}
out = append(out, " "+truncateVisible(m.task.Title, max(8, width-2)))
if cmd := verifyFromDescription(m.task.Description); cmd != "" {
out = append(out, " "+dimStyle().Render("done when ")+
lipgloss.NewStyle().Foreground(lipgloss.Color(active.Info)).Render(cmd))
}
return out
}
func (m replyModel) composerLines(width, height int) []string { func (m replyModel) composerLines(width, height int) []string {
text := m.box text := m.box
if text == "" { if text == "" {