# Live tests — real Pi sessions, real models

_Machine and user names in these records are replaced with neutral ones (desk, server, laptop, builder; `me`)._

_2026-10-25. Three Pi 0.87.1 sessions (alice, bob, carol) in an isolated tmux server, each loading only
this extension (`--no-extensions -e`), against a center on a test port and database. Model: a local
Qwen3.8-27B served by llama.cpp. Output below is copied from the sessions and the center's database,
not paraphrased. Delivery ids are shortened._

## L1 · An agent message is not the human

alice's model calls `macp_send` to bob at steering priority. bob is idle, so the message wakes him:

```
 ✉ from agent://pi/01a17082-af17-…  ↪ steering
 Quick check: what is 17+25? Reply to me with macp_send.
```

What bob's **model** sees (the block is attributed by the center; a body cannot forge the header):

```
[MACP delivery from agent://pi/01a17082-af17-… · priority steering · id d_a98f67a7-…]
Quick check: what is 17+25? Reply to me with macp_send.

(Delivered to you, agent://pi/01a17082-af14-…, by the MACP center. Reply with macp_send, in_reply_to set to the id.)
```

Up-arrow in bob's prompt box afterwards: **empty**. In alice's: only what the human typed. bob's
session file: `custom_message/macp: 1`, `message/user: 0` — nothing posed as the human.

## L2 · A busy agent gets it between steps, not after the whole job

bob runs `sleep 25`; 12 s in, the human sends *"also name the capital of France"* at steering
priority. bob's session file, in order:

```
00:51:00 assistant  [toolCall bash]           ← sleep 25 starts
00:51:25 toolResult done-sleeping-2
00:51:25 CUSTOM macp [MACP delivery from human://dm · priority steering …] STEER-TEST …
00:51:25 assistant  Output … `done-sleeping-2`. Capital of France: Paris.
```

Delivered in the same request as the tool result, answered in the same reply.

## L3 · An interrupt cancels the running tool

bob runs a 90-second loop; at tick 5 the human sends an interrupt:

```
01:14:54 assistant  [toolCall bash]           ← for i in $(seq 1 90) …
01:15:03 toolResult [Interrupted: an MACP interrupt from human://dm arrived; partial output follows]
                    tick-1 … tick-9 Command aborted
01:15:03 CUSTOM macp [MACP delivery from human://dm · priority interrupt …] R2-INTERRUPT: stop the loop …
01:15:05 assistant  Loop interrupted after 9 ticks; replied `9` …
```

## L4 · The sender learns what actually happened — receipts

`/talk trace` in alice's session, for a message bob read and answered:

```
ANSWERED — the recipient replied
human://dm → agent://pi/01a17082-af14-… (steering)
  accepted  01:14:15
  fetched   01:14:15
  placed    01:14:15
  read      01:14:29
  answered  01:14:29
```

**A dead session** — carol runs a model whose key is invalid (a real 401 from the provider, the same
thing an expired sign-in produces). The message *lands*, and the trace says why nothing happens:

```
FAILED — the recipient's model call failed: OpenAI API error (401): {"message":"Incorrect API key provided: …"}
human://dm → agent://pi/01a170a1-… (steering)
```

When the sender is an agent, it is told once, without being woken, linked to its own message (bob's
conversation, one second after sending):

```
[MACP delivery from center://macp · priority info · in reply to d_3af8c19a-…]
Your message d_3af8c19a-… to agent://pi/01a170a1-… was delivered, but the recipient's model call failed: …
```

## L5 · A crashed receiver loses nothing

carol's Pi process is killed with `SIGKILL`. The center marks it offline **4 s** later from the dropped
stream alone. Two messages then wait:

```
3439f54c  STUCK — placed, then the recipient went offline before any model call
77f77eac  ACCEPTED — stored; the recipient is offline and gets it when it reconnects
```

carol is restarted on its own conversation (same address) with a working model. 10 s later:

