9.9 KiB
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
Repair>repair→ every check passes.Shell>shell→ the box at the top says the project folder is…/test-alpha.Board>board→ "no tasks in this project".- Press
Ronce. All eight panes come back, still pointing attest-alpha.
Step 5. One task, end to end
New task>new: titleAdd a STATUS.md with one line, descriptionCreate 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. Assignyes. PressEnteron the last field.Board>board: the task appears and goestodo→in progress→done.Agents>agents:)to runs,↑to the newest run,Enter→ its log.Work>work:changeshows one file, one line.reviewsays no review recorded yet.Work>work→vstarts a review; the tab shows it run.Work>work→m→ the dialog names the branch andmain→y. The merge tab shows the rebase, the check, the review andmerged 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
Project>project→projectstab lists bothspike-2026-11-10andharness-repo, each with its folder and machine.Board>board→ empty, because this project has no tasks yet.New task>new→ createAdd a line to README, descriptionAdd the line "tested by the harness" at the end of README.md. Change nothing else. Commit it., assignyes.- Watch it in
Agents>agents, read the diff inWork>work. Do not merge it — this is the repository's own history. Cancel the task withcinBoard>boardinstead, and confirm ingit logthat 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>costsandBoard>the chosen taskreport0 cent(s)and0 tokens outover four runs.usageJsonon the run isnull: thepi_localadapter does not capture token usage from Pi, so cost cannot be shown for these runs. Open item. ocrreviews code, not Markdown. A documentation-only change getsstatus: skipped,selected: [].bin/mergealready 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 thechangetab 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.
boardmatched 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. Nowopen_panereads/proc/<pid>/cmdlineand adopts a pane only when the harness is what is running; otherwise it closes the pane and opens a fresh one.shellis exempt: it is a shell either way. Board>boardrrefreshed the task list but not the selected task's detail, so "last run running" stayed on screen minutes after the run finished.rnow 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
omninames. A name that is missing from the router's/v1/modelswill not appear in Pi, andmodels-store.jsoncaches with anetag, 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.