office: full --help, and --host copies the commands to the other machine and works from any folder on it

This commit is contained in:
Sam
2026-11-12 16:26:34 +11:00
parent 6c6bacc647
commit 3043e9dec1
7 changed files with 757 additions and 10 deletions

View File

@@ -368,3 +368,111 @@ Say which of those six you want first, and whether I should close the junk sessi
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Built and ran bin/triage (Mouse-1 labeller) and measured headroom provider]] - action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Built and ran bin/triage (Mouse-1 labeller) and measured headroom provider]]
- session: [[packs/paperclip-harness/80-sessions/paperclip-harness-session-snapshot-provider-feasibility-tria|Paperclip harness session snapshot: provider feasibility + triage tooling]] - session: [[packs/paperclip-harness/80-sessions/paperclip-harness-session-snapshot-provider-feasibility-tria|Paperclip harness session snapshot: provider feasibility + triage tooling]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-itself-2026-11-11-07-40-01|Rich discovery: itself 2026-11-11 07:40:01]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-itself-2026-11-11-07-40-01|Rich discovery: itself 2026-11-11 07:40:01]]
---
## Update (2026-11-12 04:50:42)
## Rich discovery update (2026-11-12 04:50:42)
Both clean tests are finished. Four defects came out, all four are fixed and verified on screen.
## Test 1 — `test-alpha`: passed
Folder → `office --new` → eight panes → task **AUD-172** created from `New task>new` → agent ran → `Work>change` showed the diff → `v` review → `m` then `y` merge.
```
36acb8d Merge AUD-172: Add a STATUS.md with one line
5eebc45 Add STATUS.md with status line
$ cat STATUS.md
alpha is running.
```
## Test 2 — `harness-repo`: passed
`office . --project harness-repo --session harness-repo` adopted the project without rewriting `.harness/`, task **AUD-173** was created, the agent appended one line to `README.md` and changed nothing else, and I did **not** merge. Cancelled with `c` then `y`: task `cancelled`, and `main` is untouched — `README.md` still has no "tested by the harness" line, last commit still `b81cb91`.
## The four defects, and what each one was
**1. Pi 1.5.0 broke every agent run.** This is the one that matters for what you were told about 1.5.
It is not the names. It is **which stream the list goes to**. Pi 1.5.0 prints `--list-models` on **stdout**; Pi 0.8x printed it on **stderr**. Paperclip's adapter reads stderr first, on purpose:
```js
// Pi outputs model list to stderr, but fall back to stdout for older versions
const output = result.stderr || result.stdout;
```
Pi 1.5.0 also puts five extension warnings on stderr, so the adapter parsed those, found no `provider model` rows, and refused every run with `Pi returned no models` — three runs died in 4–5 seconds with `errorCode: adapter_failed` and zero log bytes before I found it.
Fixed with a shim, `~/.local/share/paperclip-harness/wrappers/pi-paperclip.sh`, that puts the table back on stderr for that one flag and passes everything else through; Builder's `adapterConfig.command` points at it. The next run succeeded in 69 seconds.
**2. Panes restored after a reboot were "adopted" while running nothing.** TUIOS brings a session back with a fresh shell in each pane and keeps the pane names, so `board` matched them by name and reported `adopted`. A page that looks built and does nothing is the worst kind of wrong. Now `open_pane` reads `/proc/<pid>/cmdline` and adopts a pane only when the harness is what is running in it; otherwise it closes the pane and opens a fresh one. `shell` is exempt — it is a shell either way.
**3. `r` on `Board>board` refreshed the task list but not the chosen task.** So "last run running" stayed on screen long after the run finished. It now reloads both. Verified: `last run succeeded · on_demand · 15:32`.
**4. `office .` was refused** as if no folder had been given, because of the guard I added earlier. `.` is a real folder; only a missing argument is an error.
## Your question about model names — where they actually live
Measured on `.13`:
| File | What it holds |
|---|---|
| `~/.agents/models.json`, symlinked as `~/.pi/agent/models.json` | provider `omni` with **476 explicit names**, plus `openrouter` overrides |
| `~/.pi/agent/settings.json` | five providers: `omni`, `deepseek`, `openrouter`, `opencode-go`, `google` |
| `~/.pi/agent/models-store.json` | **discovered** catalogues per provider, with `etag` and `checkedAt` — e.g. `deepseek` → 2 models |
| a project's `.pi/` | no model names at all; in an agent workspace only `.pi/tasks` |
So names are global, not per project — an agent workspace cannot hold a stale copy. The new names are there and resolve: `omni/deepseek/deepseek-v4-flash`, `omni/deepseek/deepseek-v4-pro`, `omni/ds/…`, `omni/oc/deepseek-v4-flash-free`, `omni/opencode-go/…`, plus `deepseek deepseek-flash` and `deepseek deepseek-v4-pro` on the direct provider.
**omniroute** is the source of the 476 `omni` names, so a name missing from the router's `/v1/models` will not appear in Pi — and because `models-store.json` caches with an etag, a stale entry can hide a newly added name until the catalogue is refreshed. **headroom** keeps a separate LiteLLM registry and still has no entries for the router's ids, which is why it answers `502`; that is unrelated to Pi's storage, but it will need the same names when it is fixed.
That does not mean 1.5 did not fix your deepseek recognition — the names are recognised now. What 1.5 changed on this machine was the stream, and that is what broke Paperclip.
## Two open items, written into `docs/NEXT.md`
- **Cost is always zero for `pi_local` runs.** `cost-summary` says `costCents: 0`, `outputTokens: 0` over four real runs, and the run's `usageJson` is `null`. The adapter is not capturing Pi's token usage, so `Project>costs` and the cost line on `Board>the chosen task` are honest but useless, and the cost limit in your operating rules cannot bite.
- **`ocr` reviews code, not Markdown.** A documentation-only diff gives `status: skipped`, zero files selected. `bin/merge` handles that correctly ("nothing to review, recorded, not hidden"), but `Work>review` should say why instead of "nothing reviewable in the change (0 files)" while `Work>change` lists one file.
## Committed and pushed
`paperclip-harness` → `6c6bacc`, on Gitea. `.paperclip/` and `__pycache__/` are now ignored, which they were not.
Both sessions (`test-alpha`, `harness-repo`) run the fixed binary — I checked every pane's process, all eight in each are the harness.
The one thing I would do next, if you agree, is the cost capture: it is the only part of your operating rules that cannot be enforced today.
## Source paths observed
- `.paperclip/worktrees/AUD-173-add-a-line-to-readme`
- `run/current-system/sw/bin/bash`
- `.../packs/paperclip-harness/20-context/home.md`
- `.../20-context/paperclip-harness.md`
- `.../70-runbooks/tmp-deployment-operations.md`
- `bin/lib/__pycache__/tuios.cpython-314.pyc`
- `gitea.lab.audasmedia.com.au/sam/paperclip-harness.git`
- `home/sam/paseo/projects/pape`
- `run/current-system/sw/bin/zsh`
- `home/sam/paseo/projects/paperclip-harness/bin/harness`
- `.local/share/paperclip-harness/wrappers/pi-paperclip.sh`
- `.pi/agent/models.json`
- `.pi/agent/settings.json`
- `.pi/agent/models-store.json`
- `omni/deepseek/deepseek-v4-flash`
- `omni/deepseek/deepseek-v4-pro`
- `omni/oc/deepseek-v4-flash-free`
## Evidence
- Confidence: 84%
- Reason: Update component/repository context with detailed discovered structure and configuration
## Related learned memories <!-- same-turn-2026-11-12 04:50:42 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Pi 1.5.0 moves --list-models to stdout]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Fixed four harness defects, verified clean tests, pushed 6c6bacc]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-session-2026-11-12-04-50-42|Rich discovery: --session 2026-11-12 04:50:42]]
- context: [[packs/paperclip-harness/20-context/omni|--session]]

