Persistent Claude-Codex pairing inside one Herdr tab. Use for live peer work, workflows requesting a pair, or any inbound `[agent ...]` / `[herdr-pair control ...]` line — including after context compaction; resume the exact session and reply through the bundled transport.
Installs into .claude/skills of the current project.
Are you the author of Herdr Pair?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/hcaiano-herdr-pair)
---
name: herdr-pair
description: "Persistent Claude-Codex pairing inside one Herdr tab. Use for live peer work, workflows requesting a pair, or any inbound `[agent ...]` / `[herdr-pair control ...]` line — including after context compaction; resume the exact session and reply through the bundled transport."
---
# Herdr Pair
Pair Claude and Codex in one Herdr tab. Keep the pair and its `sid` alive across
tasks, accepted work cycles, and context compaction. The user can read and
interject in either pane. Keep protocol headers and identifiers literal.
## Preconditions
Set `SKILL_DIR` to this skill directory and
`PAIR_SCRIPT="$SKILL_DIR/scripts/herdr-pair.mjs"`. Always use that absolute
path; the project cwd is unrelated to the installed skill path.
Require `herdr` with the agent automation commands (`herdr agent start`,
`herdr agent prompt`), `jq`, and `trash` on `PATH`, plus `HERDR_ENV=1`. If
any is missing, stop and tell the user to install or start (or update) Herdr.
Before any Herdr mutation, read and execute
[Caller pane proof](references/caller-pane-resolution.md). It resolves the
explicit task repository, proves the unique caller from its own process
ancestry — falling back to conversation markers when that ancestry matches
anything other than exactly one pane — and returns `PAIR_PROOF` plus the pinned
`PAIR_ID`, including `--tab-id`. Stop when that proof does not complete exactly.
## Guardrails
1. Scope every pane operation to the transcript-proven `workspace_id`,
`tab_id`, `pane_id`, agent kind, terminal identity, and repository. Never
address a pane discovered only by workspace, focus, label, cwd, direction,
session metadata, or pane number.
2. Use `herdr-pair.mjs send` as the sole partner transport. Reserve normal
assistant output for a header-free user handoff. `SendMessage`, subagent
messaging, and direct pane writes are different channels.
3. Give one agent the write lease for each file scope: owner, target files,
forbidden changes, validation, and stop point. The partner stays read-only
on that scope until handoff.
4. A submitted user message overrides partner traffic. Surface any conflict in
the next reply. One failed partner spawn ends the attempt.
5. Between work steps return to the prompt; never hold the pane in long
foreground loops (sleep-and-poll receives, foreground CI watchers). A
permanently-working pane starves inbound partner traffic — run
long-running watchers in a background terminal and keep this pane
promptable.
6. Announce every deliberate broken-checkout window. Before mutation testing,
bisect, or a deliberate revert, send a `task` naming the affected paths and
stop condition; send `ready` after the tree is restored and verified. Do not
wait for `accepted`: the notices are the protection. A partner that has seen
an open window treats that checkout's test results as unusable until the
close notice arrives, and asks rather than reports. Run an experiment that
cannot be announced, or that lasts more than a few minutes, in a separate
worktree.
## Protocol
Messages start with:
```text
[agent <from> -> <to> kind=<kind> sid=<sid>]
[herdr-pair control seq=<n>: ...]
<body>
```
The helper injects the control line. It contains the exact executable
`receive` command for this message and the rule to reply through the sender.
This line is the recovery anchor when model context has been compacted.
Use these kinds:
- `task`: propose or update the work split and its write leases. Begin a
mid-flight stop with `STOP — <reason>`.
- `review`: request review with file paths and a short change summary.
- `question`: ask for clarification before proceeding.
- `ready`: report changed files, validation, and residual risk.
- `accepted`: accept the partner's `ready` and advance the work cycle.
- `blocked`: name the user decision required to continue.
- `stalemate`: report the same disagreement repeated twice without movement.
- `handoff`: return control to the user in normal local output.
## Start or resume
1. Run
`node "$PAIR_SCRIPT" discover "${PAIR_ID[@]}"`.
If no opposite agent exists, run
`node "$PAIR_SCRIPT" spawn "${PAIR_ID[@]}"` once.
Stop on multiple candidates or spawn failure.
2. Run `node "$PAIR_SCRIPT" init "${PAIR_ID[@]}"`.
It creates a new tab-scoped session or
idempotently resumes the exact live session.
Record its exact `sid` as `PAIR_SID`; every send is
bound to it so a wrong same-kind pane cannot borrow another tab's session.
3. Send the first `task` through the helper. The partners are equals:
propose a scope split — one write lease per scope, each partner
implementing its own scopes and reviewing the other's `ready` — and
include enough context for independent work; the partner accepts or
counters before implementing. A task with no independent scopes takes one
lease: the holder drives while the partner reviews, and the lease
alternates across tasks.
Done when the exact tab has one verified session and the first task has a
recorded receipt. Skill reload and compaction always resume that session.
## Send
Write only the body to a temp file, then invoke:
```bash
BODY=$(mktemp); trap 'trash "$BODY"' EXIT
# Write the partner message body to "$BODY".
node "$PAIR_SCRIPT" send "${PAIR_ID[@]}" --sid "$PAIR_SID" \
--kind "$KIND" --body-file "$BODY"
```
The sender gives a busy partner a short grace period, then delivers anyway.
Measured on Herdr 0.8.0, Claude accepts a mid-turn prompt atomically, while a
multi-line prompt to Codex still needs Enter. When the partner is still working,
the helper sends exactly one `agent prompt` with the header, control line, and
body, then runs the harmless Enter loop. It skips the visible-arrival check and
the full resend because that status has no reliable composer signal and a
resend may duplicate a queued body. Only `receive` proves the exact sequence in
`session.json`; without that ACK the receipt says the delivery is unproven.
For an idle partner, the helper proves landing from the composer: the composer
must first be seen to change, because a paste that never arrived and one already
submitted look identical; then it sends Enter until the composer releases the
text, performs one full resend, and fails loudly if delivery still cannot be
proved. The sequence ACK can recover an interruption immediately after
submission on either path.
- `receipt=acknowledged`: the partner ran `receive` for this message.
- `receipt=pending-partner-may-be-busy-do-not-retry`: the message landed and the
partner is working, so it is queued but not acknowledged yet. Do not resend.
Later run `node "$PAIR_SCRIPT" reconcile`; the status advances only after its
ACK.
- `receipt=unproven-working-inspect-that-pane-then-reconcile`: the partner was
working, received one prompt plus the Enter protection, but did not ACK before
the deadline. The helper cannot distinguish a queued prompt from a silent
drop. Do not resend while its reservation is pending; inspect after the agent
settles, then reconcile or clear the proved-absent delivery before retrying.
- `receipt=lost-partner-idle-inspect-that-pane-then-reconcile`: the partner is
idle and never acknowledged, so it did not receive the message. Read that
pane, confirm the message is absent, and clear the pending delivery below.
Long bodies are the usual cause; send those as a short message naming a file
path.
- A nonzero exit after reservation is a transport failure. The pending
reservation remains so an ACK can reconcile it; never claim delivery or clear
it without inspection.
If inspection proves a pending message never reached the partner, clear only
that delivery with explicit user approval:
```bash
node "$PAIR_SCRIPT" reconcile "${PAIR_ID[@]}" --sid "<sid>" \
--clear-pending true
```
## Receive and recover after compaction
For inbound `[agent ...]` traffic:
1. Run the exact command in `[herdr-pair control ...]`. It calls `receive` with
`--sid`, `--from`, and `--seq`, validates the live tab binding, and persists
the receipt acknowledgement.
2. Process the message, write the reply body to a temp file, and run
`node "$PAIR_SCRIPT" send ...`.
Done when `receive` records the sequence and the reply has a recorded receipt.
On failure, give the user a header-free transport report. After compaction,
reconstruct pane IDs, `sid`, task status, and close state from the control line
and verified session.
## Work cycles and persistence
Continue while producing useful artifacts. Five consecutive turns with no new
code, test result, decision, or narrowed option require a `handoff`. Reset the
count on real progress. Settle a factual disagreement with one direct proof or
focused test before it can become a stalemate. Send `stalemate` after the same
judgment call repeats twice without movement.
Two `accepted` statuses complete one work cycle. The initiator gives the user a
local handoff naming the result, verification evidence, unresolved issues, and
every pair pane, worktree, or watcher still active. Both agents may idle; the
next task resumes the same pair and `sid`. The session remains active.
`blocked` and `stalemate` also hand off without deleting the session. Use
`node "$PAIR_SCRIPT" reset "${PAIR_ID[@]}"` only to clear work-cycle
counters/statuses in a verified live pair; it preserves identity and delivery
history.
End and trash the session only when the user explicitly asks to end the pair:
```bash
node "$PAIR_SCRIPT" end "${PAIR_ID[@]}" --sid "<sid>"
```
The script verifies sid, workspace, tab, and participants itself, and
refuses while delivery is pending (wait for the ACK or use the inspected
clear path). If old pane IDs or a missing partner prevent resume, explain
the mismatch and use `end "${PAIR_ID[@]}" --sid "<sid>" --stale true` only
with explicit user approval — a stale end may discard pending state only
when the partner pane is gone, its recorded binding is stale, or its
foreground agent process/repository no longer matches. Closing the Herdr tab
ends the panes naturally; stale state is never borrowed by another tab.
## Workbench tab
Read `references/workbench-tab.md` only when a separate tab is needed for a
long-running shared process.
## Deprecated
This skill is frozen. Its successor is the `pair` skill in the same repository,
which keeps this Herdr backend byte-for-byte and adds a headless backend for
pairing outside Herdr. Install `pair` for new work; `herdr-pair` stays here so
existing installs keep running.