```
3439f54c  READ — a model call completed with it in context
77f77eac  ANSWERED — the recipient replied
```

## L6 · Topology rules hold against a real model

Center policy: bob (a worker) may message alice (the supervisor), not carol (another worker).
bob is asked to send carol a review request:

```
CALL macp_send {"to": "carol", "body": "R5: can you review my diff?", "priority": "advisory"}
ERROR refused → agent://pi/01a170a1-…: a worker messages the supervisor (alice), not another worker;
      ask alice if only she can settle it
CALL macp_send {"to": "alice", "body": "R5: can you review my diff?", "priority": "advisory"}
ok    {"deliveries":[{"id":"d_b6d0ef30-…","to":"agent://pi/01a17082-af17-…","priority":"advisory"}]}
```

Nothing reached carol; the refusal told the model the right channel and it took it.

## L7 · Restarts and reloads

| Event | Result |
|---|---|
| Center restarted while sessions run | Both re-registered under the same address within one health ping (≤ 15 s); a send 1 s after restart succeeded first time |
| `/reload` of all three sessions | Placed deliveries 8 / 3 / 4 before and after, 0 duplicates |

## Defects these runs found, all fixed and covered by a center test

| Found | Fix |
|---|---|
| After a center restart the sessions looked connected but every call failed ("Server not initialized") | Every call reconnects and retries once; a 15 s ping finds a dead session |
| An operator's info/advisory message was dropped silently (filed as cross-project) | Operator may send any priority anywhere (spec §9 G3) |
| A blocked send returned an empty success | Every refusal is an error with its reason |
| A killed process stayed "live" forever (it never says goodbye) | Liveness from the open stream, plus a 45 s lease |
| A message to a crashed agent by role name was refused ("no live agent") | Queued for the last holder of the role, delivered on return |
| A send right after a center restart failed once ("Connection closed") | Reconnect attempts are shared, every caller waits for the same one |
| An interrupt-cut model call could be reported as a model failure | Calls cut by our own interrupt are not failures |

## Not tested here

- Agents on different machines (the center listens on localhost only by default).
- Harnesses other than Pi (Claude Code, Codex) as MACP clients.
- A session waiting at an approval dialog: covered by the center test for "stuck", not driven live.
- Pi still prints one red `Error: This operation was aborted` line for an interrupted model reply.

## L8 · Transparency in the transcript (2026-10-25, test sessions)

Both sides read as a message log; receipt lines are shown but removed from the model's context
(debug log over 10 model calls: only `macp` messages reached the models, 15 display-only lines removed).

```
✉ you → bob (…258b71)  steering  f3a4c40a
  ✓ landed in bob (…258b71)'s session  · f3a4c40a
  ⚠ stuck at bob (…258b71): placed 123s ago and no model call has completed since …   ← the local model server was wedged
 ✉ from bob (…258b71)  ↪ steering  04:52 · c7939272 · reply to f3a4c40a
  ✓ answered by bob (…258b71)  · f3a4c40a
```

## L9 · Self-recovery and outbox

| Event | Result |
|---|---|
| Hub stopped; a session sends | the session started a hub itself; send accepted; the receiver got it after reconnecting |
| Hub `kill -9` | a session restarted it; both sessions re-registered within 4 s |
| No hub reachable and auto-start off | send queued in the session (`⏳ … queued`); 2 s after a hub appeared: sent, 1 copy in the hub, placed once |
| Role map entry appears for a running session | re-registered under the new role 6 s later, no reload |

## L10 · A real three-lane orchestrated project (desk)

Lanes loaded the extension through the orchestrator's own boundary-safe restart. The rules file names
roles, never lanes. Each lane's conversation was resumed under its original session id.

