Merge asks first; Work>work lists runs with a log tab; Shell says where everything is

This commit is contained in:
Sam
2026-11-11 12:58:37 +11:00
parent 7198c34e42
commit 049e25af70
14 changed files with 697 additions and 22 deletions

View File

@@ -220,3 +220,80 @@ TUIOS plugin libraries called out: PLOT, LOOM, TML, GO (prefer Go for in-TUI pan
- context: [[packs/paperclip-harness/20-context/tmp|tmp]] - context: [[packs/paperclip-harness/20-context/tmp|tmp]]
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|tmp deployment operations]] - runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|tmp deployment operations]]
- 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-11 01:02:31)
Repository: paperclip-harness (TUI workspace harness; Go terminal UI plus Python `bin/` tooling).
Key paths discovered this turn:
- `bin/lib/harness.py` — shared harness library; now prepends `~/.local/bin`, `~/.nix-profile/bin`, system profile and per-user profile to PATH before tool lookup (fixes merge pane not finding `ocr`).
- `bin/board` — workspace launcher; compares each pane's real working directory with the project folder and recreates (replaces) the `Shell>shell` pane when they differ, announcing it.
- `bin/merge` — performs a task merge; uses `ocr` for review; takes a lock so a second concurrent merge is refused.
- Go TUI code with `workModel.View()` (min size 80×24), key handling `esc/q/o`, `up/k`, `o`, `t`, `m`, `r`, `R`, `g/G`, help overlay via `helpOverlay`.
- `docs/TEST-PLAN.md` — rewritten to be keys-only (nine steps, no terminal commands); `Shell>shell` appears once as escape hatch.
Pane layout: tab bar `the work | runs | change | facts | review | merge`; workspace navigation uses `Work>work`, `Board>board`, `Shell>shell`, `Repair>cleanup`. Merge logs go to `/tmp/paperclip-harness-merge.log`. Worktrees live under `<project>/.paperclip/worktrees/<TASK-id>-<slug>` with branch `<TASK-id>-<slug>` rebased onto `main`. Issue IDs look like `AUD-###`.
## Detailed context from completed turn
Both complaints are fair, and both are now fixed in the program rather than in the instructions.
## Work>work selects its own task and merges
- **`o`** in `Work>work` opens a list of the project's tasks. Choose one and the runs, change, facts, review and merge tabs all switch to it. You no longer start in `Board>board`.
- **`m`** runs the merge for the task you are looking at, and the new **merge** tab shows it live:
```
merge of AUD-168 started 12:01:39
worktree …/worktrees/AUD-168-tidy-the-readme-introduction
rebased onto main
no verify command: this would pass on the agent's word
review saw nothing to review (0 files); recorded, not hidden
merged into main at 4a44473
merge finished 12:01:43
```
That is a real merge I ran from the pane a minute ago, with no shell. Starting it twice is safe — the merge takes a lock and the second is refused.
## Why the Shell step failed you
Two real bugs, not your memory:
1. The pane that merges inherited the pane's small `PATH`, so `bin/merge` could not find `ocr` and refused with `ocr is not installed`. Fixed at the source: `bin/lib/harness.py` now adds `~/.local/bin`, `~/.nix-profile/bin`, the system profile and your per-user profile before looking for anything.
2. `Shell>shell` was created in one folder and later adopted without checking. `bin/board` now compares the pane's real working directory with the project folder and **replaces** the shell pane when they differ, saying so.
## The principle you are pointing at
The terminal was creeping in as the normal path. It should not be. From now on:
- anything you do regularly is a key in a pane;
- `Shell>shell` exists for the things we deliberately do not wrap, and for emergencies;
- a test plan that needs me to write `git log` is a bad test plan.
`docs/TEST-PLAN.md` is rewritten accordingly: step 4 is now "press `m` and wait", and the whole plan contains no terminal commands at all. `Shell>shell` appears once, as the escape hatch it is.
## What I would do next, in your order
1. You run the plan as written (nine steps, keys only) and tell me where the screen and Paperclip disagree.
2. `Repair>cleanup` — sessions listed with age and pane count, kill only what you select, and never touching the Pi session files.
3. The Pi session browser — `pi --resume` in a tool pane, or the folder in Yazi.
Committed as `7198c34`; the eight workspaces are rebuilt and running.
## Evidence
- Confidence: 90%
- Reason: Concrete architecture and source-path details from the turn that a future agent needs to navigate the repo.; enriched with final-answer detail because persistence should be reusable
## Related learned memories <!-- same-turn-2026-11-11 01:02:31 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- decision: [[packs/paperclip-harness/50-decisions/paperclip-harness-ui-six-workspaces-spec-first-four-panel-li|Pane-first UX: repeated actions become keys, Shell only as escape hatch]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Implemented task picker, in-pane merge, PATH and shell-folder fixes (commit 7198c34)]]
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Merging a paperclip-harness task from the TUI and diagnosing merge failures]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-bin-2026-11-11-01-02-31|Rich discovery: bin 2026-11-11 01:02:31]]
- context: [[packs/paperclip-harness/20-context/tmp|bin]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|bin repository structure and configuration patterns]]

View File