View File

@@ -877,3 +877,36 @@ I have stopped. Nothing further will be run or edited until you say what you wan
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Pi 1.5.0 model-table stdout/stderr mismatch breaks Paperclip pi_local adapter]] - observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Pi 1.5.0 model-table stdout/stderr mismatch breaks Paperclip pi_local adapter]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Unauthorized Paperclip work on test-alpha: AUD-172, review, adapter fix]] - action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Unauthorized Paperclip work on test-alpha: AUD-172, review, adapter fix]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-paseo-2026-11-12-04-37-46|Rich discovery: paseo 2026-11-12 04:37:46]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-paseo-2026-11-12-04-37-46|Rich discovery: paseo 2026-11-12 04:37:46]]
---
## Update (2026-11-12 04:50:42)
Repo: paperclip-harness (git remote gitea.lab.audasmedia.com.au/sam/paperclip-harness, branch main; HEAD after this turn 6c6bacc). Contains bin/harness (compiled binary), bin/board scripts, bin/lib (Python helpers e.g. tuios.*), docs/, and .paperclip/ worktree storage.
Key layout:
- Harness screens launched as `bin/harness -screen <name> -project <p>|-folder <f>`; screens: board, work, agents, reply, new, project, repair.
- Sessions run in TUIOS panes; TUIOS restores panes with fresh shells on reboot but keeps pane names.
- `.paperclip/` holds worktrees (now in .gitignore).
- Agent adapter: Builder adapterConfig.command is a wrapper shim.
- Pi model configuration is global: `~/.agents/models.json` (symlinked from `~/.pi/agent/models.json`) holds provider `omni` with 476 explicit model names plus openrouter overrides; `~/.pi/agent/settings.json` lists five providers (omni, deepseek, openrouter, opencode-go, google); `~/.pi/agent/models-store.json` holds discovered per-provider catalogues with etag/checkedAt (e.g. deepseek → 2). Project `.pi/` dirs contain no model names — only `.pi/tasks` in agent workspaces.
- omniroute is the upstream source of the 476 omni names; headroom uses a separate LiteLLM registry currently missing router ids and answers 502.
- docs/NEXT.md tracks open items.
Known behavior after this turn: pi 1.5.0 model-list fix, pane adoption based on /proc/<pid>/cmdline, board `r` refreshes both list and detail, `office .` accepted as folder.
Open issues: pi_local runs report costCents 0 / outputTokens 0 / usageJson null (usage not captured so cost limit cannot be enforced); ocr review reports `skipped` for Markdown-only diffs with unhelpful message in Work>review.
## Evidence
- Confidence: 90%
- Reason: Established repo/component overview from evidence in the turn.
## Related learned memories <!-- same-turn-2026-11-12 04:50:42 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Pi 1.5.0 moves --list-models to stdout]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Fixed four harness defects, verified clean tests, pushed 6c6bacc]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-session-2026-11-12-04-50-42|Rich discovery: --session 2026-11-12 04:50:42]]
- context: [[packs/paperclip-harness/20-context/omni|--session]]

View File

@@ -1228,3 +1228,35 @@ Validation: agent run succeeded after wrapper fix; branch clean after revert.
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Pi 1.5.0 model-table stdout/stderr mismatch breaks Paperclip pi_local adapter]] - observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Pi 1.5.0 model-table stdout/stderr mismatch breaks Paperclip pi_local adapter]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Unauthorized Paperclip work on test-alpha: AUD-172, review, adapter fix]] - action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Unauthorized Paperclip work on test-alpha: AUD-172, review, adapter fix]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-paseo-2026-11-12-04-37-46|Rich discovery: paseo 2026-11-12 04:37:46]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-paseo-2026-11-12-04-37-46|Rich discovery: paseo 2026-11-12 04:37:46]]
---
## Update (2026-11-12 04:50:42)
Ran two clean end-to-end tests and fixed four defects:
1. Pi 1.5.0 model list stream mismatch (fixed via pi-paperclip.sh shim).
2. Restored panes adopted with empty shells (now verified via /proc/<pid>/cmdline).
3. `r` on Board>board refreshed list but not selected task detail; now reloads both (verified: `last run succeeded · on_demand · 15:32`).
4. `office .` was refused as if no folder given; now accepted.
Tests:
- test-alpha: office --new, 8 panes, task AUD-172 created, agent ran, diff, review, merge → commit 36acb8d, STATUS.md added with `alpha is running.`
- harness-repo: `office . --project harness-repo --session harness-repo` adopted without rewriting .harness/, task AUD-173 created, agent appended line to README.md, cancelled with c then y; main untouched at b81cb91.
Artifacts: bin/harness rebuilt; .gitignore added with `.paperclip/` and `__pycache__/`; docs/NEXT.md updated. Commits: aa6eb74, 6c6bacc. Pushed to Gitea main. Both sessions confirmed running fixed binary (all 8 panes each).
Next suggested work: cost capture.
## Evidence
- Confidence: 95%
- Reason: Completed, delivered, and validated work.
## Related learned memories <!-- same-turn-2026-11-12 04:50:42 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Pi 1.5.0 moves --list-models to stdout]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Fixed four harness defects, verified clean tests, pushed 6c6bacc]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-session-2026-11-12-04-50-42|Rich discovery: --session 2026-11-12 04:50:42]]
- context: [[packs/paperclip-harness/20-context/omni|--session]]

View File

