Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ a chat channel. In manual mode, each runs as a live session in its own terminal.
| Brainstorms | `pair send request --brainstorm` opens a thread for ideas in the author's lane. The reviewer replies `continue` or `complete` instead of `approve` or `changes`. Its statuses are `brainstorming` and `completed`, and the kind comes from the status, so pair ≤ 0.7.4 rejects the request as corrupt and can't review it as code; both agents need 0.8.0. The request stores `budget:`, `PAIR_MAX_ROUNDS` when it opened, so the agents' shells can't disagree on it; on the last round `continue` is refused. It never escalates. |
| Disputes | Cap of 3 rounds (`PAIR_MAX_ROUNDS`). A review that still says `changes` at the cap marks the thread `escalated`, and both agents stop to ask the user. |
| Concurrency | At most one open thread per direction. |
| Delegation | A subagent inherits the author's pair environment, so the instructions make the parent own the pair loop: subagents return changes without running pair, the parent integrates them (or reviews separate PRs one at a time) in the session's worktree and keeps it stable during each round. Several threads per lane wouldn't stop files moving under a reviewer. One owner serving reviews for subagent worktrees is planned separately. |
| Delegation | A subagent inherits the author's pair environment, so the instructions make the parent own the pair loop: subagents return changes without running pair, the parent integrates them (or reviews separate PRs one at a time) in the session's worktree and keeps it stable during each round. Several threads per lane wouldn't stop files moving under a reviewer. With `pair start --serve-worktrees`, one owner also serves subagents' own worktrees (see Served worktrees). |
| Storage | `~/.pair/<repo>-<hash>/` (or `$PAIR_HOME`). The hash comes from `git rev-parse --show-toplevel`, so each worktree gets its own channel. A reviewer reads the author's files, so both agents must run in the same worktree, and parallel pairs in different worktrees stay isolated. |
| Format | Markdown files with flat YAML frontmatter that the CLI writes. Findings go in the body. |

Expand Down Expand Up @@ -275,6 +275,33 @@ their own sessions or groups can escape it, and killing both supervisors can lea
socket. Stale sockets are reported for manual inspection, never silently replaced. Windows auto mode
is not supported.

### Served worktrees

`pair start --serve-worktrees` lets one owner review its subagents' own git worktrees, as specified
in [docs/specs/serve-worktrees.md](docs/specs/serve-worktrees.md). The author's environment carries
`PAIR_AUTO_OWNER`, the owner's state directory. A subagent inherits it, so its `pair send`, run in
its own worktree and channel, reaches the owner's socket, the only one a Claude author's sandbox
allows, with its worktree root and this run's ID. A request without a root, from an older CLI, is
the root's.

The first request from another worktree enrolls it. The owner checks that it is the canonical top
of a git worktree of the same repository, listed by `git worktree list`, with no manual Codex
session registered in its channel, which the owner computes under its own PAIR_HOME. It then claims
that worktree's auto socket and checks that the requested message is the one pending. Simultaneous
first contacts share one attempt. The claim keeps `pair start` and `pair reset` out of the
worktree, answers `pair status` and `pair reviewer --thread` there, and refuses `pair stop`. The
guardian removes claimed sockets with the owner's if the owner dies.

Rounds wait in one FIFO across all targets, the root included, with at most one entry per target. A
second request for the same message attaches to it, and a caller's disconnect doesn't cancel it. Up
to `--reviewers N` run at once (default 1). Each launch, completion and retry re-checks the
target's identity: its path, inode and git admin directory. A running child's identity is also
checked on each heartbeat, and on a mismatch the owner asks the guardian to stop that one job.
Failures, settings snapshots and reviewer sessions are kept per target and thread. A child's
requests never change the root's resume record, and nothing pair runs writes into a child: its
commands skip the instruction refresh, and every managed prompt carries the reviewer rules rendered
from `prompts/pair.md`.

## Manual reset

`pair reset` moves whole pending lanes into `resets/<reset-id>/` in the current channel, leaving
Expand Down
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ started. `-y` starts without waiting.
| `--reviewer-model MODEL` | CLI default | Reviewer model ID or alias, passed as given (Codex IDs are case-sensitive) |
| `--reviewer-effort LEVEL` | CLI default | Codex: `none` `minimal` `low` `medium` `high` `xhigh` `max` `ultra`; Claude: `low` `medium` `high` `xhigh` `max` `ultracode` |
| `--reviewer-args="ARGS"` | none | More reviewer options, space-separated ([Forwarded arguments](#32-forwarded-arguments)) |
| `--serve-worktrees` | off | Also review subagents' own worktrees of this repository ([Parallel work](#53-parallel-work)) |
| `--reviewers N` | 1 (max 8) | Review rounds at once, across all served worktrees; needs `--serve-worktrees` |
| `--claude PATH`, `--codex PATH` | first on `PATH` | Absolute path to each executable |
| `--timeout SECONDS` | 900 (max 86400) | Limit per review round; the author has none |
| `--grace SECONDS` | 2 (max 30) | Wait between termination and forced termination |
Expand Down Expand Up @@ -351,8 +353,28 @@ are still editing, or in its own worktree, where no auto session serves the requ
When the subagents' work belongs in separate PRs, the author reviews them one at a time: it checks
out each branch in the session's worktree and finishes that PR's review loop before the next. For
PRs reviewed in parallel, run one `pair start --worktree NAME` session per PR
([Worktrees](#6-worktrees)). Approval in each worktree doesn't cover a later merge of their work;
review the integrated result as well.
([Worktrees](#6-worktrees)), or let one session serve its subagents' worktrees. Approval in each
worktree doesn't cover a later merge of their work; review the integrated result as well.

With `pair start --serve-worktrees`, a subagent working in its own git worktree of this repository
runs its own review loop there, through the session's reviewer. The author prompt gives the author
the rules to pass on. The first request from a worktree enrolls it, after pair checks that it is a
worktree of this repository and that the request comes from this session. The session also claims
that worktree's auto-mode socket, so no other session or reset runs there until this one stops.
Reviews wait in one first-come-first-served queue with the session's own, and `--reviewers N` runs
up to N at once (default 1). A review in a worktree that is removed or replaced fails, and its
reviewer is stopped; the others go on.

```sh
pair start --as claude --serve-worktrees --reviewers 2
```

- Pair writes nothing in a served worktree, not even its instruction files: the reviewer's rules
come in its prompt.
- `pair status` in the session's worktree lists what is running, queued and failed. In a served
worktree it names the serving session, and `pair reviewer --thread current` fixes that worktree's
failed round. `pair stop` runs in the session's worktree, and stops everything.
- Worktrees stay served until the session stops. Pair never creates, merges or removes them.

<a id="5-worktrees"></a>
<a id="52-worktrees"></a>
Expand Down Expand Up @@ -517,6 +539,10 @@ and remove `~/.pair` from the sandbox settings.
- **Claude author socket.** Pair grants a Claude author only this session's control socket, through
`--settings`; your settings files are unchanged. If managed policy denies it, the request fails
with the required setting named and stays on disk.
- **Served worktrees.** With `--serve-worktrees`, the owner reviews a worktree only after checking
that it is a git worktree of the same repository, that the request carries this run's ID, and
that the owner can claim its socket. Each round re-checks the worktree's identity. Subagents hold
the author's environment, so this is workflow routing, not an identity boundary between them.
- **Reviewer output.** A failed round's error quotes the reviewer's own message, shortened, with
control characters escaped and labeled as reviewer output rather than instructions.
- **MCP server.** `pair mcp` runs outside Codex's sandbox, so it only reads the channel, writes only
Expand Down
Loading
Loading