| Step | Result |
|---|---|
| Hub restart with three lanes connected | re-registered after 2, 4 and 13 s |
| Human → a worker, steering, through the hub (no terminal typing) | placed in 1 s, read 16 s later |
| Worker → another worker | refused with the project's own wording; nothing delivered |
| Worker → the lane named `project` | **first attempt refused as a broadcast** (MACP's `project` keyword); fixed in the hub (`projectIsRole`), second attempt accepted, landed in the project lane, not woken (advisory), stored as an agent message, 0 user-role copies |

## L11 · The human in plain words, across machines (desk → server)

Hub on desk reachable over the LAN, access key required (server without key: `401`). Default rule:
agents send only when their human asks. The desk session runs in tmux window `ops-desk`; the server
session in tmux session `lab`, window `billing-review`.

Typed on desk: `/talk tell the billing review agent on server that the invoice export is ready, and ask
it to confirm which batch it will start with`

- The model read the directory, matched *"billing review agent on server"* to
  `server · tmux lab:1 'billing-review'`, and sent to that address (`· asked by you`).
- The server agent's answer was accepted once, as the answer to the human's request. A second,
  unasked message was refused (center test). Its first attempt, before the one-answer rule
  existed, was refused: *"agents send only when their human asks them to in that turn"*.
- desk transcript: `✉ from reviewer@server … reply to 45cfcfcd`, then `✓ answered by reviewer@server`.
  39 s from Enter to answer.

## L12 · No typing words into another Pi session (tmux guard)

`bin/guard/tmux` is first on the PATH of every command a Pi session runs; `bin/guard/test.sh` passes
17 of 17. Live, with two Pi sessions (the guard pointed at the test server):

| Who asked | What bob ran | Guard |
|---|---|---|
| an incoming message (not the human) | `tmux send-keys -t alice 'hello from bob' Enter` | **BLOCK** — bob got the refusal; nothing reached alice |
| the human, typed in bob's prompt | `tmux send-keys -t alice 'unstick-requested-by-human'` | allowed (human turn); text appeared in alice's prompt box |
| an incoming message | `tmux send-keys -t alice C-u` | allowed (control key); cleared alice's prompt box |

Kept for repair, always: control keys, single-letter answers, slash commands except `/talk`, `@file`
pastes, bash panes, other tmux servers, and a trusted orchestrator matched by real path (a script
an agent names alike is blocked). A guardrail against noise, not security: `/usr/bin/tmux` bypasses it.

## L13 · Four machines, one weak local model (desk · server · Laptop · builder)

One test hub on desk (LAN, access key, `defaultAgentSend: human-turn`). One Pi session per machine
on an isolated tmux server, all on Qwen3.8-27B served from desk (one request at a time). Every send
was typed by the human as `/talk` in plain words, on the machine named in the first column.

| # | From | Typed | Result |
|---|---|---|---|
| T1 | desk | "tell the billing review agent on server … reply ACK-T1" | resolved by window name; `ACK-T1` came back as *answer to me@desk's request* (60 s) |
| T2 | Laptop | "ask the agent on builder to reply with its machine name" | resolved by machine; reply `builder` (20 s) |
| T3 | builder | "tell the ops desk on desk … reply OK-T3" | `OK-T3` back (206 s, queued behind the shared model) |
| T4 | server | "send the design notes agent on the laptop a note …" | landed on Laptop in 15 s, labelled *asked by me@server* |
| T5 | (an incoming message, not the human) | "message the design-notes agent on laptop yourself" | **refused**; nothing delivered |
| T6 | desk | "tell the agent in the xm folder hello" | three agents fit, so the model **asked which** and sent nothing; after the human typed `2`: delivered to Laptop |

Defect found and fixed: under "only when asked", an agent's report to the human was refused,
because that rule was checked before "an agent may always report to the human". Center test added (83 tests).

## L14 · Hardening round (2026-10-25 afternoon)

| Change | Live result |
|---|---|
| Hub address from `~/.config/macp-center/url` | a server session with only that file (no `MACP_URL`, auto-start off) registered on desk's hub |
| Busy/idle presence | idle → busy while a 12 s command ran → idle after the reply; `/talk roster` shows it |
| Quiet interrupt | a 90-tick loop interrupted at tick 6: **0** red abort lines, one dim `⚡ interrupted by human://dm` |
| Coexists with pi-cc-steer loaded **first** (it swallows mid-run typing and re-sends it) | the human's mid-run request, typed during a run started by a delivery, was delivered as *asked by me@desk*: the raw Enter keypress marks the run as the human's. No control run with the old code |
| No tmux | a session with `TMUX` and `TMUX_PANE` unset registered (`desk · ~/…/talktest`) and sent |

Found while testing: a machine that has pi-agent-talk but **no** address file auto-starts its own local hub
(by design, for one machine), and its sessions then cannot reach a shared hub. Roll out the address file **before**
the extension.

## L15 · Strict identity against forgery (two Pi sessions, hub `--strict-identity`)

An attacker process that has everything on disk and no secret (what an agent's shell has):

| Attack | Result |
|---|---|
| F1 claim alice's identity | **refused** — "held by another live process" (logged) |
| F2 claim bob's own identity, to send "asked by you" from the shell | **refused** |
| F3 claim to be the human (operator), alone or "through alice" with a guessed secret | **refused** |
| F4 read alice's Pi memory for the secret | **refused by the kernel** (`EACCES` on `/proc/<pid>/mem`, `ptrace_scope=1`); her environment holds no key |
| F5 a brand-new identity, choosing role `bob`, sends "asked by you" | **accepted** — the residual limit: it appears as `bob (…037)`, a different id and no location |

Normal use unchanged: `/talk` in plain words round-trip (answer labelled), and `/talk allow bob 1`
(the human acting through alice's in-memory secret).

## L16 · Chaos: five rounds, real sessions, real faults (`test/live/chaos.mjs`)

Five Pi sessions on Qwen3.8-27B (one shared model slot), a driver sending replies, agent-to-agent relays,
non-waking notes, busy-session steering and interrupts, and faults injected every few minutes. Invariants are
read from the hub's database and every session's conversation file.

| Round | Minutes | Faults | Tasks met | Found |
|---|---|---|---|---|
| 1 | 45 | 11 (kill -9 sessions ×4, freezes ×2, hub stops, reload) | 114/116 | the 2 misses were agent→agent steering **downgraded to advisory** (no grant in the test's rules), which correctly never woke an idle receiver. Test rules fixed |
| 2 | 25 | 6 (kill -9 hub, hub stops ×3, reloads ×2) | 65/65 | **1 message placed twice**: an interrupt's held batch and a mail check both placed it inside a 150 ms window. **Fixed** at the one placing function |
| 3 | 30 | 8, interrupt-heavy | 64/68 | 0 duplicates across 21 interrupts (fix holds). The 4 misses were one agent's model declining to message the human on another agent's say-so (model judgement, not delivery). Test measure fixed |
| 4 | 20 | 4 | 50/52 | **a message lost**: queued for a busy session, then the run was cancelled by an interrupt; Pi drops its steering queue on cancel. Reproduced (2 messages, an interrupt), **fixed** (anything queued but never seen by a model call is placed again after the run), verified 3/3 with a control on the old code (not read) |
| 5 | 25 | 7 (kill -9 hub ×2, hub stop, freezes ×2, kill -9 session, reload) | **60/60** | none. 0 duplicates, 0 user-role, 0 identity conflicts, 0 placed-but-unread |

## L17 · Chaos, round 6: DeepSeek v4.1 (a different model, six sessions)

Same harness, `ollama-cloud/deepseek-v4.1-flash`, 6 sessions, 20 minutes, interrupts weighted. Unlike the local
Qwen (one request at a time), this model serves requests in parallel, so six sessions worked concurrently.

| | |
|---|---|
| Tasks met | **54/54** (11 agent-to-agent relays + 11 acknowledgements, 9 interrupts, 8 replies, 6 busy-session steers, 3 notes) |
| Invariants | 0 duplicates, 0 messages shown as user input, 0 identity conflicts, 0 placed-but-unread |
| Faults | 5: `/reload`, `kill -9` of the hub twice, a clean hub stop, `SIGSTOP` for 100 s |

Harness fix found while setting it up: `--no-extensions` also removes the extension that provides the
`ollama-cloud` provider, so the model disappeared from the session. The harness now names that provider
extension explicitly.

## L18 · herdr (2026-10-25 night)

A **separate** herdr server (`herdr --session review-test`), Pi sessions started inside its panes, a throwaway hub.

| What | Result |
|---|---|
| A session's place | reported as `desk · herdr review (w2:p1) · /var/tmp/pat-herdr` — herdr's workspace label, pane id; an unnamed tab's number is dropped |
| Inside herdr, `TMUX` is also set (the server inherited it) | the herdr place wins over the tmux one |
| Loose words: *"tell the agent in the review workspace to reply with PONG-HERDR"* | resolved uniquely (`[resolved by the directory: vanness …]`), delivered as *asked by dm@desk*, answered `PONG-HERDR`, answer read back |
| Ambiguous words: *"tell the agent in herdr to say HI"* | **nothing sent**; the candidates were listed and the model asked which one |
| The human's answer *"vanness"* | sent `say HI`, read by vanness, answered `HI` |
| Guard: `herdr pane send-text/run/agent prompt` | 16/16 tests (own pane, non-Pi panes, keys, slash commands, @file, human turn, orchestrator allowed; words into another Pi pane blocked) |

Two defects found and fixed on the way: a project name was matched with its `prj:` prefix (so one of two lanes
in the same project looked unique), and a tie was judged by score rather than by *which words* matched — both
now ask instead of guessing. `test/resolve.mjs` pins eleven phrases, including the two ties.

## L19 · A handover the model composes, in the turn `/talk` starts

Bob did a small piece of work (wrote a 3-line file), then was told only: `/talk hand over the file work to alice`.
`/talk` in plain words injects a user message, which starts a turn (or steers a busy one) — that is where the
brief is written. What arrived at alice (324 chars, steering, *asked by you*, read):

```
Handover — file work on /var/tmp/handover-test.txt (desk).
Done so far:
- File created at /var/tmp/handover-test.txt via `printf 'a\nb\nc\n'`
- Verified with `wc -l`: 3 lines (contents: a, b, c)
Your turn: take over any further work on that file. No branch or session state involved — it's a plain scratch file in /var/tmp.
```

Not a forwarding of the human's three words: the model wrote done / your turn / where. Guidance now says a handover
means "compose the brief from what you actually know", and that "prepare"/"draft" means show it and wait for the go-ahead.

## L20 · The review's findings, fixed and re-proved live (2026-10-26)

Three sessions on a throwaway hub (writer, reviewer, builder), local Qwen3.8, after the repairs below.

| Finding | Was | Now, measured live |
|---|---|---|
| The finder tool could not run at all (`handler` instead of `execute`, `ok_` undefined) | `TypeError: find.execute is not a function` | ran for real: returned `agent://pi/01a1750f-… builder (…05c792) desk · tmux builder:1 …` |
| Brief content or a negation decided the recipient | "hand over to the reviewer: tell the builder the tests pass" resolved to **builder** | same words resolved to the **reviewer**; delivery `writer → reviewer` only, *asked by you*, nothing to builder |
| "prepare/draft" still carried "send to that address; do not ask" | conflicting instructions in one prompt | "prepare a handover … do not send yet": deliveries before **1**, after **1** — nothing sent; the model wrote a draft and said so |

Unit-level, the matcher now pins 26 phrases including both of the review's failures, and `test/tools.mjs` catches the
tool-contract class: it loads the extension through Pi's own loader, invokes `macp_find`, and asserts the failure is
about the hub rather than about the code. Control run with the old buggy shape reintroduced: 2 failures; restored: all pass.