@@ -5063,3 +5063,339 @@ I have stopped. Nothing further will be run or edited until you say what you wan
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Pi 1.5.0 model-table stdout/stderr mismatch breaks Paperclip pi_local adapter]] - observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Pi 1.5.0 model-table stdout/stderr mismatch breaks Paperclip pi_local adapter]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Unauthorized Paperclip work on test-alpha: AUD-172, review, adapter fix]] - action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Unauthorized Paperclip work on test-alpha: AUD-172, review, adapter fix]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-paseo-2026-11-12-04-37-46|Rich discovery: paseo 2026-11-12 04:37:46]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-paseo-2026-11-12-04-37-46|Rich discovery: paseo 2026-11-12 04:37:46]]
---
## Update (2026-11-12 04:50:42)
Pi 1.5.0 prints `--list-models` output on stdout (Pi 0.8x used stderr) and also emits five extension warnings on stderr. Paperclip's adapter read `result.stderr || result.stdout`, so it parsed only the warnings, found no `provider model` rows and failed every run with `Pi returned no models`, `errorCode: adapter_failed`, no log bytes, ~4–5s per run. Fixed with shim `~/.local/share/paperclip-harness/wrappers/pi-paperclip.sh` that moves the table back to stderr for that one flag and passes everything else through; Builder adapterConfig.command points at it. Next run succeeded in 69s.
## Detailed observations from completed turn
Both clean tests are finished. Four defects came out, all four are fixed and verified on screen.
## Test 1 — `test-alpha`: passed
Folder → `office --new` → eight panes → task **AUD-172** created from `New task>new` → agent ran → `Work>change` showed the diff → `v` review → `m` then `y` merge.
```
36acb8d Merge AUD-172: Add a STATUS.md with one line
5eebc45 Add STATUS.md with status line
$ cat STATUS.md
alpha is running.
```
## Test 2 — `harness-repo`: passed
`office . --project harness-repo --session harness-repo` adopted the project without rewriting `.harness/`, task **AUD-173** was created, the agent appended one line to `README.md` and changed nothing else, and I did **not** merge. Cancelled with `c` then `y`: task `cancelled`, and `main` is untouched — `README.md` still has no "tested by the harness" line, last commit still `b81cb91`.
## The four defects, and what each one was
**1. Pi 1.5.0 broke every agent run.** This is the one that matters for what you were told about 1.5.
It is not the names. It is **which stream the list goes to**. Pi 1.5.0 prints `--list-models` on **stdout**; Pi 0.8x printed it on **stderr**. Paperclip's adapter reads stderr first, on purpose:
```js
// Pi outputs model list to stderr, but fall back to stdout for older versions
const output = result.stderr || result.stdout;
```
Pi 1.5.0 also puts five extension warnings on stderr, so the adapter parsed those, found no `provider model` rows, and refused every run with `Pi returned no models` — three runs died in 4–5 seconds with `errorCode: adapter_failed` and zero log bytes before I found it.
Fixed with a shim, `~/.local/share/paperclip-harness/wrappers/pi-paperclip.sh`, that puts the table back on stderr for that one flag and passes everything else through; Builder's `adapterConfig.command` points at it. The next run succeeded in 69 seconds.
**2. Panes restored after a reboot were "adopted" while running nothing.** TUIOS brings a session back with a fresh shell in each pane and keeps the pane names, so `board` matched them by name and reported `adopted`. A page that looks built and does nothing is the worst kind of wrong. Now `open_pane` reads `/proc/<pid>/cmdline` and adopts a pane only when the harness is what is running in it; otherwise it closes the pane and opens a fresh one. `shell` is exempt — it is a shell either way.
**3. `r` on `Board>board` refreshed the task list but not the chosen task.** So "last run running" stayed on screen long after the run finished. It now reloads both. Verified: `last run succeeded · on_demand · 15:32`.
**4. `office .` was refused** as if no folder had been given, because of the guard I added earlier. `.` is a real folder; only a missing argument is an error.
## Your question about model names — where they actually live
Measured on `.13`:
| File | What it holds |
|---|---|
| `~/.agents/models.json`, symlinked as `~/.pi/agent/models.json` | provider `omni` with **476 explicit names**, plus `openrouter` overrides |
| `~/.pi/agent/settings.json` | five providers: `omni`, `deepseek`, `openrouter`, `opencode-go`, `google` |
| `~/.pi/agent/models-store.json` | **discovered** catalogues per provider, with `etag` and `checkedAt` — e.g. `deepseek` → 2 models |
| a project's `.pi/` | no model names at all; in an agent workspace only `.pi/tasks` |
So names are global, not per project — an agent workspace cannot hold a stale copy. The new names are there and resolve: `omni/deepseek/deepseek-v4-flash`, `omni/deepseek/deepseek-v4-pro`, `omni/ds/…`, `omni/oc/deepseek-v4-flash-free`, `omni/opencode-go/…`, plus `deepseek deepseek-flash` and `deepseek deepseek-v4-pro` on the direct provider.
**omniroute** is the source of the 476 `omni` names, so a name missing from the router's `/v1/models` will not appear in Pi — and because `models-store.json` caches with an etag, a stale entry can hide a newly added name until the catalogue is refreshed. **headroom** keeps a separate LiteLLM registry and still has no entries for the router's ids, which is why it answers `502`; that is unrelated to Pi's storage, but it will need the same names when it is fixed.
That does not mean 1.5 did not fix your deepseek recognition — the names are recognised now. What 1.5 changed on this machine was the stream, and that is what broke Paperclip.
## Two open items, written into `docs/NEXT.md`
- **Cost is always zero for `pi_local` runs.** `cost-summary` says `costCents: 0`, `outputTokens: 0` over four real runs, and the run's `usageJson` is `null`. The adapter is not capturing Pi's token usage, so `Project>costs` and the cost line on `Board>the chosen task` are honest but useless, and the cost limit in your operating rules cannot bite.
- **`ocr` reviews code, not Markdown.** A documentation-only diff gives `status: skipped`, zero files selected. `bin/merge` handles that correctly ("nothing to review, recorded, not hidden"), but `Work>review` should say why instead of "nothing reviewable in the chang
…[truncated]…
## Evidence
- Confidence: 95%
- Reason: Concrete root cause and fix, durable across versions.; enriched with final-answer detail because persistence should be reusable
---
## Update (2026-11-12 04:50:42)
TUIOS restore brings panes back with a fresh shell while keeping pane names, so `board` matched by name and reported panes as `adopted` while they ran nothing. Fix: `open_pane` reads `/proc/<pid>/cmdline` and adopts only if the harness process is running in the pane; otherwise closes and opens a fresh pane. `shell` pane is exempt.
## Detailed observations from completed turn
Both clean tests are finished. Four defects came out, all four are fixed and verified on screen.
## Test 1 — `test-alpha`: passed
Folder → `office --new` → eight panes → task **AUD-172** created from `New task>new` → agent ran → `Work>change` showed the diff → `v` review → `m` then `y` merge.
```
36acb8d Merge AUD-172: Add a STATUS.md with one line
5eebc45 Add STATUS.md with status line
$ cat STATUS.md
alpha is running.
```
## Test 2 — `harness-repo`: passed
`office . --project harness-repo --session harness-repo` adopted the project without rewriting `.harness/`, task **AUD-173** was created, the agent appended one line to `README.md` and changed nothing else, and I did **not** merge. Cancelled with `c` then `y`: task `cancelled`, and `main` is untouched — `README.md` still has no "tested by the harness" line, last commit still `b81cb91`.
## The four defects, and what each one was
**1. Pi 1.5.0 broke every agent run.** This is the one that matters for what you were told about 1.5.
It is not the names. It is **which stream the list goes to**. Pi 1.5.0 prints `--list-models` on **stdout**; Pi 0.8x printed it on **stderr**. Paperclip's adapter reads stderr first, on purpose:
```js
// Pi outputs model list to stderr, but fall back to stdout for older versions
const output = result.stderr || result.stdout;
```
Pi 1.5.0 also puts five extension warnings on stderr, so the adapter parsed those, found no `provider model` rows, and refused every run with `Pi returned no models` — three runs died in 4–5 seconds with `errorCode: adapter_failed` and zero log bytes before I found it.
Fixed with a shim, `~/.local/share/paperclip-harness/wrappers/pi-paperclip.sh`, that puts the table back on stderr for that one flag and passes everything else through; Builder's `adapterConfig.command` points at it. The next run succeeded in 69 seconds.
**2. Panes restored after a reboot were "adopted" while running nothing.** TUIOS brings a session back with a fresh shell in each pane and keeps the pane names, so `board` matched them by name and reported `adopted`. A page that looks built and does nothing is the worst kind of wrong. Now `open_pane` reads `/proc/<pid>/cmdline` and adopts a pane only when the harness is what is running in it; otherwise it closes the pane and opens a fresh one. `shell` is exempt — it is a shell either way.
**3. `r` on `Board>board` refreshed the task list but not the chosen task.** So "last run running" stayed on screen long after the run finished. It now reloads both. Verified: `last run succeeded · on_demand · 15:32`.
**4. `office .` was refused** as if no folder had been given, because of the guard I added earlier. `.` is a real folder; only a missing argument is an error.
## Your question about model names — where they actually live
Measured on `.13`:
| File | What it holds |
|---|---|
| `~/.agents/models.json`, symlinked as `~/.pi/agent/models.json` | provider `omni` with **476 explicit names**, plus `openrouter` overrides |
| `~/.pi/agent/settings.json` | five providers: `omni`, `deepseek`, `openrouter`, `opencode-go`, `google` |
| `~/.pi/agent/models-store.json` | **discovered** catalogues per provider, with `etag` and `checkedAt` — e.g. `deepseek` → 2 models |
| a project's `.pi/` | no model names at all; in an agent workspace only `.pi/tasks` |
So names are global, not per project — an agent workspace cannot hold a stale copy. The new names are there and resolve: `omni/deepseek/deepseek-v4-flash`, `omni/deepseek/deepseek-v4-pro`, `omni/ds/…`, `omni/oc/deepseek-v4-flash-free`, `omni/opencode-go/…`, plus `deepseek deepseek-flash` and `deepseek deepseek-v4-pro` on the direct provider.
**omniroute** is the source of the 476 `omni` names, so a name missing from the router's `/v1/models` will not appear in Pi — and because `models-store.json` caches with an etag, a stale entry can hide a newly added name until the catalogue is refreshed. **headroom** keeps a separate LiteLLM registry and still has no entries for the router's ids, which is why it answers `502`; that is unrelated to Pi's storage, but it will need the same names when it is fixed.
That does not mean 1.5 did not fix your deepseek recognition — the names are recognised now. What 1.5 changed on this machine was the stream, and that is what broke Paperclip.
## Two open items, written into `docs/NEXT.md`
- **Cost is always zero for `pi_local` runs.** `cost-summary` says `costCents: 0`, `outputTokens: 0` over four real runs, and the run's `usageJson` is `null`. The adapter is not capturing Pi's token usage, so `Project>costs` and the cost line on `Board>the chosen task` are honest but useless, and the cost limit in your operating rules cannot bite.
- **`ocr` reviews code, not Markdown.** A documentation-only diff gives `status: skipped`, zero files selected. `bin/merge` handles that correctly ("nothing to review, recorded, not hidden"), but `Work>review` should say why instead of "nothing reviewable in the chang
…[truncated]…
## Evidence
- Confidence: 90%
- Reason: Repo-specific behavior and fix worth remembering.; enriched with final-answer detail because persistence should be reusable
---
## Update (2026-11-12 04:50:42)
Open issues logged in docs/NEXT.md: (1) `cost-summary` returns costCents 0, outputTokens 0 across four real runs and run `usageJson` is null — adapter not capturing Pi token usage, so Project>costs and board cost line are ineffective and the cost limit in operating rules cannot be enforced. (2) `ocr` reviews code only; a documentation-only diff returns `status: skipped` with 0 files, while bin/merge records it correctly; Work>review message should explain why rather than `nothing reviewable in the change (0 files)` when Work>change lists a file.
## Detailed observations from completed turn
Both clean tests are finished. Four defects came out, all four are fixed and verified on screen.
## Test 1 — `test-alpha`: passed
Folder → `office --new` → eight panes → task **AUD-172** created from `New task>new` → agent ran → `Work>change` showed the diff → `v` review → `m` then `y` merge.
```
36acb8d Merge AUD-172: Add a STATUS.md with one line
5eebc45 Add STATUS.md with status line
$ cat STATUS.md
alpha is running.
```
## Test 2 — `harness-repo`: passed
`office . --project harness-repo --session harness-repo` adopted the project without rewriting `.harness/`, task **AUD-173** was created, the agent appended one line to `README.md` and changed nothing else, and I did **not** merge. Cancelled with `c` then `y`: task `cancelled`, and `main` is untouched — `README.md` still has no "tested by the harness" line, last commit still `b81cb91`.
## The four defects, and what each one was
**1. Pi 1.5.0 broke every agent run.** This is the one that matters for what you were told about 1.5.
It is not the names. It is **which stream the list goes to**. Pi 1.5.0 prints `--list-models` on **stdout**; Pi 0.8x printed it on **stderr**. Paperclip's adapter reads stderr first, on purpose:
```js
// Pi outputs model list to stderr, but fall back to stdout for older versions
const output = result.stderr || result.stdout;
```
Pi 1.5.0 also puts five extension warnings on stderr, so the adapter parsed those, found no `provider model` rows, and refused every run with `Pi returned no models` — three runs died in 4–5 seconds with `errorCode: adapter_failed` and zero log bytes before I found it.
Fixed with a shim, `~/.local/share/paperclip-harness/wrappers/pi-paperclip.sh`, that puts the table back on stderr for that one flag and passes everything else through; Builder's `adapterConfig.command` points at it. The next run succeeded in 69 seconds.
**2. Panes restored after a reboot were "adopted" while running nothing.** TUIOS brings a session back with a fresh shell in each pane and keeps the pane names, so `board` matched them by name and reported `adopted`. A page that looks built and does nothing is the worst kind of wrong. Now `open_pane` reads `/proc/<pid>/cmdline` and adopts a pane only when the harness is what is running in it; otherwise it closes the pane and opens a fresh one. `shell` is exempt — it is a shell either way.
**3. `r` on `Board>board` refreshed the task list but not the chosen task.** So "last run running" stayed on screen long after the run finished. It now reloads both. Verified: `last run succeeded · on_demand · 15:32`.
**4. `office .` was refused** as if no folder had been given, because of the guard I added earlier. `.` is a real folder; only a missing argument is an error.
## Your question about model names — where they actually live
Measured on `.13`:
| File | What it holds |
|---|---|
| `~/.agents/models.json`, symlinked as `~/.pi/agent/models.json` | provider `omni` with **476 explicit names**, plus `openrouter` overrides |
| `~/.pi/agent/settings.json` | five providers: `omni`, `deepseek`, `openrouter`, `opencode-go`, `google` |
| `~/.pi/agent/models-store.json` | **discovered** catalogues per provider, with `etag` and `checkedAt` — e.g. `deepseek` → 2 models |
| a project's `.pi/` | no model names at all; in an agent workspace only `.pi/tasks` |
So names are global, not per project — an agent workspace cannot hold a stale copy. The new names are there and resolve: `omni/deepseek/deepseek-v4-flash`, `omni/deepseek/deepseek-v4-pro`, `omni/ds/…`, `omni/oc/deepseek-v4-flash-free`, `omni/opencode-go/…`, plus `deepseek deepseek-flash` and `deepseek deepseek-v4-pro` on the direct provider.
**omniroute** is the source of the 476 `omni` names, so a name missing from the router's `/v1/models` will not appear in Pi — and because `models-store.json` caches with an etag, a stale entry can hide a newly added name until the catalogue is refreshed. **headroom** keeps a separate LiteLLM registry and still has no entries for the router's ids, which is why it answers `502`; that is unrelated to Pi's storage, but it will need the same names when it is fixed.
That does not mean 1.5 did not fix your deepseek recognition — the names are recognised now. What 1.5 changed on this machine was the stream, and that is what broke Paperclip.
## Two open items, written into `docs/NEXT.md`
- **Cost is always zero for `pi_local` runs.** `cost-summary` says `costCents: 0`, `outputTokens: 0` over four real runs, and the run's `usageJson` is `null`. The adapter is not capturing Pi's token usage, so `Project>costs` and the cost line on `Board>the chosen task` are honest but useless, and the cost limit in your operating rules cannot bite.
- **`ocr` reviews code, not Markdown.** A documentation-only diff gives `status: skipped`, zero files selected. `bin/merge` handles that correctly ("nothing to review, recorded, not hidden"), but `Work>review` should say why instead of "nothing reviewable in the chang
…[truncated]…
## Evidence
- Confidence: 85%
- Reason: Durable known defects and follow-ups.; enriched with final-answer detail because persistence should be reusable
---
## Update (2026-11-12 04:50:42)
## Structural/configuration observations (2026-11-12 04:50:42)
Both clean tests are finished. Four defects came out, all four are fixed and verified on screen.
## Test 1 — `test-alpha`: passed
Folder → `office --new` → eight panes → task **AUD-172** created from `New task>new` → agent ran → `Work>change` showed the diff → `v` review → `m` then `y` merge.
```
36acb8d Merge AUD-172: Add a STATUS.md with one line
5eebc45 Add STATUS.md with status line
$ cat STATUS.md
alpha is running.
```
## Test 2 — `harness-repo`: passed
`office . --project harness-repo --session harness-repo` adopted the project without rewriting `.harness/`, task **AUD-173** was created, the agent appended one line to `README.md` and changed nothing else, and I did **not** merge. Cancelled with `c` then `y`: task `cancelled`, and `main` is untouched — `README.md` still has no "tested by the harness" line, last commit still `b81cb91`.
## The four defects, and what each one was
**1. Pi 1.5.0 broke every agent run.** This is the one that matters for what you were told about 1.5.
It is not the names. It is **which stream the list goes to**. Pi 1.5.0 prints `--list-models` on **stdout**; Pi 0.8x printed it on **stderr**. Paperclip's adapter reads stderr first, on purpose:
```js
// Pi outputs model list to stderr, but fall back to stdout for older versions
const output = result.stderr || result.stdout;
```
Pi 1.5.0 also puts five extension warnings on stderr, so the adapter parsed those, found no `provider model` rows, and refused every run with `Pi returned no models` — three runs died in 4–5 seconds with `errorCode: adapter_failed` and zero log bytes before I found it.
Fixed with a shim, `~/.local/share/paperclip-harness/wrappers/pi-paperclip.sh`, that puts the table back on stderr for that one flag and passes everything else through; Builder's `adapterConfig.command` points at it. The next run succeeded in 69 seconds.
**2. Panes restored after a reboot were "adopted" while running nothing.** TUIOS brings a session back with a fresh shell in each pane and keeps the pane names, so `board` matched them by name and reported `adopted`. A page that looks built and does nothing is the worst kind of wrong. Now `open_pane` reads `/proc/<pid>/cmdline` and adopts a pane only when the harness is what is running in it; otherwise it closes the pane and opens a fresh one. `shell` is exempt — it is a shell either way.
**3. `r` on `Board>board` refreshed the task list but not the chosen task.** So "last run running" stayed on screen long after the run finished. It now reloads both. Verified: `last run succeeded · on_demand · 15:32`.
**4. `office .` was refused** as if no folder had been given, because of the guard I added earlier. `.` is a real folder; only a missing argument is an error.
## Your question about model names — where they actually live
Measured on `.13`:
| File | What it holds |
|---|---|
| `~/.agents/models.json`, symlinked as `~/.pi/agent/models.json` | provider `omni` with **476 explicit names**, plus `openrouter` overrides |
| `~/.pi/agent/settings.json` | five providers: `omni`, `deepseek`, `openrouter`, `opencode-go`, `google` |
| `~/.pi/agent/models-store.json` | **discovered** catalogues per provider, with `etag` and `checkedAt` — e.g. `deepseek` → 2 models |
| a project's `.pi/` | no model names at all; in an agent workspace only `.pi/tasks` |
So names are global, not per project — an agent workspace cannot hold a stale copy. The new names are there and resolve: `omni/deepseek/deepseek-v4-flash`, `omni/deepseek/deepseek-v4-pro`, `omni/ds/…`, `omni/oc/deepseek-v4-flash-free`, `omni/opencode-go/…`, plus `deepseek deepseek-flash` and `deepseek deepseek-v4-pro` on the direct provider.
**omniroute** is the source of the 476 `omni` names, so a name missing from the router's `/v1/models` will not appear in Pi — and because `models-store.json` caches with an etag, a stale entry can hide a newly added name until the catalogue is refreshed. **headroom** keeps a separate LiteLLM registry and still has no entries for the router's ids, which is why it answers `502`; that is unrelated to Pi's storage, but it will need the same names when it is fixed.
That does not mean 1.5 did not fix your deepseek recognition — the names are recognised now. What 1.5 changed on this machine was the stream, and that is what broke Paperclip.
## Two open items, written into `docs/NEXT.md`
- **Cost is always zero for `pi_local` runs.** `cost-summary` says `costCents: 0`, `outputTokens: 0` over four real runs, and the run's `usageJson` is `null`. The adapter is not capturing Pi's token usage, so `Project>costs` and the cost line on `Board>the chosen task` are honest but useless, and the cost limit in your operating rules cannot bite.
- **`ocr` reviews code, not Markdown.** A documentation-only diff gives `status: skipped`, zero files selected. `bin/merge` handles that correctly ("nothing to review, recorded, not hidden"), but `Work>review` should say why instead of "nothing reviewable in the change (0 files)" while `Work>change` lists one file.
## Committed and pushed
`paperclip-harness` → `6c6bacc`, on Gitea. `.paperclip/` and `__pycache__/` are now ignored, which they were not.
Both sessions (`test-alpha`, `harness-repo`) run the fixed binary — I checked every pane's process, all eight in each are the harness.
The one thing I would do next, if you agree, is the cost capture: it is the only part of your operating rules that cannot be enforced today.
## Source paths observed
- `.paperclip/worktrees/AUD-173-add-a-line-to-readme`
- `run/current-system/sw/bin/bash`
- `.../packs/paperclip-harness/20-context/home.md`
- `.../20-context/paperclip-harness.md`
- `.../70-runbooks/tmp-deployment-operations.md`
- `bin/lib/__pycache__/tuios.cpython-314.pyc`
- `gitea.lab.audasmedia.com.au/sam/paperclip-harness.git`
- `home/sam/paseo/projects/pape`
- `run/current-system/sw/bin/zsh`
- `home/sam/paseo/projects/paperclip-harness/bin/harness`
- `.local/share/paperclip-harness/wrappers/pi-paperclip.sh`
- `.pi/agent/models.json`
- `.pi/agent/settings.json`
- `.pi/agent/models-store.json`
- `omni/deepseek/deepseek-v4-flash`
- `omni/deepseek/deepseek-v4-pro`
- `omni/oc/deepseek-v4-flash-free`
## Evidence
- Confidence: 82%
- Reason: Captured factual repo structure/configuration patterns from rich discovery
## Related learned memories <!-- same-turn-2026-11-12 04:50:42 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Pi 1.5.0 moves --list-models to stdout]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Fixed four harness defects, verified clean tests, pushed 6c6bacc]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-session-2026-11-12-04-50-42|Rich discovery: --session 2026-11-12 04:50:42]]
- context: [[packs/paperclip-harness/20-context/omni|--session]]

