Files
paperclip-harness/docs/PLAN-INTERFACE.md

35 KiB

The plan, and how the interface sits on Paperclip

Written 2026-11-10, after a wrong turn: I split every workspace into three panes without being asked, and it broke the working layout. It has been reverted. This document says what exists, what is proposed, and which Paperclip endpoint each thing reads or writes. Nothing here is invented: every endpoint below was called on this machine and answered.

What is on screen today

Seven workspaces, one pane each: Shell>shell, Board>board, Work>work, Reply>reply, New task>new, Project>project, Repair>repair. Inside a pane the program draws several panels (Board draws three), and you switch focus with Tab. That is the state that works.

Paperclip's objects, and where each one is seen

Paperclip object Endpoint, measured Where it appears now
company GET /api/companies/{id} not shown; the id is in the project file
project GET /api/companies/{id}/projects Project>project
task GET /api/companies/{id}/issues, GET /api/issues/{id} Board>board, Reply>reply task panel
blockers of a task GET /api/issues/{id}/diagnostics/blockers Board>board, per row
comment thread GET /api/issues/{id}/comments Reply>reply
runs of a task GET /api/issues/{id}/runs Work>work
activity GET /api/issues/{id}/activity Board>board needs-you tab, Work>work
cost per task GET /api/issues/{id}/cost-summary Board>board detail, Work>work facts, Project>project costs
worktree and branch the task's currentExecutionWorkspace Work>work change and facts
agents GET /api/companies/{id}/agents Project>project facts and limits, by name and budget
an agent's runs, company-wide GET /api/companies/{id}/heartbeat-runs nowhere yet
a run's log GET /api/heartbeat-runs/{runId}/log — 300 KB of JSON lines for one finished run nowhere yet
a run's events GET /api/heartbeat-runs/{runId}/events nowhere yet
agent pause, resume, wake POST /api/agents/{id}/pause, /resume, /wakeup nowhere yet
agent settings PATCH /api/agents/{id} — accepts role, adapterConfig (the model), runtimeConfig (heartbeat), budgetMonthlyCents, status nowhere yet

So the honest answer to "where do I see the Pi agents working": you cannot, yet. The data is all there and the runs are already recorded — 281 of them on this instance — but no screen shows them. That is the largest missing piece, and it is the next workspace.

Missing: the Agents workspace

This is the one the person keeps asking for and it does not exist. Proposed as one pane, the same shape as the others, drawing three panels with tabs:

