The spec library's secondary source-link loop in refreshWorkflowEntries skips synthetic workflow-source alias entries. The built-in owns that cache row and the loop always discarded the read, so an unmodified user copy of a built-in workflow is now read twice per list instead of three times. Ordinary user-file source links and fallback byte verification are unchanged, and a new test covers the alias count, alias deletion, an edited copy's ownership and the fallback.
An adopted, bound or self-attached session's guidance now routes through rig unclaim, which releases it and leaves its process running, then ending that process in its own terminal. It says rig down leaves a session OpenRig didn't start running, since rig down stops only sessions carrying the seat's OpenRig marker. The seat count no longer changes the text. getting-started.md, tmux-servers.md and hosted-members.md match, and one stale test pin is updated. The external-CLI text is unchanged.
Brings 0.7.12's release teaching onto main: the CHANGELOG's [0.7.12] section and the published [0.7.11] section main didn't carry yet, and the capability map moved to the 0.7.12 delta. The opening now says answers go to the configured receiving agent or kernel operator when available, and otherwise wait. The runner-answer limit says what a native run showed. docs/reference/whats-new.md stays at main's version until the package version moves. Docs only.
Makes the CLI preflight tests deterministic. The six failure cases use port 0 instead of the ambient default daemon port. The same-endpoint case asserts no probe socket, and the different-host case uses an inert socket double that records its endpoint and teardown. The occupied-port case keeps a real, test-owned ephemeral listener. Test only; preflight's runtime is unchanged.
2026-11-11 12:08:57 -08:00
12 changed files with 866 additions and 56 deletions
@@ -8,6 +8,575 @@ deprecations, and behavioral changes. Breaking changes are called out explicitly
---
## [0.7.12]
OpenRig 0.7.12 is a patch for people running their own agent teams, cut from
main `16e8b7fb`. When you answer your team, by typing, clicking or qualifying
an answer, the answer now goes to an agent that reads it with the question and
decides. Answers go to the configured receiving agent or kernel operator when
available; otherwise they wait. Setup offers to register you as the team's
person. `rig down` no longer stops a terminal it
can't show belongs to the seat. There are fixes across delivery, Codex,
restore, the queue, Slack and speed, many contributed by people using
OpenRig. Two repairs aren't in this release: see Known limits.
### Before you upgrade
- **`rig down` is more careful.** It stops only a tmux session that carries the
seat's own `OPENRIG_NODE_ID`, on the server and session it read. A terminal
OpenRig didn't create, a mismarked one or a replaced one is left running, and
`rig down` exits 2 naming it and how to stop it yourself. A seat joined with
`rig attach --self` is in that group. ([#2962](https://github.com/mvschwarz/openrig/pull/2962))
- **openrig-core is 0.1.28.** A Codex session kept running across the upgrade
can ask for hook approval until it's relaunched.
### Answering your team
- Answers nothing else reads, such as a host pairing request or a mode's
scheduled end, use the configured receiving agent or the kernel operator.
Answers wait when no receiver is available. ([#2923](https://github.com/mvschwarz/openrig/pull/2923))
- Pairing answers go to the receiving agent, and you can decide directly in the
TUI's pairing settings. A pairing request says who it went to and who reads
the answer. ([#2931](https://github.com/mvschwarz/openrig/pull/2931), [#2935](https://github.com/mvschwarz/openrig/pull/2935))
- A runner's permission answer goes to the receiving agent, which decides while
the runner waits, so a qualified answer such as "only if it's the test file"
is read before anything acts. ([#2934](https://github.com/mvschwarz/openrig/pull/2934), [#2938](https://github.com/mvschwarz/openrig/pull/2938), [#2950](https://github.com/mvschwarz/openrig/pull/2950), [#2966](https://github.com/mvschwarz/openrig/pull/2966))
- Setup offers to register you as the team's person, and the empty-registry
hint is right. A team you start from your own assistant fits your own logins,
with a review before anything is committed. ([#2924](https://github.com/mvschwarz/openrig/pull/2924), [#2905](https://github.com/mvschwarz/openrig/pull/2905))
### Harnesses and connections
- A community harness can declare `protocol: pi-rpc`, presets can carry launch
wrappers, and a seat can switch between Pi and another runtime. The
community-maintained nodecode runner is listed in the reference.
- A relay connector that can't accept incoming connections can pull pending
messages for a person instead of being pushed to: set
`OPENRIG_RELAY_MODE=pull` in the relay secrets file. Push stays the default.
A person's gateway binding can be unset. ([#2844](https://github.com/mvschwarz/openrig/pull/2844), [#2812](https://github.com/mvschwarz/openrig/pull/2812))
- `rig terminal open <view> --provider herdr-app` opens a Rig view inside the
- Queue content can be erased for closed-work retention and account deletion,
and a retried create can't bring erased work back. ([#2837](https://github.com/mvschwarz/openrig/pull/2837), [#2843](https://github.com/mvschwarz/openrig/pull/2843))
- Each send records what's known about its delivery in one
`transport.delivery_evidence` event: what was admitted, what reached the
input, what the screen check showed, and for which occupant. ([#2937](https://github.com/mvschwarz/openrig/pull/2937), [#2943](https://github.com/mvschwarz/openrig/pull/2943))
- `rig queue create` advises on an unresolved scope, slice lists show observed
queue counts, a proof policy can require every listed judge, the execution
ladder shows each slice's recorded status, `rig queue show` names a handed-off
row's successor, and a custom blocker ID explains the wait.
- Every seat's managed guidance points to its skills, help and project context.
`rig seat status` shows when guidance was written relative to the session's
recorded moments, and what
the seat's refocus hook recorded. A rebuilt seat's briefing lists older recaps
as optional history. ([#2918](https://github.com/mvschwarz/openrig/pull/2918), [#2920](https://github.com/mvschwarz/openrig/pull/2920), [#2916](https://github.com/mvschwarz/openrig/pull/2916), [#2876](https://github.com/mvschwarz/openrig/pull/2876))
### Fixes
- **Delivery and drafts:** Claude seats with a custom status line read idle and
verify sends; a fresh idle hook doesn't type into a current Claude dialog;
Claude's context hint and drawn cursor are read correctly; a deliberate
override is delivered when the input can't be read; update notices beside the
paste hint are recognized. ([#2807](https://github.com/mvschwarz/openrig/pull/2807), [#2813](https://github.com/mvschwarz/openrig/pull/2813), [#2947](https://github.com/mvschwarz/openrig/pull/2947), [#2942](https://github.com/mvschwarz/openrig/pull/2942), [#2933](https://github.com/mvschwarz/openrig/pull/2933), [#2800](https://github.com/mvschwarz/openrig/pull/2800), [#2786](https://github.com/mvschwarz/openrig/pull/2786))
- **Codex:** when you answer a Codex choice prompt, a message waiting on it is
delivered instead of escalated; a working counter is compared only within one pane process; hook
trust records are written under smol-toml 1.9, so launches no longer stop at
"Hooks need review". ([#2952](https://github.com/mvschwarz/openrig/pull/2952), [#2980](https://github.com/mvschwarz/openrig/pull/2980), [#2804](https://github.com/mvschwarz/openrig/pull/2804))
- **Restore, start and handover:** owed restore input is delivered after a
bounded wait on unknown readiness; compaction uses the draft defaults and
reports repeated unsent preparation; `rig daemon start` archives a start
reservation left from an earlier boot; the kernel restart guidance describes
the existing recovery; an earlier occupant can't replace a Claude seat's
resume target; token switches keep the Claude launch; rebuild continuity and
- **`rig down`:** a terminal it leaves running stays running until you stop it.
It doesn't keep a stored host or boot identity, doesn't guard against another
program running as you, and a daemon restart between losing the tmux server
and the stop wasn't exercised. For an adopted session, such as a seat joined
with `rig attach --self`, `rig seat stop` still suggests `rig down`, which
leaves that terminal running too. Release it with `rig unclaim <session>`
(that leaves its process running), then close it in its own terminal. ([#2962](https://github.com/mvschwarz/openrig/pull/2962))
- **Codex working counter:** a capture that doesn't name its pane keeps the old
comparison, a restart that keeps both the pane and its process id isn't seen,
and a leftover live frame can count once until it expires. ([#2980](https://github.com/mvschwarz/openrig/pull/2980))
- **Custom blocker IDs** explain the wait, but their progress gives no event
wake; the timer remains. ([#2983](https://github.com/mvschwarz/openrig/pull/2983))
- **Delivery evidence** records what was known, not that the agent read the
message; where the input ended up can read unknown. ([#2937](https://github.com/mvschwarz/openrig/pull/2937), [#2943](https://github.com/mvschwarz/openrig/pull/2943))
- **Runner permission answers:** when a person answers a runner's permission
request, the answer is kept, and an explicit decision works. In 0.7.12 the
runner's confirmation updates fail to record, so no receipt or Feed
confirmation appears, and the decision can reach the runner on its next read
rather than at once (up to about 21 seconds in our test). To settle a request
directly, run `rig seat decide-permission <request> --option <id>`. Pairing
answers are separate, and their receipts are kept. Tested on one native daemon
with the TUI and a runner driven by a stand-in agent. ([#2934](https://github.com/mvschwarz/openrig/pull/2934))
- **herdr-app views** are one-shot; following isn't claimed. **Pull relay**
needs a connector that implements pull. ([#2744](https://github.com/mvschwarz/openrig/pull/2744), [#2844](https://github.com/mvschwarz/openrig/pull/2844))
- **From 0.7.11:** its known limits below still apply, except Codex hook setup
with smol-toml 1.9.x ([#2720](https://github.com/mvschwarz/openrig/issues/2720)), fixed by [#2804](https://github.com/mvschwarz/openrig/pull/2804). 0.7.12 changes openrig-core
again, so the Codex hook note there applies to this upgrade too. They weren't
re-checked for this release unless named here.
### Credits
Thanks to @billbenetti, @oodadoudou, @DeryFerd, @Aummadour, @seanmcn, @w4rc0n,
@ziadeh, @vanducng, @smota, @sn1thy, @dajiaohuang and @Vaishnavi220506 for
merged contributions, and to @KM199, co-author of [#2947](https://github.com/mvschwarz/openrig/pull/2947) and [#2952](https://github.com/mvschwarz/openrig/pull/2952), which finish
their [#2513](https://github.com/mvschwarz/openrig/pull/2513) and [#2515](https://github.com/mvschwarz/openrig/pull/2515).
Thanks also for the reports behind these fixes: @nassersala ([#2955](https://github.com/mvschwarz/openrig/pull/2955)), @djuntgen
([#2804](https://github.com/mvschwarz/openrig/pull/2804)), @todd-dhango ([#2851](https://github.com/mvschwarz/openrig/pull/2851)), @Kayhusk ([#2796](https://github.com/mvschwarz/openrig/pull/2796)), @pr0xc3nt4ur1 ([#2849](https://github.com/mvschwarz/openrig/pull/2849) and [#2850](https://github.com/mvschwarz/openrig/pull/2850)), @amsayfi
([#2776](https://github.com/mvschwarz/openrig/pull/2776)), @oodadoudou ([#2847](https://github.com/mvschwarz/openrig/pull/2847)) and @CaptObvious ([#2933](https://github.com/mvschwarz/openrig/pull/2933) and [#2942](https://github.com/mvschwarz/openrig/pull/2942)).
## [0.7.11]
OpenRig 0.7.11 is a patch for people running their own agent teams, cut from
main `b40bc71c`. Its biggest change: an ordinary send or work notice now goes
into a person's typed draft by default instead of being refused (see Behaviour
that changed). A person can now work with their team from their own coding
agent through the new `openrig` plugin or from an ACP app such as Zed or T3
Code, and, as an experiment that's off by default, answer the team's questions
from an assistant connected through a grant service. A prepared managed compaction waits for an idle moment instead
of asking again, and a restore stopped at a prompt clears itself. On Linux, a
daemon run as a systemd user service no longer takes its seats down when its
unit stops or restarts, and writable terminal tiles reflow text when the screen resizes. There
are fixes across the queue, workflows, launch and speed under load, many
contributed or reported by people using OpenRig. The OpenShell worker's
maintained cleanup now works on OpenShell 0.1.6, but the worker still isn't end
to end: see Known limits.
### Before you upgrade
- **Back up your database first.** Five new migrations run at the daemon's
(workflow instance spec), 153 (events retention marks) and 154 (workflow
instance root objective). 0.7.10 had none. With the daemon stopped, copy the
file `$OPENRIG_DB` names. Going back to 0.7.10 after these migrations isn't
established.
- **Restart the daemon after installing:**
```bash
npm install -g @openrig/cli@0.7.11
rig daemon stop && rig daemon start
```
- **Linux, with the daemon run as a systemd user service:** the seats move into
their own scope only when their tmux server next starts fresh. Until then the
server stays in the daemon's unit, so stopping or restarting that unit still
takes the seats down, as before. ([#2749](https://github.com/mvschwarz/openrig/pull/2749))
- **The openrig-core plugin moves from 0.1.26 to 0.1.27.**
Its activity relay and refocus hooks changed, as did the refocusing,
openrig-user and openrig-skills skills. Replaced files are kept under
`$OPENRIG_HOME/backups/managed-refresh/<id>/`; reapply local edits you still
need from there. ([#2756](https://github.com/mvschwarz/openrig/pull/2756), [#2743](https://github.com/mvschwarz/openrig/pull/2743), [#2717](https://github.com/mvschwarz/openrig/pull/2717), [#2735](https://github.com/mvschwarz/openrig/pull/2735), [#2657](https://github.com/mvschwarz/openrig/pull/2657), [#2647](https://github.com/mvschwarz/openrig/pull/2647), [#2619](https://github.com/mvschwarz/openrig/pull/2619))
- **Sends now join a person's draft** (`transport.draft_delivery` = `deliver`).
To keep the old refusal, run `rig config set transport.draft_delivery refuse`. ([#2645](https://github.com/mvschwarz/openrig/pull/2645))
- **A Pi seat that declares an absolute `PI_CODING_AGENT_DIR`** in its `env:`
now uses it as its account home and needs a readable `auth.json` there, or
the launch stops. Prepare one, or remove the declaration to keep the managed
- **A send now goes into a person's typed draft.** Under the new
`transport.draft_delivery` setting's default, `deliver`, an ordinary
`rig send` or work notice to a seat whose prompt holds a person's typed draft
pastes the message at the cursor and submits the combined input as one
prompt, and the send's result says whether submission was confirmed.
`refuse` (also `OPENRIG_TRANSPORT_DRAFT_DELIVERY` in the daemon's
environment) restores the old refusal where OpenRig detects the draft, from
the next send with no restart. Menus, permission prompts, a seat's typing
guard, managed compaction and `--wait-for-idle` still refuse or wait under
either value. ([#2645](https://github.com/mvschwarz/openrig/pull/2645))
- **A draft only the input box shows is caught:** an ordinary Claude Code or
Codex send reads the box once before pasting (three tmux calls, within
500 ms). Under `refuse` the send is refused before any input; under `deliver`
the result says it joined the draft. ([#2653](https://github.com/mvschwarz/openrig/pull/2653))
- **Claude seats showing a statusLine or the background task panel** read as a
normal composer under the draft-aware typing guard, so an empty box gets the
delivery instead of `draft_input_unknown`. ([#2703](https://github.com/mvschwarz/openrig/pull/2703))
- **The bare `✔ Update installed · Restart to update` row** of Claude Code
2.1.319 in a narrow pane is recognised, so an idle seat's empty prompt reads
empty and wakes go through under the draft-aware guard. Checked against the
row as reported, not a live pane. ([#2662](https://github.com/mvschwarz/openrig/pull/2662))
- **A full held-message quota no longer drops a sender's later wakes:** the
sender's newest held message retires its earlier ones, listed as `coalesced`
in `rig seat held-messages <seat>` and readable with `--id`. ([#2701](https://github.com/mvschwarz/openrig/pull/2701))
- **Seats stopped while they owe work** don't get a burst of undeliverable
wakes at each daemon start; the start-up log says `waiting=N (not replayed: …)`. ([#2620](https://github.com/mvschwarz/openrig/pull/2620))
### Compaction, restore and handover
- **A prepared managed compaction waits for its window.** When the restore map
is complete but `/compact` couldn't go in (a draft, a tmux mode, no idle
moment), the attempt is held as `prepared` and sends `/compact` at the next
idle moment instead of asking the seat to prepare again about every 11
minutes. `rig compact --state` shows the hold and `--cancel` disarms it. A
busy seat isn't asked again after an ended attempt, across a restart too. ([#2698](https://github.com/mvschwarz/openrig/pull/2698), [#2716](https://github.com/mvschwarz/openrig/pull/2716))
- **The final `/compact` waits out the preparation's own fresh activity:** its
guarded send now allows the 15-second hook freshness window plus the 8-second
capture budget (23 seconds, against 9), so a seat that's already idle, such
as one out of context, gets `/compact` instead of a refusal. A busy seat can
wait up to about 14 seconds longer; the attempt's deadline still limits it. ([#2764](https://github.com/mvschwarz/openrig/pull/2764))
- **A restore stopped at a prompt clears itself** (such as Claude's resume
selection) once the seat runs the conversation it was restoring, using the
check `rig seat clear-attention` runs. The docs cover the manual route. ([#2711](https://github.com/mvschwarz/openrig/pull/2711), [#2674](https://github.com/mvschwarz/openrig/pull/2674))
- **A queue wake refused at a prompt no longer goes in right after "Compacted"**
while the compaction still owes its turn boundary and restore request. It
goes in at the seat's next idle moment after that step clears, as a recovered
The plugin's README has the steps. ([#2647](https://github.com/mvschwarz/openrig/pull/2647))
- **VS Code, Copilot CLI and Cursor** install it too, with no seat; the README
and `rig_whoami` say to add your own `rig mcp serve --front-door` server with
`OPENRIG_SESSION_NAME` set and turn the plugin's off. A seatless app can still
create a task, send and post, as `unknown-mcp-<8 hex>`. ([#2683](https://github.com/mvschwarz/openrig/pull/2683), [#2709](https://github.com/mvschwarz/openrig/pull/2709), [#2718](https://github.com/mvschwarz/openrig/pull/2718), [#2657](https://github.com/mvschwarz/openrig/pull/2657))
- **Answers are recorded exactly as the person typed them.** ([#2733](https://github.com/mvschwarz/openrig/pull/2733))
- **Marketplace files at the repository root** let Claude Code's
`/plugin marketplace add mvschwarz/openrig`, and directories that take a public
Git repo, install it from GitHub (marketplace `openrig-app`). ([#2719](https://github.com/mvschwarz/openrig/pull/2719))
### ACP apps: `rig acp serve`
- **OpenRig as an agent in ACP apps:**`rig acp serve --to <member@rig>` runs
on stdio for an app that launches a custom agent (Zed's `agent_servers`, T3
Code). A prompt becomes a queue row, each row the work reaches shows as a
step, and the closing resolution is the reply. Nothing runs until an app
starts it, and it talks to the daemon `rig` uses. ([#2648](https://github.com/mvschwarz/openrig/pull/2648), [#2714](https://github.com/mvschwarz/openrig/pull/2714))
- **The team's questions reach the app** while a turn is open, as a permission
request or a one-choice form, and the choice is the person's answer. Sessions
can be listed and reopened, and a question left unanswered there is named as
still open in the TUI and Slack. ([#2651](https://github.com/mvschwarz/openrig/pull/2651), [#2708](https://github.com/mvschwarz/openrig/pull/2708), [#2686](https://github.com/mvschwarz/openrig/pull/2686), [#2712](https://github.com/mvschwarz/openrig/pull/2712))
- **The `@openrig/acp` launcher** (`openrig-acp`) runs the matching
`@openrig/cli`'s `rig acp serve` for the ACP registry's `npx` launch, with a
new `acp.to` setting for the receiver. `npm run pack:release` packs it beside
the CLI at the same version. ([#2655](https://github.com/mvschwarz/openrig/pull/2655), [#2715](https://github.com/mvschwarz/openrig/pull/2715))
### People and connected assistants
- Denying or dropping a host pairing request in Steering now gives the requesting machine no access; in 0.7.9 and 0.7.10, that action could return the daemon access token. ([#2884](https://github.com/mvschwarz/openrig/pull/2884))
- **Pairing answers now reach the target's kernel operator.** On a 0.7.11 target with a receiving kernel operator, typed words and Approve or Deny choices in the TUI, Steering or Slack are replies for that agent to read. Receiving a reply neither grants nor denies access. The operator acts on clear intent with `rig host approve <code>` or `rig host deny <code>` and asks back when the answer is unclear. The commands also work from a plain terminal on the target; they use its operator bearer (`OPENRIG_AUTH_BEARER_TOKEN`, or `--bearer <token>`). ([#2900](https://github.com/mvschwarz/openrig/pull/2900))
- **An Approve click on an operator-owned request waits for the operator to act.** A stopped operator keeps ownership; the answer waits in its queue. A stopped or unknown running state is named with the terminal route, without claiming the answer was read. If there is no operator, it cannot receive answers, or OpenRig cannot determine one, the request names that case and uses the agent-free path: Approve and Deny decide directly, and replies lead to another ask rather than denial. File-only replies and the literal `[file reply]` have no words stored on the agent-free ask; they still do not deny it.
- **TUI answers use the daemon's operator bearer when configured.** Ordinary terminal calls keep their separate terminal credential. A missing or wrong action credential leaves the answer unrecorded and shows what to set; this does not weaken the daemon's authentication.
- **Receipt, clarification and outcome are separate steps.** Receipts, agent-free re-asks and outcomes inferred from clicks or closes need the requesting machine to keep polling. An explicit `rig host approve` or `rig host deny` attempts its outcome update directly. An expiry timer closes the ask even if polling stopped; an unanswered expired ask gets no further update. Requests and decisions live in daemon memory, so a restart drops an open request. Approval still must be collected before expiry. This target-side change does not change older targets.
- **Assistants connected through a grant service** (experimental, unsupported,
off by default: you serve `rig mcp serve --http --grant-service <url>` behind
your own TLS tunnel, and an outside service grants each connection):
`rig_question_list` and `rig_question_answer` let a registered person answer
the team's questions as their own; `--task-destination <member@rig>` names
the receiver for `rig_queue_create`, and a bare seat name resolves. ([#2735](https://github.com/mvschwarz/openrig/pull/2735), [#2638](https://github.com/mvschwarz/openrig/pull/2638), [#2750](https://github.com/mvschwarz/openrig/pull/2750))
- **The `relay` gateway connector** sends questions, updates and notices to an
operator-owned bridge over loopback HTTP and takes replies back. It's off
unless you add a relay binding, and the bridge, with its reach to a messaging
platform, is yours (`docs/reference/gateway-relay.md`). ([#2624](https://github.com/mvschwarz/openrig/pull/2624))
- **The web UI's For You feed** answers a local ask's questions on its card. ([#2612](https://github.com/mvschwarz/openrig/pull/2612))
### Queue, workflows and status
- **One `unclaimed-backlog` notice** replaces a finding per row for a seat that
reads without claiming and has no escalation route or delegation parent. ([#2752](https://github.com/mvschwarz/openrig/pull/2752))
- **Claims read `working`, not stalled,** for up to 60 minutes while a Claude
owner has a live background shell, and 20 while a managed restore is owed. ([#2675](https://github.com/mvschwarz/openrig/pull/2675))
- **`rig queue show`, `list`, `transitions`, `update` and `block` take
`--host <id>`,** as `create` and `handoff` did; `claim` doesn't. ([#2678](https://github.com/mvschwarz/openrig/pull/2678))
- **Cross-host handoffs** that override a claim wake the claimer, and a close
to an unverifiable successor names `rig queue handoff`. ([#2734](https://github.com/mvschwarz/openrig/pull/2734), [#2621](https://github.com/mvschwarz/openrig/pull/2621))
- **Code results:** an `openrig.code-result/v1` manifest as `evidenceRef` gives
`rig_artifact_read` and the new `rig queue artifact` the commit pair to diff. ([#2659](https://github.com/mvschwarz/openrig/pull/2659))
- **Workflows:** a run keeps its own spec when another pod's copy reuses the id
and version; the root objective is kept and printed as `objective:`;
`rig workflow unregister` removes versions; `trace --full` returns the whole
- **Activity:** a helper harness inside a seat no longer reports its turns as
the seat's, and a resumed seat no longer reads a false model divergence. ([#2743](https://github.com/mvschwarz/openrig/pull/2743), [#2664](https://github.com/mvschwarz/openrig/pull/2664))
- **Also:** refocus traces projects outside `workspace.root`; a timed-out
`rig stream emit` names a retry that can't duplicate; `rig context
reconstruction inspect` and `view` read an exact revision from local stores.
- **Herdr follow views** work on macOS too (protocol 22), open a labelled
snapshot where live following isn't supported, and show a cleaner strip. ([#2663](https://github.com/mvschwarz/openrig/pull/2663), [#2706](https://github.com/mvschwarz/openrig/pull/2706))
- **Closing the herdr-core terminal that started OpenRig** no longer ends the
daemon or its seats (per herdr-core's documented behaviour; a real close
- **The web UI** loads the newest 100 events instead of the whole log. ([#2704](https://github.com/mvschwarz/openrig/pull/2704))
- **`daemon.seat_poll_interval_ms`** (1000 to 2000) trades activity freshness
for daemon CPU on large fleets. ([#2688](https://github.com/mvschwarz/openrig/pull/2688))
- **Also:** with `OPENRIG_TMUX_CONTROL=1`, short sends and `set-option` use the
control client; the idle-shell check reads the plain process table; skill
catalog Git reads time out after five seconds. ([#2636](https://github.com/mvschwarz/openrig/pull/2636), [#2690](https://github.com/mvschwarz/openrig/pull/2690), [#2713](https://github.com/mvschwarz/openrig/pull/2713))
### Docs and tests
Daemon HTTP clients and bearer tokens ([#2652](https://github.com/mvschwarz/openrig/pull/2652)), the 0.7.10 capability map on
main ([#2736](https://github.com/mvschwarz/openrig/pull/2736)), a neutral TUI demo snapshot ([#2724](https://github.com/mvschwarz/openrig/pull/2724)), a web UI editor timer
([#2689](https://github.com/mvschwarz/openrig/pull/2689)), and test and CI fixes ([#2753](https://github.com/mvschwarz/openrig/pull/2753), [#2681](https://github.com/mvschwarz/openrig/pull/2681), [#2685](https://github.com/mvschwarz/openrig/pull/2685), [#2673](https://github.com/mvschwarz/openrig/pull/2673), [#2628](https://github.com/mvschwarz/openrig/pull/2628)).
### Behaviour that changed
- **Sends and work notices that were refused with `prompt_draft` now go
through by default, joined to the person's draft** (above).
`rig config set transport.draft_delivery refuse` restores the old refusal
where OpenRig detects the draft, from the next send with no restart; under
`refuse`, a draft only the input box shows now refuses too. ([#2645](https://github.com/mvschwarz/openrig/pull/2645), [#2653](https://github.com/mvschwarz/openrig/pull/2653))
- **A Pi seat with an absolute `PI_CODING_AGENT_DIR`** uses it as its account
home on launch, restart, resume and restore; no `auth.json`, no launch. ([#2630](https://github.com/mvschwarz/openrig/pull/2630))
- **`/api/events` no longer emits `view.changed`,** live or replayed, so new
ones leave the web UI's log, activity feed and For You and the historical
telemetry export; the view streams still carry them. ([#2695](https://github.com/mvschwarz/openrig/pull/2695))
- **The stuck sweep** files one backlog notice where it filed one per row,
skips `stalled-after-claim` inside the two windows above, re-checks each
rule just before filing, and skips overlapping ticks. ([#2752](https://github.com/mvschwarz/openrig/pull/2752), [#2675](https://github.com/mvschwarz/openrig/pull/2675), [#2649](https://github.com/mvschwarz/openrig/pull/2649))
- **Managed compaction:** a prepared attempt stays `waiting`, and a manual
`rig compact` over it reads `already_in_progress`. ([#2698](https://github.com/mvschwarz/openrig/pull/2698), [#2716](https://github.com/mvschwarz/openrig/pull/2716))
- **The daemon and seats no longer inherit `HC_SCOPE`, `HC_TERMINAL_ID` or
`HC_TERMINAL_SOCKET`;** one already running keeps them until restarted. ([#2737](https://github.com/mvschwarz/openrig/pull/2737))
- **On Linux, the seats' tmux server runs in its own systemd user scope** when
the daemon is a user service (above): the daemon unit's stop, restart and
memory accounting no longer cover the seats. ([#2749](https://github.com/mvschwarz/openrig/pull/2749))
- **Read-only terminal tiles** show the seat without attaching and end with
tmux's own error if they cannot read it. Writable tiles use the ordinary
sizing attach; detaching ends that viewer, while the agent keeps running.
- **Also:** each sender keeps one live held message per seat; daemon start
doesn't re-send a failed wake to a seat with no receiver; restores record
`restore.outcome_reconciled` themselves; `rig terminal open --follow` returns
`follow_unsupported` with a snapshot. ([#2701](https://github.com/mvschwarz/openrig/pull/2701), [#2620](https://github.com/mvschwarz/openrig/pull/2620), [#2711](https://github.com/mvschwarz/openrig/pull/2711), [#2663](https://github.com/mvschwarz/openrig/pull/2663))
- **MCP and ACP:** an empty or placeholder seat value means no seat; the
seatless `rig_whoami` text changed; `--front-door` is `front-door/v2`;
grant-service callers' `destination` is optional; `rig acp serve` gives
form-taking apps a form, leads step titles with the latest note, and asks
nothing after a cancel. ([#2657](https://github.com/mvschwarz/openrig/pull/2657), [#2709](https://github.com/mvschwarz/openrig/pull/2709), [#2718](https://github.com/mvschwarz/openrig/pull/2718), [#2733](https://github.com/mvschwarz/openrig/pull/2733), [#2638](https://github.com/mvschwarz/openrig/pull/2638), [#2750](https://github.com/mvschwarz/openrig/pull/2750), [#2708](https://github.com/mvschwarz/openrig/pull/2708), [#2686](https://github.com/mvschwarz/openrig/pull/2686), [#2655](https://github.com/mvschwarz/openrig/pull/2655))
- **CLI:**`rig attach --self` outside tmux records `<name>@<rig>`;
`rig host pair` keeps a request for its `--timeout`; `rig config set` and
`reset` name environment overrides; `rig daemon start` and `stop` refuse when
a blocked probe leaves identity or liveness unknown; `--host` receipts print
a runnable command. ([#2641](https://github.com/mvschwarz/openrig/pull/2641), [#2692](https://github.com/mvschwarz/openrig/pull/2692), [#2547](https://github.com/mvschwarz/openrig/pull/2547), [#2502](https://github.com/mvschwarz/openrig/pull/2502), [#2678](https://github.com/mvschwarz/openrig/pull/2678))
- **Seat launches:** unchanged OpenRig copies of openrig-core's skills are
removed, with warnings; the permission hook reads the installed policy lists
on each call; model divergence reads pending until the new occupant's record.
- **Drafts:** detection isn't complete. Under `refuse`, an unreadable input box
lets the send go ahead, so an undetected draft goes with the message; under
`deliver`, a result may not mention a draft it joined. ([#2645](https://github.com/mvschwarz/openrig/pull/2645), [#2653](https://github.com/mvschwarz/openrig/pull/2653))
- **A post-compaction step can still hold** when the box reads as typed, under
draft-aware delivery or the typing guard, or after a lost `/compact`. Run
`rig compact <seat> --cancel`, then send a restore request naming the newest
restore map, marker and packet. A queue wake refused at a prompt now waits for
that step, but recovery at daemon start and a new handoff's wake don't check
for it yet. ([#2575](https://github.com/mvschwarz/openrig/issues/2575), [#2666](https://github.com/mvschwarz/openrig/pull/2666))
- **Draft-aware delivery to Claude:** a message held as `draft_input_unknown`
still isn't retried. Whether managed compaction now gets past the banner
- **Claude provider switch:** the failure reported in [#2341](https://github.com/mvschwarz/openrig/issues/2341) ("The probe pane
returned to a shell instead of staying inside the runtime") isn't shown fixed.
- **`rig exec`:** checked with Claude Code and Codex through their ACP
adapters. A cancel is acknowledged, not settled, and says nothing about
further spend. Cancel with `rig exec cancel`, not `rig acp cancel`.
- **ACP apps:** checked in T3 Code (Beta) 0.1.7 and with a scripted client; the
unanswered-question notice hasn't run in a live app, nor the note-first step
title in Zed. A cancel stops waiting; the work carries on. ([#2648](https://github.com/mvschwarz/openrig/pull/2648), [#2686](https://github.com/mvschwarz/openrig/pull/2686), [#2712](https://github.com/mvschwarz/openrig/pull/2712))
- **The `openrig` plugin** hasn't run in Claude Desktop, the Codex app or
Copilot CLI. A seatless app can't claim, update, hand off or answer
questions, and everyone using the app on a machine acts as its seat. ([#2647](https://github.com/mvschwarz/openrig/pull/2647), [#2657](https://github.com/mvschwarz/openrig/pull/2657), [#2683](https://github.com/mvschwarz/openrig/pull/2683))
- **Grant-service assistants** still aren't woken: an item stays queued until
its receiver claims it. **Relay:** "Accepted by bridge" means the bridge has
it, not the person, and a failed send replays only at a restart. ([#2735](https://github.com/mvschwarz/openrig/pull/2735), [#2624](https://github.com/mvschwarz/openrig/pull/2624))
- **Event retention** can't be undone, and the database file doesn't shrink. ([#2699](https://github.com/mvschwarz/openrig/pull/2699))
- **`keep_saved` skills:** a skill whose source changes between `rig down` and
the restore comes back missing, named in the restore's warnings; an edited seat
copy is kept. ([#2626](https://github.com/mvschwarz/openrig/pull/2626))
- **Read-only terminal tiles** each run one tmux command a second while
showing. The earlier eight-tile showing measurement used about 6–8% of one
core on an Apple Silicon Mac mini, against about 0.2% for attached 0.7.10
tiles. This polling cost no longer applies to writable tiles. ([#2757](https://github.com/mvschwarz/openrig/pull/2757))
- **Early writable-tile input:** as in 0.7.10, Enter or Ctrl-C typed while a
new tile is still attaching can close that viewer. The agent keeps running;
reopen the view. Once attached, tmux handles input and paste directly.
- **Claude `launch_wrapper`** hasn't run with zsh, on macOS arm64 or with a real
installed Claude; an alias or shell function named `claude` fails closed. ([#2746](https://github.com/mvschwarz/openrig/pull/2746))
- **Going back to 0.7.10:** besides the migrations, an older permission hook
gives named-policy Claude sessions no decision until relaunched. ([#2640](https://github.com/mvschwarz/openrig/pull/2640))
- **Codex hooks:** a session kept running across an upgrade can show "Hook
failed — plugin contents changed; hook approval required" until relaunched,
and 0.7.11 changes openrig-core again. `rig seat restart --keep-conversation`
refuses a Codex seat launched with developer instructions (`settings_drift`).
- **Codex hook setup:** with smol-toml 1.9.x and an existing `hooks.json`,
OpenRig can leave its activity hooks and trust records unchanged. The range
(`^1.8.0`) still permits 1.9.x; the repository lockfile uses 1.8.0. ([#2720](https://github.com/mvschwarz/openrig/issues/2720))
- **Claude Code 2.1.319 starts in auto mode by default,** so a new Claude seat
may act without asking; manual mode restores asking.
- **Also:** permission requests in seat status show only the latest, in memory
for five minutes, and suggested rules can contain sensitive values; Slack
private channels need the documented, untested route, direct messages aren't
supported and people reads haven't run live; a Codex snapshot restore writes
OpenRig's guidance blocks into a tracked `AGENTS.md` (check `git status`); a
scheduled reminder that fails before input isn't retried; SSH host status
reads `unknown` behind ProxyJump or ProxyCommand; advisories don't show on a
native resume; seats handed over before 0.7.9 refuse stop, restart and
clean; after a project-local install, plain `rig` isn't found in the TUI's
own shell; with Claude Code 2.1.319, `rig up` can wrongly say "Startup prompt
still staged … press Enter"; two npm installs into one folder at once once
left a partial install, cause unknown.
- **Fixed or changed from 0.7.10's known issues:** the bare update banner
([#2662](https://github.com/mvschwarz/openrig/pull/2662)) and the Claude launcher-script readiness timeout with `--no-chrome`
([#2751](https://github.com/mvschwarz/openrig/pull/2751)). 0.7.10's OpenShell limit is superseded by the one above.
- **The earlier limits below that 0.7.11 doesn't fix or change still apply.**
### Credits
Thanks to @billbenetti, @seanmcn, @smota, @Aummadour, @dajiaohuang, @w4rc0n,
@Mohsintl, @twotnguyen, @ziadeh and @Nikhi00718 for merged contributions.
Thanks to @billbenetti for the reports behind [#2752](https://github.com/mvschwarz/openrig/pull/2752), [#2660](https://github.com/mvschwarz/openrig/pull/2660), [#2738](https://github.com/mvschwarz/openrig/pull/2738), [#2749](https://github.com/mvschwarz/openrig/pull/2749), [#2666](https://github.com/mvschwarz/openrig/pull/2666) and [#2665](https://github.com/mvschwarz/openrig/pull/2665), to
@pr0xc3nt4ur1 for the report behind [#2740](https://github.com/mvschwarz/openrig/pull/2740) and [#2743](https://github.com/mvschwarz/openrig/pull/2743), to @oneandonlydean for
the process chain behind [#2751](https://github.com/mvschwarz/openrig/pull/2751), and to @niklasbaldi for the report and cause
Thanks also to @ziadeh for the reports behind [#2622](https://github.com/mvschwarz/openrig/issues/2622) and [#2486](https://github.com/mvschwarz/openrig/issues/2486), and to @mlc-factory-bot for the reports behind [#2605](https://github.com/mvschwarz/openrig/issues/2605) and [#2607](https://github.com/mvschwarz/openrig/issues/2607).
## [0.7.10]
Published 2026-11-08 as `@openrig/[email protected]`, built from the commit tagged
**Each send records one `transport.delivery_evidence` event**: what was admitted, what reached
the input, what the screen check showed, and for which occupant. Read it as what was known about
the delivery, not proof the agent read it; where the input landed can read unknown.
- **`rig send --raw`** — send exact text or keystrokes without the From/To envelope;
the interactive-prompt guard still applies. To deliberately drive an interactive prompt,
**`--dangerously-interact --reason "<why>"`** is the explicit override and implies raw text.
@@ -307,6 +316,8 @@ unseen — which makes messages the one delivery channel that never gets skipped
**Open terminals ▸** opens the inspected plan through the same desktop launcher, herdr or plain
tmux, so the TUI must run on the daemon's desktop; when no window was attempted it shows the
preview and attach commands. Help or a side trip returns to the same preview.
`--provider herdr-app` opens the view inside the herdr-app desktop window as an equal grid with a
title per cell; it's a one-shot view (following isn't claimed), so check the cells yourself.
- **`rig walk <seat> --through <files> --pace <n>`** — deliver context pieces through
file-backed terminal paste. When the current native generation record resolves, the complete
piece and its matching Claude/Codex turn closure must arrive before pacing onward, including
@@ -374,6 +385,9 @@ your context, your compaction and your replacement — and it is the only thing
another host, as `create` and `handoff` do; `claim` doesn't, since only the destination seat
claims, on its own host. `rig queue artifact <qitemId>` (and MCP `rig_artifact_read`) returns the
exact commit pair from an `openrig.code-result/v1` manifest set as the row's `evidenceRef`.
It names a handed-off row's successor. `rig queue create` advises when a row's scope doesn't
resolve, and a custom blocker ID explains the wait in the waiting views (its progress gives no
event wake; the timer remains).
- **`rig queue claim` / `unclaim`** — is this mine and running, or still pending where two agents
might double-work it. Put it down honestly when it is not yours.
- **`rig queue update`** — record what happened in a form the rest of the system can act on. A
@@ -655,6 +669,8 @@ not have to tear one down to change it, and you rarely have to start from nothin
so capture the pane first; for an unattended seat use the Gateway preset, within what it declares.
A Hermes gateway runs per Hermes home, so one per seat with the example launcher; its preset
doesn't declare `resume`.
A harness can declare `protocol: pi-rpc`, a preset can carry a launch wrapper, and a seat can
switch between Pi and another runtime.
- **`rig down`** — stop a rig's seats and take it out of the running set. It ends the agents'
sessions and any work in progress; don't run it on a live team unless the person asked. In
human mode it first prints one line with each agent's last known activity, or “unknown”: a
@@ -666,6 +682,11 @@ not have to tear one down to change it, and you rarely have to start from nothin
edited skills-line block, edited team skill files, and settings or a collector that don't match
what OpenRig wrote or that another seat shares by a link. Any other guidance block goes even if
someone edited it.
**Since 0.7.12 it stops only a tmux session that carries the seat's own `OPENRIG_NODE_ID`**, on the
server and session it read. A terminal OpenRig didn't create, a mismarked or replaced one, or a seat
joined with `rig attach --self` is left running, and `rig down` exits 2 naming it and the manual way
to stop it. Teardown is incomplete for those terminals: if one is yours, look at it and stop it
yourself, then run `rig down` again.
- **`rig launch <rig> [seat]`** — one seat is down; start just that one, without disturbing the
rest. It takes the rig's name or ID; a name shared by more than one active rig stops and lists their IDs. Add `--plan` to preview it first, locally or with `--host`; a plan is never sent to a
daemon older than 0.5.9, which would ignore it and launch. A seat whose agent exited to a shell in
@@ -767,6 +788,8 @@ version, from checking the environment to a useful report to the OpenRig team.
database another live daemon already holds (the default, `warn`, only warns), and
`rig daemon start --allow-db-conflict` allows one deliberate start. A start still in progress reads
`Daemon starting: launch reserved by pid N since T; not listening yet` in `rig daemon status`, not stopped.
A start reservation left from an earlier boot is archived by the next start; don't clear it by
hand.
- **A busy daemon isn't a down one.** The CLI refuses only when the daemon is confirmed down
(stopped, stale, a refused probe), its probe is blocked, or it's a sibling home; a busy daemon
gets the real request. A write that then times out says its outcome is **unknown**: check its
@@ -1239,6 +1262,14 @@ reaching them is ordinary work rather than an escalation.
person can settle an ask that was asked of someone else, recorded as themselves beside the person
asked. The first committed answer wins; a later one gets 409 `qitem_already_terminal` and records
nothing. A park addressed to a registered person can be resolved.
- **A person's answer goes to an agent.** Answers nothing else reads (a host pairing request, a
mode's scheduled end) use the configured receiving agent or the kernel operator. Pairing and
runner permission answers go to the receiving agent with the question for it to decide;
qualified words ("only if it's the test file") need that judgment. Answers wait when no receiver
is available: don't describe them as read or decided. A pairing request names its receiver, and
a person can decide directly in the TUI's pairing settings. A relay connector that can't accept
incoming connections can pull instead (`OPENRIG_RELAY_MODE=pull` in the relay secrets file); push
stays the default, and a person's connector binding can be unset.
constrelease=`To release it from OpenRig and leave its process running, run rig unclaim ${session}.`;
if(externalCli)return`This seat was joined with rig attach as an external CLI, so rig down leaves it running. ${release} End the process in its own terminal.`;
if(seats===1)return`To end the session, run ${rigDownRoute(rigName)}. ${release}`;
return`To end only this session, run rig unclaim ${session} (it releases the session from OpenRig and leaves its process running), `
+`then end that process in its own terminal. ${rigDownScope(rigName,seats)}`;
return`To end this session, run rig unclaim ${session} (it releases the session from OpenRig and leaves its process running), `
+`then end that process in its own terminal. ${rigDownLeavesIt(rigName)}`;
}
/** `rig down <rig>` as the route for a one-seat rig, and how the rig comes back. */
functionrigDownRoute(rigName: string):string{
constrig=shellQuote(rigName);
return`rig down ${rig} (this rig has one seat, so it stops only this session; rig up ${rig} --existing brings the rig back)`;
}
/** What `rig down <rig>` ends in a rig with more than one seat, or an unknown count. */
// B5: the refusal names rig down and rig unclaim, not only rig unclaim.
expect((claimedas{guidance?: string}).guidance).toMatch(/run rig down '[^']+' \(.*\)\. To release it from OpenRig and leave its process running, run rig unclaim '/);
// B5: the refusal's route is rig unclaim, then ending the process in its own terminal; rig down leaves a session
// OpenRig didn't start running, so it isn't offered as the way to end this one.
expect(guidance).toMatch(/^To end this session, run rig unclaim '[^']+' \(it releases the session from OpenRig and leaves its process running\), then end that process in its own terminal\. /);
expect(guidance).toMatch(/rig down '[^']+' leaves a session OpenRig didn't start running\.$/);
expect(guidance).not.toMatch(/run rig down/);
});
it("names a seat handed over before 0.7.9 instead of pointing at rig unclaim",async()=>{
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.