# The two clean tests, step by step Both tests answer one question: can a person get from a folder to a working set of panes, and then from a task to merged work, without editing a file. Run them in this order. Write down what you see; that is the useful part. --- ## Test 1 — a project that does not exist yet ### Step 1. Make the folder and its repository ```sh mkdir -p ~/paseo/projects/test-alpha cd ~/paseo/projects/test-alpha git init -b main printf '# test alpha\n' > README.md git add . && git commit -m "initial commit" ``` ### Step 2. Adopt it with one command ```sh cd ~/paseo/projects/paperclip-harness bin/office ~/paseo/projects/test-alpha --new "test-alpha" ``` `--new` creates the Paperclip project, its workspace and the worktree policy. Without `--new`, `--project ` adopts a project that already exists. Expected output: ``` project: test-alpha created (…) folder: /home/sam/paseo/projects/test-alpha session: test-alpha machine: nixos-desktop wrote …/.harness/harness.toml wrote …/.harness/check.sh wrote …/.harness/NOTES.md session: test-alpha (created; switch to it with TUIOS's session switcher) shell created Shell board created Board … workspaces in use: 8 ``` ### Step 3. Show the session A program cannot move your client: TUIOS refuses that from a pane. Use TUIOS's own session switcher, or from a terminal: ```sh tuios attach test-alpha ``` ### Step 4. Check the panes 1. `Repair>repair` → every check passes. 2. `Shell>shell` → the box at the top says the project folder is `…/test-alpha`. 3. `Board>board` → "no tasks in this project". 4. Press `R` once. All eight panes come back, still pointing at `test-alpha`. ### Step 5. One task, end to end 1. `New task>new`: title `Add a STATUS.md with one line`, description `Create STATUS.md at the repository root with the line: alpha is running. Change no other file. Commit it.` Leave the blocker and the command empty. Assign `yes`. Press `Enter` on the last field. 2. `Board>board`: the task appears and goes `todo` → `in progress` → `done`. 3. `Agents>agents`: `)` to **runs**, `↑` to the newest run, `Enter` → its log. 4. `Work>work`: `change` shows one file, one line. `review` says no review recorded yet. 5. `Work>work` → `v` starts a review; the tab shows it run. 6. `Work>work` → `m` → the dialog names the branch and `main` → `y`. The merge tab shows the rebase, the check, the review and `merged into main at …`. **Pass:** the file exists on `main`, the task is `done`, cost is visible in `Project>costs`, and nothing was typed into a terminal after step 2. --- ## Test 2 — a folder that already exists This uses **this repository**, which is real code with real history. ### Step 1. Adopt it into its own session ```sh cd ~/paseo/projects/paperclip-harness bin/office . --project harness-repo --session harness-repo ``` The project `harness-repo` already exists in Paperclip and points at this folder, so `--project` adopts it. A separate session is used on purpose: the working session `paperclip-harness` shows the sandbox project, and this test must not rebuild it. ### Step 2. Confirm nothing was overwritten ```sh git status --short ``` Expected: only new files under `.harness/` (the project file, `check.sh`, `NOTES.md`). Nothing existing is changed. If you see any modified file that you did not touch, stop and say so. ### Step 3. Show the session and look ```sh tuios attach harness-repo ``` 1. `Project>project` → `projects` tab lists both `spike-2026-11-10` and `harness-repo`, each with its folder and machine. 2. `Board>board` → empty, because this project has no tasks yet. 3. `New task>new` → create `Add a line to README`, description `Add the line "tested by the harness" at the end of README.md. Change nothing else. Commit it.`, assign `yes`. 4. Watch it in `Agents>agents`, read the diff in `Work>work`. **Do not merge it** — this is the repository's own history. Cancel the task with `c` in `Board>board` instead, and confirm in `git log` that nothing landed. **Pass:** the panes read this repository's own tasks, its own worktree is created, the diff shows only `README.md`, and cancelling removes it from the board without touching `main`. --- ## What to write down For each step: what you pressed, what appeared, and anything that did not match. That is worth more than a verdict, because it says where the interface and Paperclip disagree. --- # Results, 2026-11-12 Both tests were run on `.13`, driven with `tuios send-text`, `tuios send-keys` and `tuios capture-pane`, so every step below is what the screen actually showed. ## Test 1 — passed, after one real defect was found and fixed | Step | What happened | |---|---| | folder + `git init` | ok | | `office --new "test-alpha"` | project, session and eight panes created | | `New task>new` → **AUD-172** | created and handed to Builder; the Assign field takes the word `yes` | | first three runs | **all failed in 4–5 s**, `errorCode: adapter_failed`, `logBytes: 0` | | the fix | see "Pi 1.5.0 and the model list" below | | the run after the fix | **succeeded in 69 s** | | `Work>change` | worktree, branch, base, commits, `STATUS.md \| 1 +`, and the diff | | `Work>review` (`v`) | `ocr` **selected 0 files** — it does not review Markdown | | `Work>merge` (`m`, `y`) | rebased, no verify command, review recorded as "nothing to review", `merged into main at 36acb8d` | | `main` | `STATUS.md` present, contents `alpha is running.` | **What did not work as the test expected** - **Cost is always zero.** `Project>costs` and `Board>the chosen task` report `0 cent(s)` and `0 tokens out` over four runs. `usageJson` on the run is `null`: the `pi_local` adapter does not capture token usage from Pi, so cost cannot be shown for these runs. Open item. - **`ocr` reviews code, not Markdown.** A documentation-only change gets `status: skipped`, `selected: []`. `bin/merge` already treats this as "nothing to review, recorded, not hidden", so it does not block a merge — but a reviewer reading the tab sees "nothing reviewable" while the `change` tab lists one file. ## Test 2 — passed, after one real defect was found and fixed | Step | What happened | |---|---| | `office . --project harness-repo --session harness-repo` | adopted the project and the panes; `.harness/` was not rewritten | | panes after the reboot | **restored shells**, and `office` called them "adopted" | | the fix | a pane is adopted only if its own process is the harness | | `New task>new` → **AUD-173** | created and handed to Builder; the run succeeded in 24 s | | the diff | `README.md \| 1 +`, nothing else, committed on the branch | | merge | **not merged on purpose** | | `c`, then `y` in `Board>board` | task `cancelled`; status becomes cancelled, thread and runs stay | | `main` | untouched — `README.md` has no new line, last commit still `b81cb91` | **What did not work as the test expected** - **After a reboot, every pane is a bare shell and the pane keeps its name.** `board` matched the panes by name and reported them "adopted" while nothing was running in them. A page that looks built but does nothing is the worst kind of wrong. Now `open_pane` reads `/proc//cmdline` and adopts a pane only when the harness is what is running; otherwise it closes the pane and opens a fresh one. `shell` is exempt: it is a shell either way. - **`Board>board` `r` refreshed the task list but not the selected task's detail**, so "last run running" stayed on screen minutes after the run finished. `r` now reloads both. - A cancelled task is grouped under **Done** and its row reads `gone`. That is deliberate (Paperclip records it as finished) but it is worth knowing before you look for it. ## Pi 1.5.0 and the model list — where names live, and what changed Measured on `.13`: | File | What it holds | |---|---| | `~/.agents/models.json` (symlinked as `~/.pi/agent/models.json`) | provider `omni` with **476 explicit model names**, and `openrouter` overrides | | `~/.pi/agent/settings.json` | five providers with `baseUrl`, `api`, `apiKey`: `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; for an agent workspace it holds only `.pi/tasks` | So model names are global, not per project. An agent workspace's `.pi/` cannot hold a stale copy. The names are there: `deepseek deepseek-flash`, `deepseek deepseek-v4-pro`, and through the router `omni deepseek/deepseek-v4-flash`, `omni deepseek/deepseek-v4-pro`, `omni ds/…`, `omni oc/deepseek-v4-flash-free`, `omni opencode-go/…`. The configured agent model `omni/deepseek/deepseek-v4-flash` resolves. **The change that broke Paperclip is not the names, it is the stream.** Pi 1.5.0 prints `--list-models` on **stdout**; Pi 0.8x printed it on **stderr**. Paperclip's `pi_local` adapter reads stderr first: ```js // Pi outputs model list to stderr, but fall back to stdout for older versions const output = result.stderr || result.stdout; ``` Five extension warnings now live on stderr, so the adapter parsed those, found no `provider model` rows, and refused every run with `Pi returned no models`. The shim that fixes it, `~/.local/share/paperclip-harness/wrappers/pi-paperclip.sh`, puts the table back on stderr for that one flag and passes everything else through unchanged; the Builder agent's `adapterConfig.command` points at it. **Consequences to keep in mind** - **omniroute** is the source of the `omni` names. A name that is missing from the router's `/v1/models` will not appear in Pi, and `models-store.json` caches with an `etag`, so a stale entry can hide a new name until the catalogue is refreshed. - **headroom** keeps its own model registry (LiteLLM) and still has no entries for the router's ids, which is why it answers `502`. It is unrelated to Pi's storage, but when it is fixed it will need the same names.