@@ -768,3 +768,74 @@ Committed as `7b12b45`. Say go and I run that list end to end and report each re
- context: [[packs/paperclip-harness/20-context/tmp|tmp]] - context: [[packs/paperclip-harness/20-context/tmp|tmp]]
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|tmp deployment operations]] - runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|tmp deployment operations]]
- 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-11 01:02:31)
## Rich discovery update (2026-11-11 01:02:31)
Both complaints are fair, and both are now fixed in the program rather than in the instructions.
## Work>work selects its own task and merges
- **`o`** in `Work>work` opens a list of the project's tasks. Choose one and the runs, change, facts, review and merge tabs all switch to it. You no longer start in `Board>board`.
- **`m`** runs the merge for the task you are looking at, and the new **merge** tab shows it live:
```
merge of AUD-168 started 12:01:39
worktree …/worktrees/AUD-168-tidy-the-readme-introduction
rebased onto main
no verify command: this would pass on the agent's word
review saw nothing to review (0 files); recorded, not hidden
merged into main at 4a44473
merge finished 12:01:43
```
That is a real merge I ran from the pane a minute ago, with no shell. Starting it twice is safe — the merge takes a lock and the second is refused.
## Why the Shell step failed you
Two real bugs, not your memory:
1. The pane that merges inherited the pane's small `PATH`, so `bin/merge` could not find `ocr` and refused with `ocr is not installed`. Fixed at the source: `bin/lib/harness.py` now adds `~/.local/bin`, `~/.nix-profile/bin`, the system profile and your per-user profile before looking for anything.
2. `Shell>shell` was created in one folder and later adopted without checking. `bin/board` now compares the pane's real working directory with the project folder and **replaces** the shell pane when they differ, saying so.
## The principle you are pointing at
The terminal was creeping in as the normal path. It should not be. From now on:
- anything you do regularly is a key in a pane;
- `Shell>shell` exists for the things we deliberately do not wrap, and for emergencies;
- a test plan that needs me to write `git log` is a bad test plan.
`docs/TEST-PLAN.md` is rewritten accordingly: step 4 is now "press `m` and wait", and the whole plan contains no terminal commands at all. `Shell>shell` appears once, as the escape hatch it is.
## What I would do next, in your order
1. You run the plan as written (nine steps, keys only) and tell me where the screen and Paperclip disagree.
2. `Repair>cleanup` — sessions listed with age and pane count, kill only what you select, and never touching the Pi session files.
3. The Pi session browser — `pi --resume` in a tool pane, or the folder in Yazi.
Committed as `7198c34`; the eight workspaces are rebuilt and running.
## Source paths observed
- `tmp/pc-sandbox-2026-11-10/.paperclip/worktrees/AUD-168-tidy-the-rea`
- `bin/lib/harness.py`
## Evidence
- Confidence: 84%
- Reason: Update component/repository context with detailed discovered structure and configuration
## Related learned memories <!-- same-turn-2026-11-11 01:02:31 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- decision: [[packs/paperclip-harness/50-decisions/paperclip-harness-ui-six-workspaces-spec-first-four-panel-li|Pane-first UX: repeated actions become keys, Shell only as escape hatch]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Implemented task picker, in-pane merge, PATH and shell-folder fixes (commit 7198c34)]]
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Merging a paperclip-harness task from the TUI and diagnosing merge failures]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-bin-2026-11-11-01-02-31|Rich discovery: bin 2026-11-11 01:02:31]]
- context: [[packs/paperclip-harness/20-context/tmp|bin]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|bin repository structure and configuration patterns]]

View File

@@ -552,3 +552,37 @@ Committed as `77014d0` — documentation only, nothing in the running layout cha
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-pi-2026-11-10-21-03-21|Rich discovery: .pi 2026-11-10 21:03:21]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-pi-2026-11-10-21-03-21|Rich discovery: .pi 2026-11-10 21:03:21]]
- context: [[packs/paperclip-harness/20-context/home|.pi]] - context: [[packs/paperclip-harness/20-context/home|.pi]]
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|.pi deployment operations]] - runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|.pi deployment operations]]
---
## Update (2026-11-11 01:02:31)
Completed work in paperclip-harness, committed as `7198c34` ("Work>work chooses its own task and merges from the interface; panes find their tools; shell follows the project folder"); eight workspaces rebuilt and running.
Deliverables:
- `o` in `Work>work` opens "choose a task to look at" list; selecting a task retargets all five tabs (runs, change, facts, review, merge).
- `m` starts merge for the focused task; new merge tab streams live log. Verified real merge of AUD-168 into main at `4a44473` (log: rebased, no verify command warning, "review saw nothing to review (0 files); recorded, not hidden").
- Merge is idempotent-safe: takes a lock; second start refused.
- `bin/lib/harness.py`: prepends `~/.local/bin`, `~/.nix-profile/bin`, system profile, per-user profile so `ocr` is found (fixes "refused: ocr is not installed", exit status 2).
- `bin/board`: replaces mismatched `Shell>shell` pane when its cwd differs from project folder, with a message.
- `docs/TEST-PLAN.md` rewritten: step 4 is now "press `m` and wait"; no terminal commands anywhere.
Validation: `build=0`, `syntax ok`, real merge succeeded.
Follow-ups proposed (in order): user runs nine-step keys-only plan and reports disagreements; `Repair>cleanup` (list sessions with age and pane count, kill only selected, never touch Pi session files); Pi session browser via `pi --resume` in a tool pane or the folder in Yazi.
## Evidence
- Confidence: 90%
- Reason: Completed implementation with concrete artifacts, validation and follow-ups.
## Related learned memories <!-- same-turn-2026-11-11 01:02:31 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- decision: [[packs/paperclip-harness/50-decisions/paperclip-harness-ui-six-workspaces-spec-first-four-panel-li|Pane-first UX: repeated actions become keys, Shell only as escape hatch]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Implemented task picker, in-pane merge, PATH and shell-folder fixes (commit 7198c34)]]
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Merging a paperclip-harness task from the TUI and diagnosing merge failures]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-bin-2026-11-11-01-02-31|Rich discovery: bin 2026-11-11 01:02:31]]
- context: [[packs/paperclip-harness/20-context/tmp|bin]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|bin repository structure and configuration patterns]]