Agents>agents
╭─ agents   roster   runs   ╮
│ Campfire    idle
│  ● Builder   running  AUD-163  started 14:12  1m 20s  1 cent
│  ● Hub       idle      last run 13:58
│  ● Documenter idle
╰───────────────────────────╯
  • roster — every agent: name, state (idle, running, needs input, and Paperclip's own status), current task if any, spend this month, budget. From GET /api/companies/{id}/agents.
  • runs — the company's run list, newest first: agent, task, state, what woke it, start, duration, cost. From GET /api/companies/{id}/heartbeat-runs (281 today).
  • log — the tail of the chosen run, from GET /api/heartbeat-runs/{runId}/log. This is the closest thing to "watching Pi work": the log grows while the run is live, and lastOutputAt/lastOutputBytes on the run say how fresh it is.

Actions, all with the user key, all reversible:

  • enter on a run → its log in the log tab.
  • p pause, P resume an agent (POST /api/agents/{id}/pause|resume).
  • w wake an agent (POST /api/agents/{id}/wakeup).
  • e open its settings (see below).

Missing: working on an agent — role, model, cost

Paperclip owns these, so the interface edits Paperclip, never a local file. One more panel, Agents>settings, or a tab inside Agents>agents:

Field Paperclip field Endpoint
name and job title name, title PATCH /api/agents/{id}
role role same
who it reports to reportsTo same
model (the LLM) adapterConfig.model same, with replaceAdapterConfig care
heartbeat on/off, concurrency runtimeConfig.heartbeat same
monthly budget budgetMonthlyCents same
pause / resume POST /api/agents/{id}/pause, /resume separate calls

Two rules that fall out of this and are not negotiable: a change to a model applies to that agent's next run, never to one in flight; and a budget change is recorded with who changed it, because Paperclip's audit trail is the only record we have.

Tool panels: Lazygit, Hunk, a shell, the review

Both are installed on this machine and I verified the paths: /run/current-system/sw/bin/lazygit and /etc/profiles/per-user/sam/bin/hunk.

They are not to be reimplemented. The proposal is one key in Work>work:

  • t opens a chooser: shell · lazygit · hunk · git log · ocr.
  • The chosen tool opens as a pane in the current workspace, beside the panel you were reading, and takes focus.
  • q or esc closes it and focus returns exactly where it was.
  • Nothing is created until you ask, so the layout stays as it is today.

This is the answer to "I thought we had Lazygit and Hunk": they are installed, they are not wired in, and wiring them in is one pane-opener plus a key.

The review: ocr is installed and has already run

ocr is at /home/sam/.local/bin/ocr, version open-code-review v1.13.6. It is in the project's bin/merge and bin/review, and it has blocked a real merge: on AUD-165 it reported a critical finding (rm -rf "$HOME/tmp" deleting /tmp when HOME is empty) and the merge was refused. The Repair screen once said "not on the path" because a pane started by TUIOS gets a smaller PATH than a shell; that was fixed by looking in the usual places.

Where it belongs in the interface: a review panel inside Work>work, showing the last verdict for the chosen task, its findings, and the blocking ones in red. The merge stays a command (bin/merge), because a merge is a decision with a lock, not a keystroke.

Mouse-1 and Switch

Measured just now: not installed — no mouse, mouse1 or switch binary on this machine, and nothing obviously serving them locally. So nothing can be built on them yet without adding software first.

Where they would fit if added: Mouse-1 is a small probability model that answers typed questions; Switch turns a state into those questions. The honest use in this factory is routing and judgement that must be cheap and repeatable — "is this task ready", "does this comment answer the question", "which agent should take this" — rather than prose generation. That is a later milestone, after agents and the review panel, and it needs a decision about installing both, plus a measurement of whether they are better than the existing cheap model already configured for agents (omni/deepseek/deepseek-v4-flash).

The order I propose

  1. Agents workspace — roster, runs, log. This is the missing view of Pi working, and it is read-only, so it cannot break anything.
  2. Agent settings — role, model, budget, pause/resume. First write surface aimed at agents rather than tasks.
  3. Tool panels — t opens Lazygit, Hunk, a shell, ocr in the current workspace.
  4. Review panel in Work>work — the last verdict and its findings, beside the diff.
  5. Mouse-1 / Switch — only after the above, and only after they are installed and measured.

Each of the first four is one pane, one program, no change to the working layout. Nothing in this list requires splitting workspaces again.


Answers to the open questions, 2026-11-10

Where settings are set. Agents>agents has a fourth tab, settings, which edits the chosen agent: role, job title, model, monthly budget, heartbeat. enter edits the field in place, esc cancels, and p / P / w pause, resume and wake. Each save is one PATCH /api/agents/{id} merged into that agent's existing configuration, so a model change never wipes its folder or instructions. Project>project shows spend and limits read-only, because Paperclip owns them and a second editable copy is how two screens start disagreeing.

Spend. Read from cost-summary per task (Project>costs, Board>board, Work>work). The monthly budget is per agent, set in Agents>agents settings. There is no company-level control on any screen yet.

Skills for sub-agents. Not in the interface. Paperclip has the endpoints — GET /api/agents/{id}/skills, POST /api/agents/{id}/skills/sync with add, remove or replace — so a read-only skills row is cheap and a full editor is a later step. The rule worth keeping: an agent gets the skills its job needs and no more, which the add/replace model already expresses.

Where work is added. New task>new, which writes a Paperclip task. A second place to write tasks (Tuxedo, a notes file, a card board) would be a second owner for the same fact and is exactly the failure the old project had. Personal notes are different from tasks: if they are wanted, they belong in the project's own .harness/NOTES.md, and Tuxedo can read it per project without pretending to be a task board.

office --host from another machine. Not implemented, and the runbook says why: session creation needs a real terminal, so a person creates the session (tuios new --host nixos-desktop "new test project") and then runs bin/office <folder> inside it, on the machine that owns the folder. --host today only records the host in the project file. Driving bin/office on .27 from .13 would need ssh plus a remote bin/board, which is a deliberate later step, not an accident.

Seeing and switching projects. Not built. Paperclip has the list (GET /api/companies/{id}/projects); what it cannot know is which folder on which machine each one lives in. The proposal: a small local registry of adopted projects (~/.config/paperclip-harness/projects.toml: project id, folder, host, session), filled by bin/office, and a Project>projects tab that lists them and switches by writing the project into the current folder's .harness/harness.toml. A project on another machine can only be switched to if the pane can reach it, so it would be shown as on .27, not offered.

Artifacts. Paperclip work products: an uploaded file, a pull request, a preview URL, a commit or a branch, attached to a task by an agent. They are inspectable records of what a task produced. Nothing shows them yet; the natural home is a Work>artifacts tab listing them with their links, read from the task's work products.

"Workspaces" in Paperclip. Paperclip has no workspaces. It has companies, projects, issues (tasks), agents and runs. A workspace is TUIOS's word for a page; a pane is a terminal inside it. When Paperclip says workspace it means a project's code folder (currentExecutionWorkspace), which is the git worktree a task runs in.

Charts. TUIOS ships plot (Go), loom and TML. Go is the better fit here because the panels are already Go and the data is already in memory: a spend-by-day chart in Project>costs is a few dozen lines against tuios.dev/plot, where a TML surface would be a second language and a second round trip. Worth doing once the functional tests pass.

Mouse-1 and Switch. Still not installed on this machine. Unchanged from the earlier note: they would serve cheap repeatable judgement, later, after they are installed and measured.


Findings for the next round of decisions, 2026-11-10

Everything below was measured on this machine. Nothing here is proposed without saying so.

Skills per agent — the API already models it

GET /api/agents/{id}/skills returns, for each agent:

mode: "persistent"
desiredSkills: ["paperclipai/paperclip/paperclip"]
entries: [
  { key: "company/<id>/obsidian-cli", runtimeName: "obsidian-cli--b74ae42966",
    desired: false, managed: true, state: "stale",
    sourcePath: "…/instances/default/skills/<id>/obsidian-cli",
    targetPath: "/home/sam/.pi/agent/skills/obsidian-cli--b74ae42966" }
]

So Paperclip already models exactly what is wanted: a desired set per agent, a managed set actually installed, and a state (stale today for obsidian-cli on Builder, which is worth fixing on its own). POST /api/agents/{id}/skills/sync with add, remove or replace sets the desired set; replace is the operation that means "this agent gets these and nothing else", which is how a sub-agent's context is kept small.

Company level, there is more: GET/POST /api/companies/{id}/skills (the catalog), /skills/{id}/versions, /skills/{id}/audit, and GET/PUT /api/companies/{id}/skill-policy with /skill-policy/evaluate, which is a company-wide rule about which skills may be used.

Proposed in the interface: Agents>agents settings tab gains two rows — skills (the desired set, read-only at first, with add/remove later) and skill source (company catalog or the agent's own). A full skill browser is a later screen, not a panel.

Memory — three different things wearing one word

What State here What it does
engram installed, engram v1.16.1, MCP server mode, no pi MCP entry yet persistent memory for agents, over HTTP or MCP
headroom running, headroom-proxy v0.24.0, healthy at 192.168.20.13:8787, memory backend disabled, compression enabled compresses prompts on the way out; it is a proxy, not a store
pi memctx in use in this session the memory gateway that injects notes into a prompt

They are not alternatives: engram stores, headroom compresses, memctx injects. For agents, the only lever Paperclip gives us is per-agent configuration — adapterConfig (the folder, the model, the instructions bundle) and runtimeConfig — so memory is set per agent by pointing that agent at the proxy or the storage it should use, not by a global switch.

Proposed: a memory row in Agents>agents settings showing which of the three that agent uses, read from its adapter configuration; and nothing more until one agent has been run both ways and the difference measured.

MOUSE-1

  • MOUSE_API_KEY is set in ~/.config/environment.d/10-secrets.conf (name checked, value never printed). JEV_API_KEY is set as well.
  • No mouse, mouse1, jev or switch binary is installed.
  • The previous project already wrote the policy, in ~/paseo/projects/tuios_coding_harness/plan/*: MOUSE-1 is "triage only: label, score, route, escalate. Never a gate, never a permit", and explicitly ruled out for deciding a merge or allowing a tool call, because a cheap classifier that fails open on a network error turns a green run into a lie.

That policy is right and should be adopted unchanged. Its place in this interface, if it is added: Board>board needs-you could be ordered by a MOUSE-1 score, and Work>review could use it to pre-sort findings. Never the merge, never an approval.

Lazygit

Already wired: t in Board>board or Work>work, then choose Lazygit. It opens beside the pane, in the same workspace, and q closes it. Hunk, Yazi, Neovim, fzf, a shell and ocr are in the same chooser.

The old office command, and what ours is missing

The previous project's office does four things, in order:

  1. makes the folder and remembers the host in <folder>/.harness/host;
  2. runs new-project to write the project scaffold — settings.toml, one file per role, verify.sh, the per-screen notes — writing only what is missing, never overwriting;
  3. refuses to continue if the TUIOS session does not exist, and prints the exact tuios new <session> line to run;
  4. runs its board to build or repair the pages, then prints how to attach.

Ours does 1, 3 and 4 (with a different file: .harness/harness.toml). It does not do 2: there is no scaffold, no per-role file, no verify.sh, no page notes. That is a real gap and the cheapest useful one to close.

Dashboard — the API already answers it

GET /api/companies/{id}/dashboard returns 1.8 KB:

agents: {active: 6, running: 0, paused: 0, error: 0}
tasks:  {open: 8, inProgress: 0, blocked: 4, done: 147}
costs:  {monthSpendCents: 11, monthBudgetCents: 0, monthUtilizationPercent: 0}
pendingApprovals: 0
budgets: {activeIncidents: 0, pendingApprovals: 0, pausedAgents: 0, pausedProjects: 0}
runActivity: [14 entries]

That is a whole dashboard in one call, and it is cheap enough to poll. Proposal: a Project>overview panel showing those four blocks and a sparkline of runActivity, drawn in Go with tuios.dev/plot rather than embedding Paperclip's web dashboard.

Not doing

Artifacts, as instructed. Nothing about them will be added.


Pi agents: how their configuration actually resolves

Advice only — nothing here was built. Every fact below was read from the installed code, the config files, or the API on this machine.

Your picker, and what it really writes

pi-config is an extension with five commands: /config-add, /config-remove, /config-list, /config-show, /config-setup. picker.ts is the overlay — a filtered list of ~/.agents/skills and ~/.agents/extensions showing which are active in the project and which are active globally. The commands write JSON, at:

<project>/.pi/settings.json      ← project settings (the picker's "-l" mode)
~/.pi/agent/settings.json        ← global settings

and the shape they manipulate is a packages array of sources and filters:

{"packages": [
  "npm:pi-memctx",
  {"source": "git:https://gitea.lab.audasmedia.com.au/sam/pi-config",
   "extensions": ["extensions/pi-config/index.ts", "extensions/tavily-search/index.ts"]},
  {"source": "/home/sam/.agents", "skills": ["nixos-workflow"], "extensions": []}
]}

So yes: this is a stable JSON contract, and anything that can write a file can formulate an agent's context the way the picker does. The question is not whether it can be written, but which one Pi actually reads on a Paperclip run.

What Pi reads, in order

From Pi's own documentation:

  • global: ~/.pi/agent/skills/, ~/.agents/skills/
  • project, only after the project is trusted: .pi/skills/, .agents/skills/ in the working directory and its ancestors up to the git root
  • packages and the settings skills array
  • command line: --skill <path>, additive even with --no-skills

Settings merge the same way: user settings, then <cwd>/.pi/settings.json.

What Paperclip actually runs

From @paperclipai/adapter-pi-local, a run is:

pi --mode json
   --append-system-prompt <rendered system prompt>
   --provider <p> --model <m> --thinking <t>
   --tools read,bash,edit,write,grep,find,ls
   --session /home/sam/.pi/paperclips/<timestamp>-<agentId>.jsonl
   --skill <a directory Paperclip builds from that agent's desired skills>

and before the run it symlinks the agent's desired skills into ~/.pi/agent/skills/<runtimeName>.

Four consequences, and they matter:

  1. Skills are already per agent — desiredSkills plus POST /api/agents/{id}/skills/sync. That is the supported lever, and it is what the --skill directory is built from.
  2. The isolation is not complete. Injected skills are symlinked into the shared global directory and are not removed when another agent runs, and Paperclip does not pass --no-skills. So a skill an agent once had can remain visible to another agent.
  3. The tool list is fixed at read,bash,edit,write,grep,find,ls. An agent cannot gain a tool by being given an extension unless the adapter is changed.
  4. Project-local .pi/settings.json is unlikely to be honoured for Paperclip runs. Pi only loads project settings from a trusted path. ~/.pi/agent/trust.json holds 14 entries and none of them is a Paperclip worktree or ~/paperclip-agents. Worse, a worktree path changes with every task (…/worktrees/AUD-165-…), so there is nothing stable to trust.

So what is the honest advice

  • Agent identity, instructions, model, budget, and skills: Paperclip. Those are per-agent already and they are what a run uses. The instructions bundle at ~/.paperclip/instances/default/companies/<co>/agents/<id>/instructions/AGENTS.md is the real per-agent context control, and it is editable through the API.
  • Extensions: one global set, not per agent. There is no per-agent extension mechanism in this adapter. Pretending otherwise would produce agents whose behaviour depends on which one ran last.
  • Folder-level cleverness: keep out of the critical path. Writing .pi/settings.json into a project is useful for you when you open Pi in that folder by hand; it is not reliable inside a Paperclip run, because of trust and because worktrees are new paths each time.
  • If per-agent extensions are genuinely wanted, the change is in the adapter: pass a per-agent config directory or --no-skills plus an explicit set, and remove stale links. That is a Paperclip feature, not something our terminal can arrange from outside.

History: yes, and it is richer than the run log

  • ~/.pi/paperclips/*.jsonl — 178 files on this machine, one per run, named <timestamp>-<agentId>.jsonl, and each is the full conversation (session start, model changes, messages).
  • GET /api/agents/{id}/task-sessions — maps a task to its session file and working directory.
  • GET /api/heartbeat-runs/{runId}/log — the run log already shown in Agents>agents.
  • GET /api/agents/{id}/runtime-state — the agent's current runtime state.

A history panel is therefore feasible without new plumbing: list an agent's sessions from task-sessions, open one, and render the JSONL transcript. That is the natural next panel after the functional tests, and it is read-only.


Correction: per-agent skills are the design, not an accident

The section above gives the wrong impression. Here is the accurate version.

Each agent has its own skill set, and that is stored in Paperclip

GET /api/agents/{id}/skills returns, per agent, desiredSkills and the entries actually managed for it. Builder had its own list; Documenter has another. Setting it is one call:

POST /api/agents/{id}/skills/sync
{ "mode": "replace" | "add" | "remove",
  "desiredSkills": ["company/<id>/nixos-workflow", "paperclipai/paperclip/paperclip"] }

replace means "this agent gets exactly these". At run time the adapter builds a directory from that agent's desired set and passes it as --skill, and symlinks those skills into ~/.pi/agent/skills/<runtimeName> so the run can find them. So: different agents, different skills, no problem — this is already the supported path and it needs no picker.

The caveat I over-stated is narrow: the symlinks stay in the shared global directory after a run, and Paperclip does not pass --no-skills, so a skill an agent used once can remain discoverable by another agent afterwards. That is a leak to know about (and a good argument for replace plus a periodic clean), not "one set for everybody".

Where skills come from: GET /api/skills/available, the company list at GET /api/companies/{id}/skills, and the catalog at GET /api/skills/catalog. A skill there can be assigned to any agent with the call above.

Extensions per agent: yes, through the command

The adapter's configuration document says the agent's command defaults to pi and can be set per agent:

adapterConfig.command    defaults to "pi"
adapterConfig.env        KEY=VALUE pairs

So an agent can be given its own wrapper:

~/paperclip-agents/builder/pi-wrapper.sh:

#!/bin/sh
exec pi --extension npm:pi-memctx --extension git:https://gitea…/pi-config/extensions/tavily-search/index.ts "$@"

and PATCH /api/agents/{id} with {"adapterConfig": {…, "command": "…/pi-wrapper.sh"}}. The adapter appends its own arguments (--mode json, --session, --skill, --tools, --model) to whatever the command is, so the wrapper is additive and cannot break the run. env is the same lever for environment variables.

That is how per-agent extensions become real today: one wrapper per agent role (builder, documenter, reviewer), each listing the extensions that role may use. It also gives a place to set --no-skills if strict isolation is wanted, and a place to keep the tool list deliberately small.

History: three options, in order of fidelity

  1. Open the real Pi session in a pane. Paperclip writes every run to ~/.pi/paperclips/<timestamp>-<agentId>.jsonl and starts Pi with --session <file>. A pane running pi --session <that file> gives the actual conversation, with Pi's own interface, and can be continued. Highest fidelity, zero new code — a tool-pane entry that takes a file path. Two writers on one session file is the risk, so this is for sessions that are finished, or for a deliberate hand-over.
  2. A read-only transcript panel. Parse the JSONL and draw the messages in Agents>agents as a fourth tab. Integrated, safe, no second process; less faithful than the real thing.
  3. pi --resume in a tool pane, and pick the session from Pi's own list. Simplest of all, but it makes you find the session by eye.

GET /api/agents/{id}/task-sessions maps task → session file and working directory, so option 1 and 2 both have a reliable index and neither needs guessing.


Confirmed: what the picker emulation needs, and what it can actually do

Researched by doing it, on this machine, with the board key. Everything below returned 200 or 201 unless it says otherwise.

The gap, measured

Set Where Count
what the pi-config picker lists ~/.agents/skills 21 (archify, caveman, nixos-workflow, tuios, …)
what Paperclip can assign GET /api/companies/{id}/skills 9 (agentmail, obsidian-cli, paperclip, project-docs, system-architect, …)

So eleven skills a person can see in the picker cannot be given to a Paperclip agent today, because Paperclip has never heard of them. That is the whole problem, and it is smaller than it looked.

What works, in order

  1. Copy the skill into the company's managed directory. A local import is refused from anywhere else — POST …/skills/import with a path under ~/.agents returned 403 skill_workspace_boundary_denied, "Import from a configured Paperclip workspace or the company managed-skill directory". Copying to ~/.paperclip/instances/default/skills/<companyId>/<name> and importing that path returned 201:

    {"key": "local/9a9eba3826/nixos-workflow", "slug": "nixos-workflow", …}
    
  2. Assign it to one agent, and only that agent:

    POST /api/agents/{agentId}/skills/sync
    {"mode": "add", "desiredSkills": ["local/9a9eba3826/nixos-workflow"]}   → 200
    

    Verified on Documenter: its list went from three skills to four and back to three after a remove. Different agents, different lists — as expected.

  3. Extensions have no Paperclip equivalent. ~/.agents/extensions holds them; Paperclip's skill model does not. The per-agent lever is adapterConfig.command, which defaults to pi and is appended to by the adapter. A wrapper per agent that passes --extension … and then exec pi "$@" is the equivalent of pi-config for a Paperclip run.

So the emulation is this, and nothing more

A picker that does what pi-config's does, for Paperclip agents:

  • reads ~/.agents/skills and ~/.agents/extensions (the same source the picker uses), the company skill list, and the chosen agent's desiredSkills;
  • marks each entry: available but not in Paperclip · in Paperclip · on this agent;
  • on choosing an unavailable skill: copy it into the managed directory, import it, then skills/sync it onto that agent;
  • on removing: skills/sync with remove;
  • for an extension: add it to that agent's wrapper file and point adapterConfig.command at the wrapper.

That is a small command or one panel, not a new subsystem, and every step above is already verified.

A change made while checking this

nixos-workflow was copied into the company's managed directory and imported, so it is now in Paperclip's list and can be assigned to any agent. It is assigned to nobody. Nothing else was changed; Documenter's skills were put back exactly as they were.

Pi history — the cheap answer is probably enough

The instinct is right. Every run is already a file:

~/.pi/paperclips/<timestamp>-<agentId>.jsonl     178 of them here

GET /api/agents/{id}/task-sessions maps a task to its session file and folder. So the cheapest useful thing is a tool-pane entry that opens ~/.pi/paperclips in Yazi, or a pane running pi --resume that lists sessions and opens one with Pi's own interface. Both are minutes of work and neither invents anything.

A transcript tab inside Agents>agents is the integration option: read the JSONL, draw the messages, no second process. Worth doing only if the file browser feels like a detour after a week of use. Recommendation: do the cheap one now, decide later.

The project's check: .harness/check.sh

This is a script in the project folder that exits 0 when the project still passes its own tests. It answers "did this change break the project", not "is this task finished".

What decides a task is finished, in order of authority:

  1. the agent, by setting the task's status — this is Paperclip's own model and nothing in our software can overrule it;
  2. a reviewer, if a review stage is configured on the task (Paperclip's execution policy);
  3. ocr, at merge time, which can refuse the change.

The project's check is a regression check at the same point as a CI pipeline: bin/merge runs it in the task's worktree before merging, and again on the base branch after merging, and rolls the merge back if it fails. It never decides that a task is finished.

Why keep it at all: an agent says it is done, a review says the diff looks right, and neither proves the build still works. One command does. If a project has no check, bin/merge says so and the review decides alone — that is the current state of this sandbox.

Naming: check.sh is the current name. .harness/verify.sh was the earlier project's name and is still honoured if present, so an adopted folder keeps working. bin/office writes check.sh with a comment explaining what to replace.

Headroom, concretely

Headroom is a proxy that accepts the same HTTP API as the model router and shortens long prompts before forwarding. Measured here:

GET  http://192.168.20.13:8787/v1/models   → 200, 486 models (auto/best-coding, deepseek/…)
GET  http://192.168.20.13:8787/health      → 200, healthy, compression on, memory off
upstream (config `openai_api_url`)          → http://192.168.20.13:20129/v1

The agents' provider omni already points at http://192.168.20.13:20129/v1, which is the same upstream headroom forwards to. So headroom can sit in front of it.

How to use it for one agent. Two steps, and only the second is per agent:

  1. Add a provider to ~/.pi/agent/settings.json (global, once):

    "headroom": {
      "baseUrl": "http://192.168.20.13:8787/v1",
      "apiKey": "<the same key the omni provider uses>",
      "api": "openai-completions"
    }
    
  2. Set that agent's model in Agents>settings to headroom/auto/best-coding (or any model id from the list above). Paperclip stores the model per agent, so exactly one agent changes.

Would it work. The endpoint answers the OpenAI-style API that Pi's openai-completions provider speaks, and lists the models Pi would ask for, so the pieces line up. It is still a hypothesis until one run is made through it: the unknowns are whether the proxy passes Pi's tool-calling requests unchanged, and what compression does to a prompt with large tool results. The measurement is one run: compare cost, duration and result quality for the same task with and without the provider.

Per project is not possible. The provider is chosen by the agent's model, and agents belong to the company, not to a project. The finest granularity is one agent, which is what the Agents>settings model field already gives.

Measured afterwards: it does not work yet, and why

A provider entry was added and removed again after testing, because requests fail:

POST http://192.168.20.13:8787/v1/chat/completions   → 502 {"code":"proxy_error"}
POST http://192.168.20.13:8787/v1/messages           → 401 "invalid x-api-key" (Anthropic shape)
POST http://192.168.20.13:20129/v1/chat/completions  → 400 (the router's own model naming)

Headroom's backend is anthropic: it speaks the Anthropic Messages API and authenticates with an Anthropic key, which this machine does not have. The omni router uses an OpenAI-shaped API with its own key. So headroom cannot be put in front of the agents as things stand.

To use it, one of these is needed:

  1. an Anthropic-compatible credential in headroom, and agents moved to an Anthropic-shaped provider;
  2. or headroom configured to accept the omni router's OpenAI API and key;
  3. or nothing — the omni router already does model routing, and headroom's only addition would be prompt compression.

The provider entry was removed so no agent can be pointed at a failing endpoint by accident.

Mouse-1, measured

The person supplied docs/MOUSE-1-NOTES.md, written from measurements on 2026-11-08/09. The facts that shape the design:

  • POST https://api.mouse.dev/v1/decisions, Authorization: Bearer $MOUSE_API_KEY, body {model, state, questions:{id:{type:"choice", instructions, criteria}}}; the answer returns choice, confidence, probabilities.
  • Cold start is minutes. The first call after idle returned 503 engine_timeout after 30 s; the retry returned 200 in 13 s; warm calls were 1044 ms then ~489 ms.
  • The same state and options gave the same choice at ~0.98 confidence. It behaves as a classifier, not a writer.
  • Proven useful: failure routing (redo / stronger model / environment / person), unblock triage, intake clarity, task sizing. Measured in the old project as bin/route-failure.
  • Ruled out: the merge decision, judging test results, anything on a keystroke path, vague options, and being the only record of a decision.

Where it would go in this interface, if added: Agents>agents → a failure on a run offers f, which asks Mouse-1 one question with four options and writes the recommendation, the confidence and the options as a comment on the task. A person still decides. Expected cost of the first call after idle is 30 seconds, so f must show "asking, this can take half a minute" rather than appear to hang.

Mouse-1, corrected

Earlier notes in this file said "software is not installed". That was wrong: Mouse-1 is a hosted API, not a program. What exists here is MOUSE_API_KEY; what is missing is a client integration — an HTTP call like any other. No install is needed, and nothing has to run locally. The old project's policy still applies: triage only, never a gate, never a permit.