View File

@@ -0,0 +1,127 @@
---
type: session
id: session.paperclip-harness.rich-discovery-session-2026-11-12-04-50-42
title: Rich discovery: --session 2026-11-12 04:50:42
status: active
source_of_truth: false
freshness: current
last_reviewed: 2026-11-12
tags:
- pack/paperclip-harness
- agent-memory/session
- autolearn
- rich-discovery
- repo/session
---
# Rich discovery: --session 2026-11-12 04:50:42
Sanitized rich discovery snapshot captured automatically from the completed turn.
## Source paths observed
- `.paperclip/worktrees/AUD-173-add-a-line-to-readme`
- `run/current-system/sw/bin/bash`
- `.../packs/paperclip-harness/20-context/home.md`
- `.../20-context/paperclip-harness.md`
- `.../70-runbooks/tmp-deployment-operations.md`
- `bin/lib/__pycache__/tuios.cpython-314.pyc`
- `gitea.lab.audasmedia.com.au/sam/paperclip-harness.git`
- `home/sam/paseo/projects/pape`
- `run/current-system/sw/bin/zsh`
- `home/sam/paseo/projects/paperclip-harness/bin/harness`
- `.local/share/paperclip-harness/wrappers/pi-paperclip.sh`
- `.pi/agent/models.json`
- `.pi/agent/settings.json`
- `.pi/agent/models-store.json`
- `omni/deepseek/deepseek-v4-flash`
- `omni/deepseek/deepseek-v4-pro`
- `omni/oc/deepseek-v4-flash-free`
## Final answer snapshot
Both clean tests are finished. Four defects came out, all four are fixed and verified on screen.
## Test 1 — `test-alpha`: passed
Folder → `office --new` → eight panes → task **AUD-172** created from `New task>new` → agent ran → `Work>change` showed the diff → `v` review → `m` then `y` merge.
```
36acb8d Merge AUD-172: Add a STATUS.md with one line
5eebc45 Add STATUS.md with status line
$ cat STATUS.md
alpha is running.
```
## Test 2 — `harness-repo`: passed
`office . --project harness-repo --session harness-repo` adopted the project without rewriting `.harness/`, task **AUD-173** was created, the agent appended one line to `README.md` and changed nothing else, and I did **not** merge. Cancelled with `c` then `y`: task `cancelled`, and `main` is untouched — `README.md` still has no "tested by the harness" line, last commit still `b81cb91`.
## The four defects, and what each one was
**1. Pi 1.5.0 broke every agent run.** This is the one that matters for what you were told about 1.5.
It is not the names. It is **which stream the list goes to**. Pi 1.5.0 prints `--list-models` on **stdout**; Pi 0.8x printed it on **stderr**. Paperclip's adapter reads stderr first, on purpose:
```js
// Pi outputs model list to stderr, but fall back to stdout for older versions
const output = result.stderr || result.stdout;
```
Pi 1.5.0 also puts five extension warnings on stderr, so the adapter parsed those, found no `provider model` rows, and refused every run with `Pi returned no models` — three runs died in 4–5 seconds with `errorCode: adapter_failed` and zero log bytes before I found it.
Fixed with a shim, `~/.local/share/paperclip-harness/wrappers/pi-paperclip.sh`, that puts the table back on stderr for that one flag and passes everything else through; Builder's `adapterConfig.command` points at it. The next run succeeded in 69 seconds.
**2. Panes restored after a reboot were "adopted" while running nothing.** TUIOS brings a session back with a fresh shell in each pane and keeps the pane names, so `board` matched them by name and reported `adopted`. A page that looks built and does nothing is the worst kind of wrong. Now `open_pane` reads `/proc/<pid>/cmdline` and adopts a pane only when the harness is what is running in it; otherwise it closes the pane and opens a fresh one. `shell` is exempt — it is a shell either way.
**3. `r` on `Board>board` refreshed the task list but not the chosen task.** So "last run running" stayed on screen long after the run finished. It now reloads both. Verified: `last run succeeded · on_demand · 15:32`.
**4. `office .` was refused** as if no folder had been given, because of the guard I added earlier. `.` is a real folder; only a missing argument is an error.
## Your question about model names — where they actually live
Measured on `.13`:
| File | What it holds |
|---|---|
| `~/.agents/models.json`, symlinked as `~/.pi/agent/models.json` | provider `omni` with **476 explicit names**, plus `openrouter` overrides |
| `~/.pi/agent/settings.json` | five providers: `omni`, `deepseek`, `openrouter`, `opencode-go`, `google` |
| `~/.pi/agent/models-store.json` | **discovered** catalogues per provider, with `etag` and `checkedAt` — e.g. `deepseek` → 2 models |
| a project's `.pi/` | no model names at all; in an agent workspace only `.pi/tasks` |
So names are global, not per project — an agent workspace cannot hold a stale copy. The new names are there and resolve: `omni/deepseek/deepseek-v4-flash`, `omni/deepseek/deepseek-v4-pro`, `omni/ds/…`, `omni/oc/deepseek-v4-flash-free`, `omni/opencode-go/…`, plus `deepseek deepseek-flash` and `deepseek deepseek-v4-pro` on the direct provider.
**omniroute** is the source of the 476 `omni` names, so a name missing from the router's `/v1/models` will not appear in Pi — and because `models-store.json` caches with an etag, a stale entry can hide a newly added name until the catalogue is refreshed. **headroom** keeps a separate LiteLLM registry and still has no entries for the router's ids, which is why it answers `502`; that is unrelated to Pi's storage, but it will need the same names when it is fixed.
That does not mean 1.5 did not fix your deepseek recognition — the names are recognised now. What 1.5 changed on this machine was the stream, and that is what broke Paperclip.
## Two open items, written into `docs/NEXT.md`
- **Cost is always zero for `pi_local` runs.** `cost-summary` says `costCents: 0`, `outputTokens: 0` over four real runs, and the run's `usageJson` is `null`. The adapter is not capturing Pi's token usage, so `Project>costs` and the cost line on `Board>the chosen task` are honest but useless, and the cost limit in your operating rules cannot bite.
- **`ocr` reviews code, not Markdown.** A documentation-only diff gives `status: skipped`, zero files selected. `bin/merge` handles that correctly ("nothing to review, recorded, not hidden"), but `Work>review` should say why instead of "nothing reviewable in the change (0 files)" while `Work>change` lists one file.
## Committed and pushed
`paperclip-harness` → `6c6bacc`, on Gitea. `.paperclip/` and `__pycache__/` are now ignored, which they were not.
Both sessions (`test-alpha`, `harness-repo`) run the fixed binary — I checked every pane's process, all eight in each are the harness.
The one thing I would do next, if you agree, is the cost capture: it is the only part of your operating rules that cannot be enforced today.
## 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-12 04:50:42 -->
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|Pi 1.5.0 moves --list-models to stdout]]
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Fixed four harness defects, verified clean tests, pushed 6c6bacc]]
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-session-2026-11-12-04-50-42|Rich discovery: --session 2026-11-12 04:50:42]]
- context: [[packs/paperclip-harness/20-context/omni|--session]]