View File

@@ -115,3 +115,80 @@ Consequences / review caveats:
- session: [[packs/paperclip-harness/80-sessions/paperclip-harness-reverted-pane-split-interface-gap-discover|Paperclip-harness — reverted pane split, interface gap discovery, and PLAN-INTERFACE roadmap]] - session: [[packs/paperclip-harness/80-sessions/paperclip-harness-reverted-pane-split-interface-gap-discover|Paperclip-harness — reverted pane split, interface gap discovery, and PLAN-INTERFACE roadmap]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-home-2026-11-10-05-41-48|Rich discovery: home 2026-11-10 05:41:48]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-home-2026-11-10-05-41-48|Rich discovery: home 2026-11-10 05:41:48]]
- context: [[packs/paperclip-harness/20-context/home|home]] - context: [[packs/paperclip-harness/20-context/home|home]]
---
## Update (2026-11-11 01:02:31)
Decision (in paperclip-harness): the terminal must not be the normal path for routine work.
Rules:
- Anything done regularly is a key inside a pane (e.g. `o` to pick a task, `m` to merge).
- `Shell>shell` exists only for deliberately-unwrapped actions and emergencies.
- A test plan that requires the user to type `git log` is considered a bad test plan.
Context: users were falling back to the shell to pick tasks and merge, and the Shell step failed because of a small inherited `PATH` (missing `ocr`) and a stale shell pane created in a different folder.
Consequences: `Work>work` gained `o` (task picker switches runs/change/facts/review/merge tabs) and `m` (merge with live merge tab); `docs/TEST-PLAN.md` rewritten keys-only; PATH and shell-folder bugs fixed in `bin/lib/harness.py` and `bin/board`.
## Detailed decision context from completed turn
Both complaints are fair, and both are now fixed in the program rather than in the instructions.
## Work>work selects its own task and merges
- **`o`** in `Work>work` opens a list of the project's tasks. Choose one and the runs, change, facts, review and merge tabs all switch to it. You no longer start in `Board>board`.
- **`m`** runs the merge for the task you are looking at, and the new **merge** tab shows it live:
```
merge of AUD-168 started 12:01:39
worktree …/worktrees/AUD-168-tidy-the-readme-introduction
rebased onto main
no verify command: this would pass on the agent's word
review saw nothing to review (0 files); recorded, not hidden
merged into main at 4a44473
merge finished 12:01:43
```
That is a real merge I ran from the pane a minute ago, with no shell. Starting it twice is safe — the merge takes a lock and the second is refused.
## Why the Shell step failed you
Two real bugs, not your memory:
1. The pane that merges inherited the pane's small `PATH`, so `bin/merge` could not find `ocr` and refused with `ocr is not installed`. Fixed at the source: `bin/lib/harness.py` now adds `~/.local/bin`, `~/.nix-profile/bin`, the system profile and your per-user profile before looking for anything.
2. `Shell>shell` was created in one folder and later adopted without checking. `bin/board` now compares the pane's real working directory with the project folder and **replaces** the shell pane when they differ, saying so.
## The principle you are pointing at
The terminal was creeping in as the normal path. It should not be. From now on:
- anything you do regularly is a key in a pane;
- `Shell>shell` exists for the things we deliberately do not wrap, and for emergencies;
- a test plan that needs me to write `git log` is a bad test plan.
`docs/TEST-PLAN.md` is rewritten accordingly: step 4 is now "press `m` and wait", and the whole plan contains no terminal commands at all. `Shell>shell` appears once, as the escape hatch it is.
## What I would do next, in your order
1. You run the plan as written (nine steps, keys only) and tell me where the screen and Paperclip disagree.
2. `Repair>cleanup` — sessions listed with age and pane count, kill only what you select, and never touching the Pi session files.
3. The Pi session browser — `pi --resume` in a tool pane, or the folder in Yazi.
Committed as `7198c34`; the eight workspaces are rebuilt and running.
## Evidence
- Confidence: 90%
- Reason: Explicit durable product/UX convention chosen by the user/agent, with rationale and consequences.; enriched with final-answer detail because persistence should be reusable
## Related learned memories <!-- same-turn-2026-11-11 01:02:31 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- decision: [[packs/paperclip-harness/50-decisions/paperclip-harness-ui-six-workspaces-spec-first-four-panel-li|Pane-first UX: repeated actions become keys, Shell only as escape hatch]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Implemented task picker, in-pane merge, PATH and shell-folder fixes (commit 7198c34)]]
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Merging a paperclip-harness task from the TUI and diagnosing merge failures]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-bin-2026-11-11-01-02-31|Rich discovery: bin 2026-11-11 01:02:31]]
- context: [[packs/paperclip-harness/20-context/tmp|bin]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|bin repository structure and configuration patterns]]

View File

