{"slug":"openrig-herdr","title":"openrig-herdr","summary":"Use when opening OpenRig fleet terminals as a herdr wall — turning a rig, pod, mission, slice, or saved view into live interactive agent tiles via `rig terminal`, watching another rig read-only, or driving herdr on an agent's request (\"open all my rigs + a mission as views\"). Cov","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-08-31T16:20:47.85116Z","repo":{"url":"https://github.com/mvschwarz/openrig","stars":371,"forks":51,"license":"Apache-2.0","updatedAt":"2026-09-25T04:59:18Z"},"bodyHtml":"<hr>\n<h2>name: openrig-herdr\ndescription: &gt;\nUse when opening OpenRig fleet terminals as a herdr wall — turning a rig, pod, mission, slice, or\nsaved view into live interactive agent tiles via <code>rig terminal</code>, watching another rig read-only,\nor driving herdr on an agent's request (\"open all my rigs + a mission as views\"). Covers the\n<code>rig terminal open|views|status</code> verbs, the honest-partial/degrade reading of the result, the\nread-only-by-construction rail for cross-rig views, scroll/copy out of the box, and the\nsame-size-only duplicate-pane limit. herdr is the default, proof-gated provider.\nmetadata:\nopenrig:\nstage: candidate\nsibling_skills:\n- openrig-cmux\n- openrig-user</h2>\n<h1>openrig-herdr</h1>\n<p>OpenRig decides <strong>which</strong> agents make up a view (a rig, a pod, a mission, a slice, or a saved\ngroup); <strong>herdr</strong> renders the pixels. A view opens as live, interactive terminal tiles — each tile\nis a nested <code>tmux attach</code> to a daemon-owned agent session, so you get the real session, not a\nsnapshot. OpenRig owns the semantics; herdr owns the surface. You drive it entirely through the\n<code>rig terminal</code> CLI, which rides the installed herdr binary at arm's length — never link, embed, or\nplugin it.</p>\n<h2>The whole surface — three verbs</h2>\n<pre><code>rig terminal open &lt;view&gt; [--provider herdr|cmux] [--json]   # herdr is the default provider\nrig terminal views [--json]                                 # list openable views (saved + derived)\nrig terminal status [--provider] [--json]                   # provider liveness / health\n</code></pre>\n<p><code>&lt;view&gt;</code> resolves, in order, to one of:</p>\n<ul>\n<li>a <strong>rig name</strong> — every live agent in that rig, auto-laid-out;</li>\n<li><strong><code>pod:&lt;rig&gt;/&lt;podNamespace&gt;</code></strong> — every live agent in one pod of a rig (the rig's inventory filtered by pod);</li>\n<li><strong><code>mission:&lt;id&gt;</code></strong> — the agents working that mission (derived live from topology);</li>\n<li><strong><code>slice:&lt;id&gt;</code></strong> — the agents working that slice (derived live);</li>\n<li>a <strong>saved-view name</strong> — a user-defined group (see Saved views).</li>\n</ul>\n<h2>Compose a view from a sentence</h2>\n<p>An agent asked \"open all my rigs plus a mission as views\" runs one <code>open</code> per target:</p>\n<pre><code>rig terminal open acme-web                     # a whole rig, live agents as tiles\nrig terminal open mission:site-relaunch        # exactly the agents working the mission\nrig terminal open slice:search-filters      # the agents working one slice\n</code></pre>\n<p>No hand-listing of seats: the mission/slice membership is derived from live topology at open time.</p>\n<h2>Read the result honestly — partial and degrade</h2>\n<p>The result is a partition — every seat lands in exactly one bucket, each <strong>named</strong>:</p>\n<ul>\n<li><strong>opened</strong> — the live agents now showing as tiles.</li>\n<li><strong>absent</strong> — seats in the view that aren't currently live: <strong>named and skipped, never silently\ndropped.</strong> A view that opens <em>some</em> of its seats is a success (the partial is disclosed) and exits\n<strong>0</strong>. Only a view where <strong>no</strong> pane opens exits <strong>non-zero</strong>.</li>\n<li><strong>degraded</strong> — an agent that structurally cannot tile, named <strong>with the reason</strong>. The v1 case:\nan agent on a host registered over HTTP has no ssh path, so its tile can't be composed — it reads\nas <strong>\"host <code>&lt;id&gt;</code> is http-registered; tiles need ssh\"</strong> and is skipped, never dropped. (ssh-reachable\nhosts tile via an ssh-wrapped attach; full http-host tiling is deferred to the cross-host transport\nseam.)</li>\n</ul>\n<p>Read <code>--json</code> to branch on the partition programmatically; the exit code alone tells you opened-something\n(0) vs opened-nothing (non-zero).</p>\n<h2>Read-only — who's interactive, who's watch-only</h2>\n<p>Read-only is composed into the pane at open time — a read-only tile is a <code>tmux attach -r</code>, the client\nreports <code>readonly=1</code>, and it <strong>physically cannot send input</strong>. The current policy, by view kind:</p>\n<ul>\n<li><strong>A rig view or a <code>pod:</code> view is interactive</strong> — you asked for that rig (or pod) directly, so you\ncan drive its agents.</li>\n<li><strong><code>mission:</code> and <code>slice:</code> views are read-only by construction</strong> — these derived views cut across\nrigs; you watch and scroll, you don't keystroke into them.</li>\n<li><strong>A saved view is per-member</strong> — each member carries its own <code>readOnly</code> (omit = interactive;\n<code>true</code> = <code>attach -r</code>).</li>\n</ul>\n<p>So you don't have to remember to be careful: the view kind (and, for a saved view, the per-member\nflag) sets it at open time.</p>\n<h2>Safety rails (hard — the fleet-safety rail)</h2>\n<ul>\n<li>A tile is a <strong>view</strong> (a nested <code>tmux attach</code>) of a daemon-owned session. <strong>Never</strong> move, join, kill,\nor re-parent a daemon-owned pane. Closing a tile detaches one tmux client — the daemon's session is\nuntouched and its addressing (send/capture/nudge) is unaffected.</li>\n<li>Host-local read-only viewing mutates nothing. Anything that would change a <strong>live</strong> daemon session's\nshared state (e.g. flipping a tmux option on a running seat) is a config/design change — prove it in\nan isolated environment, never live-flip in production.</li>\n</ul>\n<h2>Scroll + copy work out of the box</h2>\n<p>On a freshly-launched agent tile the mouse wheel scrolls the pane's history and a drag-select copies\nto your <strong>system</strong> clipboard — with no tmux commands typed. This rides the daemon's terminal defaults\n(a per-session scroll option set at launch, plus the daemon tmux server's clipboard defaults). Honest\ntiming: agents that were already running before this shipped pick up wheel-scroll at their <strong>next\nnatural relaunch</strong> (the scroll default is per-session and running seats are never retro-flipped);\nsystem-clipboard copy works immediately (it's server-wide).</p>\n<h2>Limits — state them, don't paper over them</h2>\n<ul>\n<li><strong>Tile chrome v1 = a plain label</strong> (agent + slice). Rich per-tile status is roadmap.</li>\n<li><strong>Duplicate / multi-view membership</strong> (the same agent live in two views at once) works — put the\nduplicates in <strong>same-size panes</strong>. Different-sized duplicate panes have an inherent tmux\nmulti-client resize mismatch (tmux clamps to the smallest client); it is <strong>documented, not fixed</strong> —\ndon't expect a setting to remove it.</li>\n<li><strong>Inner tmux status bar</strong> is hidden by default (herdr provides the chrome); one config key\n(<code>terminal.status_bar</code>) flips it back on for raw-tmux / no-provider surfaces, and the flip applies\nto <strong>future launches only</strong>.</li>\n</ul>\n<h2>Saved views — the library (<code>terminal-views.yaml</code>)</h2>\n<p>In v1 you <strong>create</strong> a saved view by <strong>hand-authoring</strong> <code>terminal-views.yaml</code> — there is no save/write\nverb (<code>open</code> / <code>views</code> / <code>status</code> are the whole surface; a <code>rig terminal save &lt;id&gt;</code> verb is a named\nstretch/follow-up). You <strong>reopen</strong> it like any other view: <code>rig terminal open &lt;id&gt;</code>.</p>\n<p>The file lives at the OpenRig home root (resolved via <code>getDefaultOpenRigPath()</code>), is written\natomically (tmp + rename), and is byte-stable. <strong>Only hand-authored saved views live here — derived\nviews (a rig, <code>pod:&lt;rig&gt;/&lt;pod&gt;</code>, <code>mission:&lt;id&gt;</code>, <code>slice:&lt;id&gt;</code>) are computed live and never written to\nthis file.</strong></p>\n<p>Schema — use these field names <strong>exactly</strong>, in this order; <strong>omit</strong> optional fields when absent\n(never write <code>null</code>):</p>\n<pre><code>version: 1\nviews:\n  - id: my-view-id             # required — the id `rig terminal open &lt;id&gt;` takes\n    name: Human Name           # required\n    members:\n      - seat: pod-member@rig    # required — the canonical session name\n        label: agent . slice    # optional — pane label\n        host: some-host-id      # optional — STRUCTURED host id (NEVER a `member@rig@host` string);\n                                #            omit for local. ssh-registered hosts tile;\n                                #            http-registered hosts honest-degrade (named + skipped, with reason)\n        tmuxSession: sessname   # optional — defaults to `seat`\n        readOnly: true          # optional — omit = interactive; true = `tmux attach -r`\n</code></pre>\n<p>An agent composing a saved view on request <strong>writes this YAML</strong> and then opens the id:\n<code>rig terminal open my-view-id</code> (add <code>--provider cmux</code> for cmux). The library is provider-agnostic —\nthe same saved view opens in herdr or cmux.</p>\n<h2>AGPL / clean-room</h2>\n<p>Driving herdr through the <code>rig terminal</code> CLI (which talks to the installed herdr binary over its\nCLI/socket) is arm's-length and fine. Do <strong>not</strong> link herdr's source, embed it in-process, or ship a\nherdr plugin — a plugin needs legal review. This skill and its docs are clean-room: patterns only,\nnever herdr source text.</p>\n","files":[{"path":"SKILL.md","sizeBytes":8309,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-08-31T16:21:06.750272Z","sha256":"30A7C47CBDE8EC350E665BDA4029133C0CBFF5205DB93C387A8A7F09A9CADF97","sizeBytes":3724},"review":null,"source":{"repositoryUrl":"https://github.com/mvschwarz/openrig","path":"skills/_canonical/core/openrig-herdr","license":"Apache-2.0","commit":"b374dde300fd2a3cf1ee139b89b11e2fa3945784","subtreeSha":"648AD8880A4A9CCB5FA1F89D33262E7809798E6A78E899426BE6977F9394B978","lastSyncedAt":"2026-09-25T06:48:47.236757Z"},"reviewedAt":"2026-08-31T16:21:43.741444Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/mvschwarz/openrig/tree/main/skills/_canonical/core/openrig-herdr"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install mvschwarz-openrig@llmmart"},{"target":"git","command":"git clone https://github.com/mvschwarz/openrig.git"}]}