Files
paperclip-harness/docs/CLEAN-TESTS.md

9.9 KiB
Raw Blame History

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

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

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 <id|name> 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:

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

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

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

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/<pid>/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:

// 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.