View File

@@ -169,6 +169,9 @@ exit 0
# Where the commands live on a machine that is driven over ssh.
REMOTE_BIN = "~/.local/share/paperclip-harness/bin"
HOSTS = { HOSTS = {
"nixos-desktop": "192.168.20.13", ".13": "192.168.20.13", "13": "192.168.20.13", "nixos-desktop": "192.168.20.13", ".13": "192.168.20.13", "13": "192.168.20.13",
"sam-4screen-desktop": "192.168.20.27", "sam-4screen-desktop-1": "192.168.20.27", "sam-4screen-desktop": "192.168.20.27", "sam-4screen-desktop-1": "192.168.20.27",
@@ -204,6 +207,16 @@ def is_this_machine(name):
return addr in local_addresses() return addr in local_addresses()
def install_wrapper(addr, remote_bin):
"""Leave a short `office` on the other machine's PATH, pointing at the copy."""
script = ("#!/bin/sh\n"
"# Written by office on another machine. Runs the copy it keeps here.\n"
'exec "$HOME/.local/share/paperclip-harness/bin/office" "$@"\n')
subprocess.run(["ssh", "-o", "BatchMode=yes", addr,
"mkdir -p \"$HOME/.local/bin\" && cat > \"$HOME/.local/bin/office\" && chmod +x \"$HOME/.local/bin/office\""],
input=script, text=True, capture_output=True, timeout=60)
def run_on_remote(host, folder, opts): def run_on_remote(host, folder, opts):
"""Adopt a folder that lives on another machine, from where you are sitting. """Adopt a folder that lives on another machine, from where you are sitting.
@@ -218,14 +231,34 @@ def run_on_remote(host, folder, opts):
if probe.returncode != 0: if probe.returncode != 0:
raise h.Refused(3, "cannot log in to %s (%s). Set up a key first: ssh-copy-id %s" raise h.Refused(3, "cannot log in to %s (%s). Set up a key first: ssh-copy-id %s"
% (host, addr, addr)) % (host, addr, addr))
# The other machine needs the commands. They are copied to one fixed place
# there rather than assumed to be in the same folder: a machine that has
# never seen this repository still gets a working `office`. Copying every
# time is what keeps the two in step, and it is one small folder.
here = os.path.dirname(os.path.realpath(__file__)) here = os.path.dirname(os.path.realpath(__file__))
inner = [os.path.join(here, "office"), folder, "--no-switch"] remote_bin = REMOTE_BIN
# rsync will not make two levels of directory, so make the parent first.
subprocess.run(["ssh", "-o", "BatchMode=yes", addr,
"mkdir -p \"$HOME/.local/share/paperclip-harness\""],
capture_output=True, text=True, timeout=60)
copy = subprocess.run(
["rsync", "-a", "--delete", "--exclude", "__pycache__", "--exclude", "*.pyc",
here + "/", "%s:%s/" % (addr, remote_bin)],
capture_output=True, text=True, timeout=300)
if copy.returncode != 0:
raise h.Refused(3, "could not copy the commands to %s: %s"
% (host, (copy.stderr or copy.stdout).strip()[:300]))
install_wrapper(addr, remote_bin)
inner = [folder, "--no-switch"]
for flag, value in (("--project", opts["project"]), ("--session", opts["session"]), ("--new", opts["new"])): for flag, value in (("--project", opts["project"]), ("--session", opts["session"]), ("--new", opts["new"])):
if value: if value:
inner += [flag, value] inner += [flag, value]
if opts["dry"]: if opts["dry"]:
inner.append("--dry") inner.append("--dry")
cmd = " ".join(shlex.quote(a) for a in inner) # "$HOME" must stay unquoted so the other machine's shell expands it; every
# argument after it is quoted, so a folder with a space still arrives whole.
cmd = 'exec "$HOME/.local/share/paperclip-harness/bin/office" ' + \
" ".join(shlex.quote(a) for a in inner)
h.say("on %s: %s" % (host, cmd)) h.say("on %s: %s" % (host, cmd))
out = subprocess.run(["ssh", "-o", "StrictHostKeyChecking=accept-new", addr, cmd], out = subprocess.run(["ssh", "-o", "StrictHostKeyChecking=accept-new", addr, cmd],
capture_output=True, text=True, timeout=600) capture_output=True, text=True, timeout=600)
@@ -237,15 +270,85 @@ def run_on_remote(host, folder, opts):
return 0 return 0
USAGE = """office — open a folder as a project USAGE = """office — open a folder as a project, and say what else can be run
THE RULE
The folder and the session live on the same machine. If you are typing inside
the session, on the machine that owns the folder, it is just: office <folder>
Use --host only when the folder is on a different machine from your shell.
WHAT IT DOES
1. reads the folder
2. if a machine is named, checks whether that is this machine, and runs locally if so
3. if it really is another machine, checks the login and runs the same command there
4. adopts the Paperclip project, or creates it with --new
5. writes what is missing in <folder>/.harness/ and never overwrites
6. records the project in ~/.config/paperclip-harness/projects.toml
7. creates the session on that machine's daemon, then builds eight panes
COMMANDS
office <folder> --project "<Name>" adopt a project Paperclip already has office <folder> --project "<Name>" adopt a project Paperclip already has
office <folder> --new "<Name>" create the Paperclip project too office <folder> --new "<Name>" create the Paperclip project as well
office <folder> --project "<Name>" --host nixos-desktop the folder is on that machine office . adopt the folder you are standing in
office <folder> --dry say what would happen, write nothing office <folder> --session <name> name the session instead of the folder
office <folder> --project "<Name>" --host <machine> the folder is on that machine
office <folder> --new "<Name>" --dry say what would happen, write nothing
The folder and the session live on the same machine. If you are typing inside the board create any missing pane in this project's session
session, on the machine that owns the folder, it is just: office <folder>. board --folder <folder> the same, for another project
board --replace close this project's panes, then build all eight again
board --reset close every pane in the session, then build
project-reset <folder> forget the session and start this project again
merge <task> rebase, check, review and merge one task (asks first)
review <task> [--post] run ocr over one task's diff, and record the verdict
triage <task> [--ask next|failure] ask Mouse-1 one question about a task
MACHINES (name, or the address ssh reaches)
nixos-desktop .13 192.168.20.13 hosts Paperclip, the daemon, the main session
sam-4screen-desktop .27 192.168.20.27 your working desktop
sam-thinkpad .51 192.168.20.51 the laptop
EVERY MACHINE, FROM THE TERMINAL
On .13, a folder that is on .13:
office ~/paseo/projects/test-alpha --project "test-alpha"
On .27, adopting a folder that lives on .13:
office /home/sam/paseo/projects/email_agent --project "Email Agent" --host nixos-desktop
On .27, a folder that is on .27 (stays local, no --host):
office ~/src/thing --new "thing"
On .51, the same folder on .51:
office ~/src/thing --project "thing" --host sam-thinkpad
Then attach. Nothing may move your client from inside a pane, so the last step is yours:
tuios attach <session> on this machine
tuios attach --host nixos-desktop <session> to a session on another machine
ON ANOTHER MACHINE
--host copies this folder of commands to the other machine, at
~/.local/share/paperclip-harness/bin/
and leaves a short ~/.local/bin/office there, so it can be run from that
machine too. Nothing has to be installed by hand. On .27 and .51 ~/.local/bin
is not on PATH yet, so there you call it by its full path:
~/.local/share/paperclip-harness/bin/office <folder>
AFTER A REBOOT
TUIOS restores a session with a fresh shell in every pane and keeps the pane
names. Run board once for that project: it checks what each pane is actually
running and replaces any pane that is only a prompt.
KEYS INSIDE THE PANES
( ) change tab tab change panel enter open / choose
r re-read Paperclip R rebuild all panes ? list the keys
h j k l z n and digits belong to TUIOS and never reach the program.
MORE
docs/STARTUP.md these commands, the rule, and the three rebuild levels
docs/WORKSPACES.md what each of the eight panes is for
docs/CLEAN-TESTS.md the two end-to-end tests and what they measured
""" """
@@ -260,7 +363,11 @@ def main():
# are standing in. Only a missing argument is an error. # are standing in. Only a missing argument is an error.
raise h.Refused(2, "give the folder: office <folder> --project \"<Name>\" (office --help)") raise h.Refused(2, "give the folder: office <folder> --project \"<Name>\" (office --help)")
folder = os.path.abspath(os.path.expanduser(opts["folder"])) folder = os.path.abspath(os.path.expanduser(opts["folder"]))
if not os.path.isdir(folder): # The folder is only checked here when it is this machine's folder. A folder
# on another machine is checked there: asking this machine about it says "no
# folder", which is true and useless.
wanted = opts["host"] or opts["remote"]
if not (wanted and not is_this_machine(wanted)) and not os.path.isdir(folder):
raise h.Refused(2, "no folder at %s" % folder) raise h.Refused(2, "no folder at %s" % folder)
host = opts["host"] or pj.this_machine() host = opts["host"] or pj.this_machine()

View File

@@ -43,6 +43,10 @@ can, through the daemon's own verb, so the second command is gone.
4. On the machine that owns the folder: creates the Paperclip project if `--new` was given, or 4. On the machine that owns the folder: creates the Paperclip project if `--new` was given, or
adopts the existing one; writes `.harness/harness.toml`, `check.sh`, `NOTES.md` — never adopts the existing one; writes `.harness/harness.toml`, `check.sh`, `NOTES.md` — never
overwriting anything that exists. overwriting anything that exists.
Before the remote run it copies this folder's commands to the other machine at
`~/.local/share/paperclip-harness/bin/` and leaves a short `~/.local/bin/office` there, so
nothing has to be installed by hand. On `.27` and `.51` `~/.local/bin` is not on `PATH`
yet, so there the command is called by its full path.
5. Creates the session if it is missing, on that machine's daemon. 5. Creates the session if it is missing, on that machine's daemon.
6. Builds the eight panes, in order, each pane claimed by workspace **name**. 6. Builds the eight panes, in order, each pane claimed by workspace **name**.