@@ -2223,3 +2223,74 @@ Committed as `678c497`. `AUD-168` is running now if you want to watch it in `Age
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-agents-2026-11-11-00-28-33|Rich discovery: agents 2026-11-11 00:28:33]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-agents-2026-11-11-00-28-33|Rich discovery: agents 2026-11-11 00:28:33]]
- context: [[packs/paperclip-harness/20-context/agents|agents]] - context: [[packs/paperclip-harness/20-context/agents|agents]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|agents repository structure and configuration patterns]] - observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|agents repository structure and configuration patterns]]
---
## Update (2026-11-11 01:02:31)
## Structural/configuration observations (2026-11-11 01:02:31)
Both complaints are fair, and both are now fixed in the program rather than in the instructions.
## Work>work selects its own task and merges
- **`o`** in `Work>work` opens a list of the project's tasks. Choose one and the runs, change, facts, review and merge tabs all switch to it. You no longer start in `Board>board`.
- **`m`** runs the merge for the task you are looking at, and the new **merge** tab shows it live:
```
merge of AUD-168 started 12:01:39
worktree …/worktrees/AUD-168-tidy-the-readme-introduction
rebased onto main
no verify command: this would pass on the agent's word
review saw nothing to review (0 files); recorded, not hidden
merged into main at 4a44473
merge finished 12:01:43
```
That is a real merge I ran from the pane a minute ago, with no shell. Starting it twice is safe — the merge takes a lock and the second is refused.
## Why the Shell step failed you
Two real bugs, not your memory:
1. The pane that merges inherited the pane's small `PATH`, so `bin/merge` could not find `ocr` and refused with `ocr is not installed`. Fixed at the source: `bin/lib/harness.py` now adds `~/.local/bin`, `~/.nix-profile/bin`, the system profile and your per-user profile before looking for anything.
2. `Shell>shell` was created in one folder and later adopted without checking. `bin/board` now compares the pane's real working directory with the project folder and **replaces** the shell pane when they differ, saying so.
## The principle you are pointing at
The terminal was creeping in as the normal path. It should not be. From now on:
- anything you do regularly is a key in a pane;
- `Shell>shell` exists for the things we deliberately do not wrap, and for emergencies;
- a test plan that needs me to write `git log` is a bad test plan.
`docs/TEST-PLAN.md` is rewritten accordingly: step 4 is now "press `m` and wait", and the whole plan contains no terminal commands at all. `Shell>shell` appears once, as the escape hatch it is.
## What I would do next, in your order
1. You run the plan as written (nine steps, keys only) and tell me where the screen and Paperclip disagree.
2. `Repair>cleanup` — sessions listed with age and pane count, kill only what you select, and never touching the Pi session files.
3. The Pi session browser — `pi --resume` in a tool pane, or the folder in Yazi.
Committed as `7198c34`; the eight workspaces are rebuilt and running.
## Source paths observed
- `tmp/pc-sandbox-2026-11-10/.paperclip/worktrees/AUD-168-tidy-the-rea`
- `bin/lib/harness.py`
## Evidence
- Confidence: 82%
- Reason: Captured factual repo structure/configuration patterns from rich discovery
## Related learned memories <!-- same-turn-2026-11-11 01:02:31 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- decision: [[packs/paperclip-harness/50-decisions/paperclip-harness-ui-six-workspaces-spec-first-four-panel-li|Pane-first UX: repeated actions become keys, Shell only as escape hatch]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Implemented task picker, in-pane merge, PATH and shell-folder fixes (commit 7198c34)]]
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Merging a paperclip-harness task from the TUI and diagnosing merge failures]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-bin-2026-11-11-01-02-31|Rich discovery: bin 2026-11-11 01:02:31]]
- context: [[packs/paperclip-harness/20-context/tmp|bin]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|bin repository structure and configuration patterns]]

View File

@@ -709,3 +709,86 @@ Committed as `e44b0df`, documentation only — the running layout is untouched.
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-paperclip-2026-11-10-21-58-38|Rich discovery: .paperclip 2026-11-10 21:58:38]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-paperclip-2026-11-10-21-58-38|Rich discovery: .paperclip 2026-11-10 21:58:38]]
- context: [[packs/paperclip-harness/20-context/paperclipai|.paperclip]] - context: [[packs/paperclip-harness/20-context/paperclipai|.paperclip]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|.paperclip repository structure and configuration patterns]] - observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|.paperclip repository structure and configuration patterns]]
---
## Update (2026-11-11 01:02:31)
Trigger: need to review or merge a completed task in paperclip-harness.
Procedure:
1. In `Work>work` press `o` and choose the task from the list (IDs like `AUD-168`). All tabs switch to it.
2. Check the `review` and `change` tabs.
3. Press `m` to merge; watch the `merge` tab for live progress. Log file: `/tmp/paperclip-harness-merge.log`.
4. Success line looks like `merged into main at <sha>` then `merge finished <time>`.
Expected log details: worktree `<project>/.paperclip/worktrees/<TASK-id>-<slug>`, branch `<TASK-id>-<slug>` rebased onto `main`; warnings `no verify command` and `review saw nothing to review (0 files); recorded, not hidden` are informational.
Troubleshooting:
- `refused: ocr is not installed` then `merge stopped: exit status 2` → PATH too small. `bin/lib/harness.py` should add `~/.local/bin`, `~/.nix-profile/bin`, system profile, per-user profile; verify `ocr` resolves in the environment the pane runs in.
- Second merge attempt while one is running is refused by the merge lock — expected, not an error.
- If `Shell>shell` behaves oddly, `bin/board` should detect cwd ≠ project folder and recreate that pane.
Rollback: merges land on `main`; revert with git as usual from the recorded sha.
## Detailed procedure from completed turn
Both complaints are fair, and both are now fixed in the program rather than in the instructions.
## Work>work selects its own task and merges
- **`o`** in `Work>work` opens a list of the project's tasks. Choose one and the runs, change, facts, review and merge tabs all switch to it. You no longer start in `Board>board`.
- **`m`** runs the merge for the task you are looking at, and the new **merge** tab shows it live:
```
merge of AUD-168 started 12:01:39
worktree …/worktrees/AUD-168-tidy-the-readme-introduction
rebased onto main
no verify command: this would pass on the agent's word
review saw nothing to review (0 files); recorded, not hidden
merged into main at 4a44473
merge finished 12:01:43
```
That is a real merge I ran from the pane a minute ago, with no shell. Starting it twice is safe — the merge takes a lock and the second is refused.
## Why the Shell step failed you
Two real bugs, not your memory:
1. The pane that merges inherited the pane's small `PATH`, so `bin/merge` could not find `ocr` and refused with `ocr is not installed`. Fixed at the source: `bin/lib/harness.py` now adds `~/.local/bin`, `~/.nix-profile/bin`, the system profile and your per-user profile before looking for anything.
2. `Shell>shell` was created in one folder and later adopted without checking. `bin/board` now compares the pane's real working directory with the project folder and **replaces** the shell pane when they differ, saying so.
## The principle you are pointing at
The terminal was creeping in as the normal path. It should not be. From now on:
- anything you do regularly is a key in a pane;
- `Shell>shell` exists for the things we deliberately do not wrap, and for emergencies;
- a test plan that needs me to write `git log` is a bad test plan.
`docs/TEST-PLAN.md` is rewritten accordingly: step 4 is now "press `m` and wait", and the whole plan contains no terminal commands at all. `Shell>shell` appears once, as the escape hatch it is.
## What I would do next, in your order
1. You run the plan as written (nine steps, keys only) and tell me where the screen and Paperclip disagree.
2. `Repair>cleanup` — sessions listed with age and pane count, kill only what you select, and never touching the Pi session files.
3. The Pi session browser — `pi --resume` in a tool pane, or the folder in Yazi.
Committed as `7198c34`; the eight workspaces are rebuilt and running.
## Evidence
- Confidence: 85%
- Reason: Repeatable procedure plus failure modes with concrete strings a future agent can match.; enriched with final-answer detail because persistence should be reusable
## Related learned memories <!-- same-turn-2026-11-11 01:02:31 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- decision: [[packs/paperclip-harness/50-decisions/paperclip-harness-ui-six-workspaces-spec-first-four-panel-li|Pane-first UX: repeated actions become keys, Shell only as escape hatch]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Implemented task picker, in-pane merge, PATH and shell-folder fixes (commit 7198c34)]]
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Merging a paperclip-harness task from the TUI and diagnosing merge failures]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-bin-2026-11-11-01-02-31|Rich discovery: bin 2026-11-11 01:02:31]]
- context: [[packs/paperclip-harness/20-context/tmp|bin]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|bin repository structure and configuration patterns]]

View File

@@ -0,0 +1,90 @@
---
type: session
id: session.paperclip-harness.rich-discovery-bin-2026-11-11-01-02-31
title: Rich discovery: bin 2026-11-11 01:02:31
status: active
source_of_truth: false
freshness: current
last_reviewed: 2026-11-11
tags:
- pack/paperclip-harness
- agent-memory/session
- autolearn
- rich-discovery
- repo/bin
---
# Rich discovery: bin 2026-11-11 01:02:31
Sanitized rich discovery snapshot captured automatically from the completed turn.
## Source paths observed
- `tmp/pc-sandbox-2026-11-10/.paperclip/worktrees/AUD-168-tidy-the-rea`
- `bin/lib/harness.py`
## Final answer snapshot
Both complaints are fair, and both are now fixed in the program rather than in the instructions.
## Work>work selects its own task and merges
- **`o`** in `Work>work` opens a list of the project's tasks. Choose one and the runs, change, facts, review and merge tabs all switch to it. You no longer start in `Board>board`.
- **`m`** runs the merge for the task you are looking at, and the new **merge** tab shows it live:
```
merge of AUD-168 started 12:01:39
worktree …/worktrees/AUD-168-tidy-the-readme-introduction
rebased onto main
no verify command: this would pass on the agent's word
review saw nothing to review (0 files); recorded, not hidden
merged into main at 4a44473
merge finished 12:01:43
```
That is a real merge I ran from the pane a minute ago, with no shell. Starting it twice is safe — the merge takes a lock and the second is refused.
## Why the Shell step failed you
Two real bugs, not your memory:
1. The pane that merges inherited the pane's small `PATH`, so `bin/merge` could not find `ocr` and refused with `ocr is not installed`. Fixed at the source: `bin/lib/harness.py` now adds `~/.local/bin`, `~/.nix-profile/bin`, the system profile and your per-user profile before looking for anything.
2. `Shell>shell` was created in one folder and later adopted without checking. `bin/board` now compares the pane's real working directory with the project folder and **replaces** the shell pane when they differ, saying so.
## The principle you are pointing at
The terminal was creeping in as the normal path. It should not be. From now on:
- anything you do regularly is a key in a pane;
- `Shell>shell` exists for the things we deliberately do not wrap, and for emergencies;
- a test plan that needs me to write `git log` is a bad test plan.
`docs/TEST-PLAN.md` is rewritten accordingly: step 4 is now "press `m` and wait", and the whole plan contains no terminal commands at all. `Shell>shell` appears once, as the escape hatch it is.
## What I would do next, in your order
1. You run the plan as written (nine steps, keys only) and tell me where the screen and Paperclip disagree.
2. `Repair>cleanup` — sessions listed with age and pane count, kill only what you select, and never touching the Pi session files.
3. The Pi session browser — `pi --resume` in a tool pane, or the folder in Yazi.
Committed as `7198c34`; the eight workspaces are rebuilt and running.
## Evidence
- Confidence: 86%
- Reason: Preserve full sanitized discovery detail from a rich investigation turn
## Related
- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]]
## Related learned memories <!-- same-turn-2026-11-11 01:02:31 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- decision: [[packs/paperclip-harness/50-decisions/paperclip-harness-ui-six-workspaces-spec-first-four-panel-li|Pane-first UX: repeated actions become keys, Shell only as escape hatch]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Implemented task picker, in-pane merge, PATH and shell-folder fixes (commit 7198c34)]]
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Merging a paperclip-harness task from the TUI and diagnosing merge failures]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-bin-2026-11-11-01-02-31|Rich discovery: bin 2026-11-11 01:02:31]]
- context: [[packs/paperclip-harness/20-context/tmp|bin]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|bin repository structure and configuration patterns]]

View File

@@ -58,9 +58,9 @@ program running in the Board workspace.
| Workspace>pane | shows | keys | | Workspace>pane | shows | keys |
|---|---|---| |---|---|---|
| Shell>shell | a prompt in the project folder: the pane that never exits, and the escape hatch for anything no panel covers | — | | Shell>shell | a prompt in the project folder, with a box saying where the project's files, worktrees and Pi sessions are | — |
| Board>board | tasks by group · the chosen task · needs you | ↑↓ j k · enter · a · u · / · t (tools) · tab | | Board>board | tasks by group · the chosen task · needs you | ↑↓ j k · enter · a · u · / · t (tools) · tab |
| Work>work | runs · change · facts · review · merge | [ ] tab · ↑↓ j k · g G · o (choose task) · m (merge) · t (tools) | | Work>work | runs · change · facts · review · log · merge | [ ] tab · ↑↓ j k · enter (open a run) · o (choose task) · m (merge, asks first) · t (tools) |
| Reply>reply | the task · the thread · the reply box | enter · s · esc | | Reply>reply | the task · the thread · the reply box | enter · s · esc |
| New task>new | the form · what a task needs · what will be sent | tab · enter · ctrl+n | | New task>new | the form · what a task needs · what will be sent | tab · enter · ctrl+n |
| Project>project | overview · facts · costs · settings | [ ] tab · ↑↓ j k | | Project>project | overview · facts · costs · settings | [ ] tab · ↑↓ j k |

View File

@@ -44,7 +44,8 @@ def pages(cfg, folder):
# on the rail, so it is capitalised and says what the page is for; the pane # 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. # keeps the program's own name. Written Workspace>pane when talking about it.
task_pages = [ task_pages = [
("shell", "Shell", "exec zsh -i"), ("shell", "Shell", "%s %s; exec zsh -i" % (
shlex.quote(os.path.join(os.path.dirname(BIN), "shell-banner")), shlex.quote(folder))),
("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))),
("agents", "Agents", "%s -screen agents -folder %s" % (shlex.quote(bin_path), shlex.quote(folder))), ("agents", "Agents", "%s -screen agents -folder %s" % (shlex.quote(bin_path), shlex.quote(folder))),

Binary file not shown.

53
bin/shell-banner Executable file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""Print where a project's files are, then the shell takes over.
Shown in Shell>shell when it opens, because the first question there is always
"where am I". It reads the project's own file and nothing else.
"""
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "lib"))
import project as pj
def main():
folder = os.path.abspath(sys.argv[1] if len(sys.argv) > 1 else os.getcwd())
cfg = pj.load(folder)
home = os.path.expanduser("~")
rows = [
("project folder", folder),
("task worktrees", os.path.join(folder, ".paperclip", "worktrees")),
("this project's file", pj.path(folder)),
("harness commands", os.path.join(os.path.dirname(os.path.abspath(__file__)))),
("pi conversations", os.path.join(home, ".pi", "paperclips")),
("pi skills you own", os.path.join(home, ".agents", "skills")),
("paperclip data", os.path.join(home, ".paperclip", "instances", "default")),
("session", cfg.get("session", "")),
("watching", cfg.get("watching", "")),
]
inner = 70
label = max(len(k) for k, _ in rows) + 2
print("┌─ where things are " + "─" * (inner - 19) + "┐")
for k, v in rows:
room = inner - label - 2
if len(v) > room:
v = v[: room - 1] + "…"
line = " " + k.ljust(label) + v
print("│" + line.ljust(inner) + "│")
print("└" + "─" * inner + "┘")
print("""
the panes are the normal way to work. this shell is for the commands we do not wrap:
bin/merge <TASK> checks and merges one task (the Merge tab in Work>work does this)
bin/review <TASK> the review on its own
bin/board rebuild or repair the panes (or press R in Repair>repair)
bin/office <folder> adopt another folder as a project
""")
if __name__ == "__main__":
try:
main()
except Exception as exc: # a banner must never stop the shell opening
print("(no project file here: %s)" % exc)

View File

@@ -88,7 +88,21 @@ Go to `Work>work`. It follows the task you chose on the board.
No terminal. Stay in `Work>work`. No terminal. Stay in `Work>work`.
1. Press `m`. The **merge** tab opens by itself and shows what is happening: 1. Press `m`. It **asks first**, and tells you what it is about to do:
```
merge AUD-1xx?
what happens rebase onto the base branch
no verify command: the review decides
ocr reviews the change; a critical finding refuses the merge
merge, then re-run the checks on the base branch
y do it
esc or n do nothing
```
That is the approval step. Nothing merges until you press `y`.
2. Press `y`. The **merge** tab shows what is happening:
``` ```
merge of AUD-1xx started 12:01:39 merge of AUD-1xx started 12:01:39
@@ -99,7 +113,7 @@ No terminal. Stay in `Work>work`.
merge finished 12:01:43 merge finished 12:01:43
``` ```
2. Wait for `merged into main at <commit>`. That commit is the task's work, on the project's 3. Wait for `merged into main at <commit>`. That commit is the task's work, on the project's
base branch. base branch.
**Expected:** a green refusal line, not a red one. If it says `refused: the review found 1 **Expected:** a green refusal line, not a red one. If it says `refused: the review found 1
@@ -108,7 +122,13 @@ blocking item(s)`, the gate worked — the findings are on the task and in `Work
**Starting a merge twice by accident is safe**: the merge takes a lock and the second one is **Starting a merge twice by accident is safe**: the merge takes a lock and the second one is
refused with `another merge is in progress`. refused with `another merge is in progress`.
## Step 4b — choose a task without leaving Work ## Step 4b — look at one run, without leaving Work
In `Work>work` the **runs** tab lists every run of the chosen task. `↑`/`↓` to one and press
`Enter`: the **log** tab opens with what Pi actually did — the wake payload, its reasoning, its
tool calls. `[` goes back.
## Step 4c — choose a task without leaving Work
In `Work>work`, press `o`. A list of this project's tasks appears. `↑`/`↓` to one, `Enter`, and In `Work>work`, press `o`. A list of this project's tasks appears. `↑`/`↓` to one, `Enter`, and
the runs, change, facts, review and merge tabs all switch to it. You never have to go to the runs, change, facts, review and merge tabs all switch to it. You never have to go to

View File

@@ -37,7 +37,8 @@ what it shows is never a second copy of anything.
| `Work>change` | worktree, branch, base, commits, changed files, the diff | built | | `Work>change` | worktree, branch, base, commits, changed files, the diff | built |
| `Work>facts` | task, state, worktree path, branch, base, cost | built | | `Work>facts` | task, state, worktree path, branch, base, cost | built |
| `Work>review` | the verdicts recorded on this task: review findings, refused merges, merged results, blocking ones in red | built | | `Work>review` | the verdicts recorded on this task: review findings, refused merges, merged results, blocking ones in red | built |
| `Work>merge` | the last merge this screen ran, live, with `m` to start one — no terminal needed | built | | `Work>log` | the output of the run you picked on the runs tab | built |
| `Work>merge` | the last merge this screen ran, live; `m` asks first, then merges — no terminal needed | built |
## 4. Reply ## 4. Reply

View File

@@ -19,6 +19,12 @@ import (
"paperclip-harness/tui/paperclip" "paperclip-harness/tui/paperclip"
) )
type runLogMsg2 struct {
runID string
text string
err error
}
type pickTasksMsg struct { type pickTasksMsg struct {
issues []paperclip.Issue issues []paperclip.Issue
err error err error
@@ -46,9 +52,13 @@ type workModel struct {
diff string diff string
diffErr error diffErr error
err error err error
logRunID string
logText string
tab int tab int
scroll int scroll int
runAt int
confirm bool
tools bool tools bool
toolAt int toolAt int
@@ -63,7 +73,7 @@ type workModel struct {
width, height int width, height int
} }
var workTabs = []string{"runs", "change", "facts", "review", "merge"} var workTabs = []string{"runs", "change", "facts", "review", "log", "merge"}
func newWorkModel(c *paperclip.Client, folder, issueID string) workModel { func newWorkModel(c *paperclip.Client, folder, issueID string) workModel {
return workModel{client: c, folder: folder, issue: issueID, explicit: issueID != ""} return workModel{client: c, folder: folder, issue: issueID, explicit: issueID != ""}
@@ -73,6 +83,15 @@ func (m workModel) Init() tea.Cmd {
return tea.Batch(m.load(), tickEvery(5*time.Second), tea.WindowSize()) return tea.Batch(m.load(), tickEvery(5*time.Second), tea.WindowSize())
} }
// loadRunLog reads one run's output for the log tab.
func (m workModel) loadRunLog(runID string) tea.Cmd {
c := m.client
return func() tea.Msg {
text, err := c.RunLog(runID, 8<<20)
return runLogMsg2{runID: runID, text: text, err: err}
}
}
func (m workModel) load() tea.Cmd { func (m workModel) load() tea.Cmd {
c, folder := m.client, m.folder c, folder := m.client, m.folder
fixed, explicit := m.issue, m.explicit fixed, explicit := m.issue, m.explicit
@@ -136,6 +155,22 @@ func (m workModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, tea.Batch(m.load(), tickEvery(5*time.Second), tea.WindowSize()) return m, tea.Batch(m.load(), tickEvery(5*time.Second), tea.WindowSize())
case tea.KeyMsg: case tea.KeyMsg:
if m.confirm {
switch msg.String() {
case "y":
m.confirm = false
if err := startMerge(m.folder, m.task.Identifier); err != nil {
m.err = err
return m, nil
}
m.tab = 5
m.scroll = 1 << 30
return m, m.load()
case "esc", "n", "q":
m.confirm = false
}
return m, nil
}
if m.picking { if m.picking {
switch msg.String() { switch msg.String() {
case "esc", "q", "o": case "esc", "q", "o":
@@ -220,18 +255,12 @@ func (m workModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return pickTasksMsg{issues: mine} return pickTasksMsg{issues: mine}
} }
case "m": case "m":
// Merging is a decision, so it asks first and shows what it will do.
if m.task.Identifier == "" { if m.task.Identifier == "" {
return m, nil return m, nil
} }
// The merge runs on its own and writes what it does to a log this m.confirm = true
// tab reads, so nobody has to type a command or leave the screen.
if err := startMerge(m.folder, m.task.Identifier); err != nil {
m.err = err
return m, nil return m, nil
}
m.tab = 4
m.scroll = 1 << 30
return m, m.load()
case "?": case "?":
m.showHelp = true m.showHelp = true
return m, nil return m, nil
@@ -258,9 +287,15 @@ func (m workModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
} }
return m, nil return m, nil
case "up", "k": case "up", "k":
if m.tab == 0 && m.runAt > 0 {
m.runAt--
}
m.scroll-- m.scroll--
return m, nil return m, nil
case "down", "j": case "down", "j":
if m.tab == 0 && m.runAt < len(m.runs)-1 {
m.runAt++
}
m.scroll++ m.scroll++
return m, nil return m, nil
case "g": case "g":
@@ -269,10 +304,25 @@ func (m workModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case "G": case "G":
m.scroll = 1 << 30 m.scroll = 1 << 30
return m, nil return m, nil
case "enter":
if m.tab == 0 && m.runAt < len(m.runs) {
m.tab = 4
m.scroll = 1 << 30
return m, m.loadRunLog(m.runs[m.runAt].ID)
}
return m, nil
case "esc": case "esc":
return m, nil return m, nil
} }
case runLogMsg2:
m.logRunID = msg.runID
m.logText = msg.text
if msg.err != nil {
m.err = msg.err
}
return m, nil
case pickTasksMsg: case pickTasksMsg:
m.pickList = msg.issues m.pickList = msg.issues
m.pickAt = 0 m.pickAt = 0
@@ -341,11 +391,27 @@ func (m workModel) View() string {
case 3: case 3:
body = m.reviewLines(m.width - 4) body = m.reviewLines(m.width - 4)
case 4: case 4:
body = m.logLinesWork(m.width - 4)
case 5:
body = m.mergeLines(m.width - 4) body = m.mergeLines(m.width - 4)
default: default:
body = m.factsLines(m.width - 4) body = m.factsLines(m.width - 4)
} }
if m.confirm {
verify := "no verify command: the review decides"
if cmd := verifyFromDescription(m.task.Description); cmd != "" {
verify = "verify command: " + cmd
}
return helpOverlay("merge "+m.task.Identifier+"?", [][2]string{
{"what happens", "rebase onto the base branch"},
{"", verify},
{"", "ocr reviews the change; a critical finding refuses the merge"},
{"", "merge, then re-run the checks on the base branch"},
{"y", "do it"},
{"esc or n", "do nothing"},
}, m.width, m.height)
}
if m.picking { if m.picking {
entries := make([][2]string, 0, len(m.pickList)) entries := make([][2]string, 0, len(m.pickList))
for i, is := range m.pickList { for i, is := range m.pickList {
@@ -391,7 +457,7 @@ func (m workModel) View() string {
keyHint("↑↓ or j k", "scroll"), keyHint("↑↓ or j k", "scroll"),
keyHint("t", "open a tool pane"), keyHint("t", "open a tool pane"),
keyHint("o", "choose a task"), keyHint("o", "choose a task"),
keyHint("m", "merge this task"), keyHint("m", "merge this task (asks first)"),
keyHint("]", "tabs"), keyHint("]", "tabs"),
keyHint("r", "read again"), keyHint("r", "read again"),
keyHint("R", "rebuild every pane"), keyHint("R", "rebuild every pane"),
@@ -540,6 +606,37 @@ func (m workModel) reviewLines(width int) []string {
// mergeLines shows the last merge this screen started, read from the log the // mergeLines shows the last merge this screen started, read from the log the
// merge writes. Pressing m again is safe: the merge takes a lock. // merge writes. Pressing m again is safe: the merge takes a lock.
// logLinesWork shows the output of the run chosen on the runs tab.
func (m workModel) logLinesWork(width int) []string {
if m.logRunID == "" {
return []string{"", mutedStyle2().Render(" choose a run on the runs tab, then press enter")}
}
if m.logText == "" {
return []string{"", mutedStyle2().Render(" no output recorded for this run")}
}
out := []string{"", dimStyle().Render(" run " + short(m.logRunID)), ""}
entries := decodeRunLog(m.logText)
if len(entries) > 2000 {
entries = entries[len(entries)-2000:]
}
for _, e := range entries {
colour := active.Text
switch e.stream {
case "thinking":
colour = active.Muted
case "prompt":
colour = active.Info
case "tool":
colour = active.Warning
case "event":
colour = active.Overlay
}
out = append(out, " "+dimStyle().Render(e.when)+" "+
lipgloss.NewStyle().Foreground(lipgloss.Color(colour)).Render(truncateVisible(e.line, width-10)))
}
return out
}
func (m workModel) mergeLines(width int) []string { func (m workModel) mergeLines(width int) []string {
out := []string{"", dimStyle().Render(" " + mergeLogPath()), ""} out := []string{"", dimStyle().Render(" " + mergeLogPath()), ""}
for _, ln := range mergeLogTail(max(6, m.height-6)) { for _, ln := range mergeLogTail(max(6, m.height-6)) {