@h-rig/product-ad-website 0.0.6-alpha.283
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/package.json +52 -0
- package/site/assets/favicon.svg +1 -0
- package/site/assets/og-card.png +0 -0
- package/site/assets/rig.css +199 -0
- package/site/assets/rig.js +66 -0
- package/site/assets/search-index.json +1 -0
- package/site/assets/site.css +46 -0
- package/site/assets/site.js +510 -0
- package/site/cli/index.html +88 -0
- package/site/config/index.html +256 -0
- package/site/docs/agent-tools/index.html +129 -0
- package/site/docs/architecture/index.html +42 -0
- package/site/docs/capabilities/index.html +84 -0
- package/site/docs/entities/index.html +68 -0
- package/site/docs/environment/index.html +62 -0
- package/site/docs/faq/index.html +17 -0
- package/site/docs/fleet-irc/index.html +44 -0
- package/site/docs/gate/index.html +128 -0
- package/site/docs/getting-started/index.html +50 -0
- package/site/docs/glossary/index.html +31 -0
- package/site/docs/index.html +68 -0
- package/site/docs/operator/index.html +49 -0
- package/site/docs/packages/index.html +36 -0
- package/site/docs/pi-extensions/index.html +15 -0
- package/site/docs/recipes/index.html +28 -0
- package/site/docs/runs/index.html +35 -0
- package/site/docs/security/index.html +29 -0
- package/site/docs/server/index.html +27 -0
- package/site/docs/swarm-commander/index.html +41 -0
- package/site/docs/task-sources/index.html +129 -0
- package/site/docs/troubleshooting/index.html +23 -0
- package/site/docs/validators/index.html +177 -0
- package/site/index.html +1653 -0
- package/site/plugins/examples/index.html +419 -0
- package/site/plugins/index.html +193 -0
- package/site/skills/index.html +140 -0
- package/site/variants/deck/index.html +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"pages":[{"url":"docs/index.html","title":"Docs","sections":[{"id":"","heading":"","text":"Docs How to run Rig now: install it, start foreground OMP with bare rig , dispatch from the inline /fleet card or the Swarm Commander model role, watch the cnet, materialize an exact drone card, and let the strict merge gate land only reviewed heads. $ curl -fsSL https://where.rig-does.work/install | bash $ rig OMP /tasks · /fleet · /drone"},{"id":"what-rig-is","heading":"What Rig is // session · cnet · operator","text":"What Rig is // session · cnet · operator Rig is a plugin-based product for scoped, validated coding work inside an OMP session. Bare rig launches upstream OMP with the bundled Rig extension loaded for the current workspace. The active OMP session is the runtime authority for cwd, transcript, tools, prompts, participants, and package-loaded capabilities; Rig adds fleet operation around it. The operator model: you stay in foreground OMP while the Rig extension maintains a bounded current CNet projection. /tasks , /fleet , and /drone append inline cards; the Swarm Commander model role uses generated discoverable CNet tools in the same chat. Terminal and web attach always select one exact detached drone run."},{"id":"the-loop","heading":"The run loop // one task or prompt, end to end","text":"The run loop // one task or prompt, end to end Every run follows the same story. Dispatch from /fleet or ask the Swarm Commander, let OMP/Pi work in a per-run isolated worktree under an enforced sandbox, run configured validators, ship through PR automation and the strict gate, then close the source task or materialize the next operator decision inline. Start → Dispatch → Work → Review → Merge"},{"id":"the-path","heading":"The path","text":"The path install → bare rig → session: CNet / Swarm Commander → dispatch a task → inline drone card → merge gate → extend"},{"id":"start","heading":"Start","text":"Start 00 // CAPABILITIES"},{"id":"card-capabilities","heading":"What Rig can do","text":"What Rig can do The capability map at a glance — foreground OMP, bounded CNet projection, inline actions, isolated worktrees, validators, strict review gate, exact-run attach, and the plugin/package surface. the whole surface 01 // START"},{"id":"card-getting-started","heading":"Getting started","text":"Getting started Install, open rig , enter the session, dispatch via /fleet or Swarm Commander chat, watch the cnet, and use rig doctor when setup needs diagnosis. $ rig 02 // RUNS"},{"id":"card-run-lifecycle","heading":"Runs & sessions","text":"Runs & sessions A run is a detached OMP session whose JSONL journal feeds a bounded current CNet projection and explicit inline snapshots. /fleet → /drone"},{"id":"operate","heading":"Operate","text":"Operate 03 // SESSION"},{"id":"card-operator-guide","heading":"Operator guide","text":"Operator guide Run foreground OMP with the Rig extension: current CNet projection, inline query/action cards, the Swarm Commander model role, exact-run attach, placement, and rig doctor . /fleet · /tasks · /drone 04 // GATE"},{"id":"card-merge-gate","heading":"The strict merge gate","text":"The strict merge gate Nothing merges on vibes. GitHub review approval, green CI, resolved threads, and the SHA-pinned merge — the full gate. --match-head-commit <sha> 05 // CLI"},{"id":"card-cli-reference","heading":"CLI reference","text":"CLI reference The launcher and the exact headless CLI: bare rig , rig doctor , version checks, workspace targeting, and the command groups that run the same operations for scripts and CI. $ rig doctor"},{"id":"architecture","heading":"Architecture","text":"Architecture LAYERS How Rig works The OMP session, Rig extension, workflow engine, task graph, plugin/package loading, and worktree-isolated execution path. DATA Entities & data model Tasks, runs, runtimes, worktrees, workflow entries, and the status fields that flow through the run journal and read-model. RELAY Security & relay Encrypted OMP collab semantics, the wss://where.rig-does.work relay boundary, sandbox evidence, and trust rules. ENV Environment variables Every RIG_* knob in one place — runtime adapter, sandbox switches, relay/registry overrides, GitHub auth."},{"id":"extend","heading":"Extend","text":"Extend 07 // PLUGINS"},{"id":"card-plugin-authoring","heading":"Plugins & packages","text":"Plugins & packages Current package semantics for the bundled Rig extension, project plugin list, task-source adapters, validators, hooks, and capabilities. packages + extension 08 // OMP/Pi"},{"id":"card-pi-extensions","heading":"OMP/Pi extensions","text":"OMP/Pi extensions Installable packages that add tools, slash commands, subagents, and behaviors to OMP/Pi, declared in runtime.pi.packages and materialized into .pi/settings.json . rig pi add 09 // SKILLS"},{"id":"card-skills","heading":"Skills","text":"Skills Standing prompt context for OMP/Pi sessions: where skill content lives now, how it is packaged, and what remains operator-owned. SKILL.md 10 // ENGINE"},{"id":"card-packages","heading":"Packages","text":"Packages The Bun-first package graph: the three-package mechanism floor plus the plugin packages composed above it. package graph"},{"id":"in-one-paragraph","heading":"What Rig is, in one paragraph","text":"What Rig is, in one paragraph Bare rig launches foreground OMP with the bundled Rig extension. From that one transcript you dispatch through inline CNet actions or the Swarm Commander model role, watch detached drones, and attach to one exact run when direct interaction is needed. Every run is its own supervised OMP session ( runId === sessionId ) with an isolated worktree, runtime home, credentials, compiled binaries, and OS sandbox. Its journal is projected back into the foreground cnet."}]},{"url":"docs/capabilities/index.html","title":"Capabilities","sections":[{"id":"","heading":"","text":"Capabilities The current surface is OMP-first: bare rig opens foreground OMP with the Rig extension, the CNet carries a bounded current projection and exact receipts, query aliases materialize inline cards, and the Swarm Commander model role uses generated discoverable tools. $ curl -fsSL https://where.rig-does.work/install | bash $ rig OMP /fleet OMP /tasks OMP /drone $ rig attach <run-id>"},{"id":"map","heading":"Capability map","text":"Capability map OMP Rig session Bare rig opens foreground OMP with the Rig extension: chat, the cnet, inline query cards, and no separate product server. TASKS Tasks /tasks reads work from the configured source — GitHub Issues or Linear — with state derived on read. RUNS Runs Every run is a detached OMP drone session. CNet projects current status, recent timeline, controls, and attach actions. RELAY Transport & relay Local or remote selects where a detached drone runs. The CNet workflow and exact-run attach contract stay the same. CAP Swappable capabilities Task source, reviewer, isolation backend, provider, and validators are all capability contributions resolved at boot. PKG Explicit packages rig.config.ts composes plugins; runtime.pi.packages pins OMP/Pi extension packages."},{"id":"session","heading":"Rig session","text":"Rig session Rig contributes a bounded CNet projection, model control, generated discoverable tools, and the /fleet , /tasks , and /drone query aliases inside foreground OMP. Each alias materializes cards in the same transcript. OMP remains the authority for transcript, cwd, tools, prompts, participants, and history."},{"id":"tasks","heading":"Tasks and runs","text":"Tasks and runs /tasks materializes a bounded summary from the configured source — GitHub Issues or Linear — with create, rename, and close actions owned by the tasks plugin; exact dispatch is the /fleet card action or rig run dispatch <taskId> through the run plugin. Dispatching starts a detached OMP drone session ( runId === sessionId ). The run read model supplies current CNet projections, and operator requests resolve through inline attention items or the same generated actions used by the Swarm Commander role."},{"id":"relay","heading":"Transport & collab relay","text":"Transport & collab relay Placement comes from the selected endpoint in .rig/state/connection.json ; RIG_REMOTE_ALIAS overrides it for scripts or one-off dispatch. Local and remote are placement choices for the same detached-drone runtime, not separate product modes. Terminal attach uses rig attach <run-id> ; web attach uses rig attach <run-id> --web or the exact run's CNet action. Rig resolves the private transport credential internally."},{"id":"cap","heading":"Swappable capabilities","text":"Swappable capabilities At startup Rig composes plugins that provide and consume capabilities resolved by @rig/kernel-seed . The task source, the PR reviewer / merge-gate policy, the runtime-isolation backend, the agent-harness provider, and the validators are capability contributions — each one replaceable by swapping the plugin that provides it."},{"id":"extend","heading":"Extensibility","text":"Extensibility Rig plugins are explicit packages for the workflow automation Rig runs around a session: task sources, validators, hooks, skills, roles, repo sources, task fields, and plugin commands. OMP/Pi extensions are separate community packages that add live-session tools, slash commands, and behavior inside the Pi session."},{"id":"headless-cli","heading":"Headless CLI","text":"Headless CLI The exact CLI is a first-class headless adapter over the same CNet and domain operations, for scripts, CI, release checks, and agents. Interactively, the same work runs through bare rig , inline /fleet / /tasks / /drone cards, exact-run attach, the Swarm Commander model role, and rig doctor ."}]},{"url":"docs/getting-started/index.html","title":"Getting started","sections":[{"id":"","heading":"","text":"Getting started Install Rig, run bare rig in a repo, dispatch through the inline /fleet card or the Swarm Commander model role, watch the cnet, materialize an exact drone card when needed, and use rig doctor for diagnosis."},{"id":"install","heading":"Install","text":"Install $ curl -fsSL https://where.rig-does.work/install | bash $ command -v rig $ command -v rig-run $ rig --version The installer fetches the prebuilt, self-contained rig binary for your platform (published to npm as @h-rig/cli-<os>-<arch> ) and links rig and rig-run directly to that binary. This is the post-launcher install path: no installed entrypoint executes a JS launcher. It writes direct shims at ~/.local/bin/rig and ~/.local/bin/rig-run . It also repairs the historical Bun bin names at ~/.bun/bin/rig and ~/.bun/bin/rig-run when they are missing or still launcher-shaped, while leaving a real binary alone. The installer verifies both entrypoints by running rig --help and rig-run --help before it prints done. Rig is on an alpha release line. Run rig --version for the exact installed build; the same public domain carries the transport used when Rig attaches to an exact detached run."},{"id":"open","heading":"Open the session","text":"Open the session $ cd /path/to/your-project $ rig Bare rig starts upstream OMP in the foreground for the workspace and loads the bundled Rig extension. The transcript is the CNet, and /fleet , /tasks , and /drone materialize inline cards there."},{"id":"session","heading":"Use the session","text":"Use the session The CNet keeps a bounded current projection in the transcript. The ambient feed shows the fleet aggregate plus active or attention-requiring runs; explicit queries and action receipts remain in OMP's normal session history. When a run needs a human, Rig materializes an inline attention item with the action owned by that run or request. The result is recorded as a CNet receipt in the same transcript."},{"id":"dispatch","heading":"Dispatch work","text":"Dispatch work action use it for /tasks Materialize one bounded task summary with create, rename, and close actions owned by the tasks plugin. Swarm Commander chat Ask the configured model role to query or act. It receives a bounded current projection and discovers generated CNet tools from the loaded plugins. /fleet Materialize one aggregate fleet card with the fleet-owned dispatch action; exact dispatch is also rig run dispatch <taskId> . /drone [ref] Materialize one exact run with current status, placement, stage progress, recent timeline, controls, and terminal/web attach. With no ref, it targets the newest projected run."},{"id":"first-dispatch","heading":"First dispatch","text":"First dispatch Open rig in the repo, then dispatch from the /fleet card, chat, or rig run dispatch <taskId> . Each path submits the owning plugin's typed CNet action and appends its durable receipt. operator Show ready tasks, then dispatch task 132. swarmer I'll query the configured task source, then use the run dispatch action. cnet dispatch applied · task 132 · run 8a31e7"},{"id":"operating-policy","heading":"Keep operating policy in chat","text":"Keep operating policy in chat Tell the Commander role how you want work handled as ordinary conversation. That context stays in OMP history. The current CNet runtime does not install a separate standing-order store or an autonomous wake loop."},{"id":"watch","heading":"Watch the cnet","text":"Watch the cnet Stay in the conversation while the fleet works. The bounded live projection updates current fleet, run, and attention items; query snapshots and action receipts remain in the transcript. The Commander role acts on operator turns rather than waking itself in the background."},{"id":"doctor","heading":"Diagnose setup","text":"Diagnose setup $ rig doctor Use rig doctor for setup and runtime health: sandbox backend checks, collab seams, and the conditions that keep the session attached to the fleet. Diagnosis lives in the CLI; daily operation lives in the Rig session."},{"id":"packages","heading":"Configure extensions","text":"Configure extensions Declare project data in .rig/rigfig.toml , or use .rig/rig.config.ts with defineConfig for executable project plugin extras. The application graph is always present. Pin OMP/Pi packages through runtime.pi.packages for live-session tools and slash commands; skills materialize to .pi/skills/ ."}]},{"url":"docs/runs/index.html","title":"The run lifecycle","sections":[{"id":"","heading":"","text":"The run lifecycle A run is a detached OMP (Pi) drone session: runId === sessionId , and its state is folded from that drone's OMP JSONL session journal. Runs dispatched through CNet use the same local-or-remote harness; you follow them through inline cards and can attach to one exact run."},{"id":"flow","heading":"The flow","text":"The flow rig → foreground OMP + Rig → /fleet or Swarm Commander → detached OMP drone ↘ current CNet projection · inline /fleet · inline /drone · exact-run attach · strict gate → merge"},{"id":"stages","heading":"The 12-stage lifecycle","text":"The 12-stage lifecycle stage what to watch Open Bare rig opens the OMP session for the workspace and starts the first fleet read. Select /tasks materializes task cards inline; the Swarm Commander model role can also inspect tasks before dispatch. Dispatch A run is created and the CNet fleet projection updates. Provision The harness prepares a per-run worktree, runtime home, credentials, compiled binaries, native tools, and an OS sandbox. Work The drone works inside its OMP session; that session's JSONL journal is the run authority. Unblock Approval and user-input needs appear as inline attention items. Resolutions return exact CNet receipts. Validate Configured validators check the finished worktree before closeout side effects. Verify The completion review decides whether the task is actually done. Commit The lifecycle commits the run worktree changes through the repo-native git capability. Push and PR The branch syncs, pushes, and opens or reuses the pull request. The run projection exposes the PR URL when present. Gate and merge The strict merge gate waits for current-head review, green CI, resolved threads, and a SHA-pinned merge. Closeout Source closeout reflects the merged PR"},{"id":"evidence-completion","heading":"Evidence-based completion","text":"Evidence-based completion Closeout does not mark a run completed just because the drone stopped cleanly. The lifecycle publishes an explicit completion verdict, then the run status follows that verdict. verdict when it lands published run status complete-with-evidence Closeout status is merged, opened, or skipped and evidence contains at least one of commit , diff , or pr . completed needs-attention Closeout produces zero evidence, or the closeout result itself says needs-attention. needs-attention failed An unexpected closeout error escapes the verdict mapper. failed Zero-diff closeouts intentionally land needs-attention : a no-op drone did not produce product evidence. Evidence is local and non-networked: committed state, non-empty product diff against the run base ref, or a PR URL recorded by closeout. Validation errors preserve the existing needs-attention path and carry their detail into statusDetail / errorText ."},{"id":"drone-view","heading":"CNet and drone cards","text":"CNet and drone cards The ambient CNet feed keeps one fleet aggregate plus active or attention-requiring runs current. /fleet appends one aggregate snapshot. /drone [ref] appends one exact run with status, placement, session liveness, stage progress, up to eight recent timeline entries, errors or PR facts when present, control actions, and terminal/web attach."},{"id":"read-model","heading":"Run state & the read-model","text":"Run state & the read-model @rig/run-plugin owns run journal encoding and the read model. A worker writes typed entries into the detached OMP session; the read model projects them for CNet, /fleet , /drone , automation, and release checks. Treat every projection as a view; the drone's JSONL session journal is authoritative."}]},{"url":"docs/operator/index.html","title":"Operator guide","sections":[{"id":"","heading":"","text":"Operator guide Operate one foreground OMP conversation. Bare rig loads the Rig extension for the workspace; the bounded CNet projection, inline queries and actions, and the Swarm Commander model role all live there."},{"id":"session","heading":"The session surface","text":"The session surface surface what it does CNet Maintains a bounded current projection and records explicit query/action receipts in the transcript. attention items Inline cards for runs, approvals, or questions that currently need operator input. Swarm Commander The configured model role in foreground OMP. It receives the bounded current projection and uses generated, discoverable CNet tools; it is not another process. CNet queries /fleet , /tasks , and /drone [ref] materialize inline CNet cards in the transcript."},{"id":"cnet","heading":"The cnet","text":"The cnet The CNet is the Rig extension's typed layer inside the OMP transcript. Feed items keep the current fleet aggregate plus active and attention-requiring runs updated. /fleet , /tasks , and /drone append explicit snapshots; actions append requests and exact receipts. Current run fields come from the run plugin's read model over detached OMP session journals. OMP remains the only foreground history writer."},{"id":"attention","heading":"Attention in the CNet","text":"Attention in the CNet Runs that need operator input and pending approval or question requests appear as ordinary inline CNet items. Each item exposes only actions contributed by its owning plugin. There is no separate pin above the editor."},{"id":"swarm-commander","heading":"The Swarm Commander","text":"The Swarm Commander The Swarm Commander lives in the chat. At each provider call it receives a bounded snapshot of currently visible CNet items. Loaded plugins generate discoverable query and action tools from their registrations, so tailored binaries expose the tools contributed by their actual plugin set. Tool names are generated implementation details, not a fixed public command vocabulary. Mutations must target the observed item id, target, and revision, and success means an exact durable CNet receipt. The role does not own a background process, standing-order subsystem, or autonomous wake loop."},{"id":"fiber-line","heading":"Give direction","text":"Give direction Use the enabled Send direction action on an exact run card, or ask the Commander role to use that generated CNet action. The run-control result must carry the exact session and effect receipt before CNet records it as applied."},{"id":"cnet-queries","heading":"Inline CNet queries","text":"Inline CNet queries command what it materializes /fleet One aggregate fleet card with counts, bounded recent rows, and the fleet-owned dispatch action. /tasks One bounded task summary with task-owned create, rename, and close actions; exact dispatch is the /fleet action or rig run dispatch <taskId> . /drone [ref] One exact run card with current status, placement, stage progress, recent timeline, error or PR facts when present, controls, and terminal/web attach. With no ref, it targets the newest projected run."},{"id":"attach","heading":"Attach to a detached drone","text":"Attach to a detached drone Attach always targets one exact detached run. Use rig attach <run-id> for terminal attach or add --web for web attach; the same actions are available on that run's inline /drone card. Rig resolves the private run credential internally. Bare rig and the Swarm Commander role are never attach targets."},{"id":"placement","heading":"Placement and endpoints","text":"Placement and endpoints Run placement is selected from the endpoint stored in .rig/state/connection.json . Set RIG_REMOTE_ALIAS to override it. Local and remote only say where the detached drone runs; CNet controls and attach behavior do not split into different modes."},{"id":"doctor","heading":"Diagnosis","text":"Diagnosis $ rig doctor rig doctor is the CLI diagnosis path for sandbox backend health and collab seams. Use it when the session cannot attach cleanly, when sandbox execution needs proof, or when relay behavior looks wrong."},{"id":"authority","heading":"Runtime authority","text":"Runtime authority Foreground OMP owns cwd, transcript, tools, prompts, participants, package-loaded capabilities, and its JSONL history. Rig adds the CNet reducer, rendering, generated tools, and typed actions inside that session. Each detached drone's own OMP JSONL session journal is authoritative for that run; inline CNet cards project it."}]},{"url":"docs/swarm-commander/index.html","title":"Swarm Commander","sections":[{"id":"","heading":"","text":"Swarm Commander Swarm Commander is the configured model role inside foreground OMP. It is not a process, supervisor, session, or attach target. CNet remains useful without the role."},{"id":"what-it-is","heading":"What it receives","text":"What it receives input contract current projection A bounded snapshot of currently visible CNet items at the provider call, including item ids, revisions, targets, summaries, and enabled actions. query tools Discoverable tools generated from every CNetQuery contributed by the loaded Rig plugins. action tools Discoverable tools generated from every CNetAction ; calls must use the observed item id, target, and base revision. receipts Exact durable query or action outcomes written through CNet into the same OMP transcript."},{"id":"how-to-use","heading":"How to use it","text":"How to use it Speak normally. The role discovers the tools provided by this tailored Rig binary rather than relying on a fixed, hardcoded tool deck. operator Show ready tasks and dispatch task 132. swarmer I'll query the configured task source, then use the enabled run dispatch action. cnet dispatch applied · task 132 · run 8a31e7 operator Tell run 41c to rerun only TaskList.spec.ts. swarmer I'll materialize the exact run and submit its Send direction action. cnet direction accepted · run 41c · exact session receipt"},{"id":"tool-surface","heading":"Generated tool surface","text":"Generated tool surface Tool names are derived from each contribution id and are intentionally not documented as stable commands. Query tools accept a typed target and query input. Action tools additionally require the exact projected item id and base revision. A tailored .rig/rig.config.ts binary therefore exposes only the CNet operations supplied by its embedded plugins and overrides. Discovery is part of the contract. The model searches by each contribution's title and description. It should never call hardcoded fleet, run-control, or inbox tool names."},{"id":"wake-mechanics","heading":"Operator turns only","text":"Operator turns only The current product does not install an autonomous Commander wake loop or a separate standing-order store. Instructions such as keep one drone on ready bugs are ordinary OMP conversation context until the operator asks the model to act."},{"id":"how-it-works","heading":"How it works","text":"How it works @rig/omp-extension-plugin installs the model-turn context and generated discoverable tools. The CNet coordinator routes each query or action to the plugin that owns its registration, then appends the request and exact outcome to OMP's normal JSONL session history. The role never writes run state directly. Detached drone state remains in each drone's own OMP JSONL journal; task state remains in the configured source; the foreground CNet is their typed projection and action surface."},{"id":"honest-limits","heading":"Honest limits","text":"Honest limits The bounded context is not complete history. The model uses generated queries when it needs a full current item. An action can apply only to an observed item revision. Stale or mismatched targets refuse instead of guessing. A transport send is not success. CNet reports applied only after the owning capability returns the exact durable receipt. Removing the Commander role does not remove CNet, inline cards, model control, or detached drone execution."}]},{"url":"docs/fleet-irc/index.html","title":"Drone messages","sections":[{"id":"","heading":"","text":"Drone messages A detached drone has a narrow journal-backed message lane to the operator. It is not an all-peer fleet bus: the current drone tool accepts only the operator as a destination."},{"id":"model","heading":"One run, one journal","text":"One run, one journal The drone-side irc tool is installed only in Rig run sessions. Outbound messages and operator replies are recorded in that run's OMP JSONL journal and exposed through the run read model."},{"id":"verbs","heading":"Drone tool operations","text":"Drone tool operations op parameters what it does send or omitted optional to , message or body , optional replyTo Append a message to the run journal. The only accepted destination is operator ; omitted means operator. inbox optional sinceSeq , optional limit Read that run's operator messages from the run read model. The limit defaults to 20 and clamps to 50."},{"id":"receipts","heading":"What a receipt means","text":"What a receipt means The drone tool returns a local journal sequence receipt after the message is appended. Foreground CNet action results follow a stricter rule: sending transport data is not enough; an action is applied only when the owning run capability returns the exact durable session/effect receipt."},{"id":"channels","heading":"Current channel boundary","text":"Current channel boundary The current product does not expose drone-to-drone broadcast, a Commander mailbox tool, or a fleet-wide wake bus. Operator-to-drone direction goes through the exact run's CNet action. Drone-to-operator messages remain facts in that run's journal."},{"id":"wire","heading":"Local and remote","text":"Local and remote Local and remote describe only where the detached drone is placed. The same run id, journal format, CNet action contract, and exact-run attach behavior apply to both placements."},{"id":"cnet-surface","heading":"In the CNet","text":"In the CNet /fleet: one bounded fleet aggregate; it is not a callsign inbox. /drone: current status, recent timeline, controls, and attach actions for one exact run; it does not render a full DM thread. Attention items: approvals and questions that need operator input are separate inline CNet items. Commander: uses generated CNet actions on an operator turn; it has no autonomous wake mailbox."},{"id":"from-a-drone","heading":"From inside a drone","text":"From inside a drone drone tool irc { \"message\": \"Blocked on APP_TOKEN; integration cannot run.\" } receipt journal append · to operator drone tool irc { \"op\": \"inbox\", \"sinceSeq\": 120, \"limit\": 10 }"},{"id":"semantics","heading":"Semantics, precisely","text":"Semantics, precisely The run journal is authoritative for message order. replyTo correlates an operator reply with a prior message. Unknown destinations and calls outside a Rig run session refuse structurally. Message transport does not synthesize a CNet action receipt."}]},{"url":"docs/agent-tools/index.html","title":"Agent tools","sections":[{"id":"","heading":"","text":"Agent tools A drone inside a Rig run does not probe the raw CLI. It gets a small typed tool pack: task state, scoped git, repo sync, runtime profile, review policy, and operator messaging. Every call is a schema'd op; unknown ops refuse with NEEDS_CLARIFICATION instead of becoming argv."},{"id":"tool-pack","heading":"Tool pack // detached-drone controls","text":"Tool pack // detached-drone controls tool what it controls execution seam rig_task Current task facts, scope, dependency state, artifacts, validation, task lookup, and closeout evidence records. Builds a typed rig-agent argv array. rig_git Run-scoped git status, preflight, branch sync, commit, snapshot, and PR open. Builds rig-agent git ... with only declared flags. rig_repo_sync Repo-sync lifecycle: sync, ensure, pins, verify, discover, baseline. Builds rig-agent repo-sync ... . rig_profile Worker runtime/model/plugin profile for the run. Builds rig-agent profile ... . rig_review Review mode and provider policy. Builds rig-agent review ... . irc Drone-to-operator fleet messages and run inbox reads. Appends/reads run journal IRC messages. The five rig_* tools are registered by the run worker extension after the IRC tool. They resolve $RIG_AGENT_BIN , run inside $RIG_TASK_WORKSPACE or the session cwd, spawn with shell:false , return stdout on success, and return structured COMMAND_FAILED details on non-zero exit."},{"id":"rig-task","heading":"rig_task // task state and artifacts","text":"rig_task // task state and artifacts op params argv info none rig-agent info scope files?: boolean rig-agent scope [--files] deps none rig-agent deps status none rig-agent status artifacts none rig-agent artifacts artifact-dir none rig-agent artifact-dir project-root none rig-agent project-root monorepo-root none rig-agent monorepo-root artifact-write filename and exactly one of file or content rig-agent artifact-write <filename> [--file <file>] ; content is stdin validate none rig-agent validate lookup id rig-agent lookup <id> record type: decision|failure , text rig-agent record <type> <text> help none rig-agent help completion-verification none rig-agent completion-verification completition-verification none rig-agent completition-verification — alias for the correct spelling rig_task({ \"op\": \"scope\", \"files\": true }) rig_task({ \"op\": \"artifact-write\", \"filename\": \"proof.txt\", \"content\": \"typecheck passed\\n\" }) rig_task({ \"op\": \"record\", \"type\": \"decision\", \"text\": \"kept provider writeback read-only\" }) Artifact write guard: artifact-write accepts exactly one source: file maps to --file , content goes to stdin. Both or neither is a schema refusal."},{"id":"rig-git","heading":"rig_git // scoped branch discipline","text":"rig_git // scoped branch discipline op params argv status task? rig-agent git status [--task <id>] changed task? , scoped? rig-agent git changed [--task <id>] [--scoped] preflight task? , strict? rig-agent git preflight [--task <id>] [--strict] sync-branch task? rig-agent git sync-branch [--task <id>] ensure-branch task? rig-agent git ensure-branch [--task <id>] commit task? , target?: monorepo|project|both , message? , allowEmpty? , scoped? rig-agent git commit [--task <id>] [--target ...] [--message ...] [--allow-empty] [--scoped] snapshot task? , output? rig-agent git snapshot [--task <id>] [--output <path>] open-pr task? , target?: monorepo|project , reviewer? , base? , title? , body? , draft? rig-agent git open-pr [--task <id>] [--target ...] [--reviewer ...] [--base ...] [--title ...] [--body ...] [--draft] rig_git({ \"op\": \"changed\", \"scoped\": true }) rig_git({ \"op\": \"preflight\", \"strict\": true }) rig_git({ \"op\": \"commit\", \"target\": \"project\", \"message\": \"tighten IRC proof\", \"scoped\": true }) rig_git({ \"op\": \"open-pr\", \"target\": \"project\", \"draft\": true, \"base\": \"main\" }) This is the drone's branch discipline panel. It is still git underneath, but the drone never builds arbit"},{"id":"rig-repo-sync","heading":"rig_repo_sync // remote repo pin line","text":"rig_repo_sync // remote repo pin line op params argv sync task? rig-agent repo-sync sync [--task <id>] ensure task? rig-agent repo-sync ensure [--task <id>] pins task? rig-agent repo-sync pins [--task <id>] verify task? rig-agent repo-sync verify [--task <id>] discover task? rig-agent repo-sync discover [--task <id>] baseline task? , refresh? rig-agent repo-sync baseline [--task <id>] [--refresh] rig_repo_sync({ \"op\": \"pins\" }) rig_repo_sync({ \"op\": \"baseline\", \"refresh\": true }) Use it when the sortie spans repos and the drone needs to prove the pin state before committing."},{"id":"rig-profile","heading":"rig_profile // worker profile","text":"rig_profile // worker profile op params argv show compact? rig-agent profile show [--compact] set Either preset: claude-code|codex-cli|codex-app-server or custom model? , runtime? , plugin? rig-agent profile set <preset> or rig-agent profile set [--model ...] [--runtime ...] [--plugin ...] rig_profile({ \"op\": \"show\", \"compact\": true }) rig_profile({ \"op\": \"set\", \"preset\": \"codex-cli\" }) rig_profile({ \"op\": \"set\", \"model\": \"gpt-5.5\", \"runtime\": \"pi\", \"plugin\": \"rig-run-worker\" }) Schema guard: profile set accepts a preset or the custom option trio, not both. That is enforced before argv construction."},{"id":"rig-review","heading":"rig_review // merge-gate review mode","text":"rig_review // merge-gate review mode op params argv show none rig-agent review show set mode: off|advisory|required , provider?: github rig-agent review set <mode> [--provider github] rig_review({ \"op\": \"show\" }) rig_review({ \"op\": \"set\", \"mode\": \"required\", \"provider\": \"github\" })"},{"id":"irc","heading":"irc // drone-to-operator comms","text":"irc // drone-to-operator comms The drone-side irc tool is the fiber line back to the operator. It is available only inside a Rig run session ( RIG_RUN_PROCESS=1 ); outside that context it refuses with CONFLICT . op params behavior send or omitted to? , message? , body? , replyTo? Appends an irc-message envelope to the run journal. v1 accepts only to: \"operator\" ; omitted means operator. Returns a local seq receipt. inbox sinceSeq? , limit? Reads run IRC messages from the run read model. Limit defaults to 20 and clamps to 50. irc({ \"message\": \"Blocked on missing Redis URL for this sortie.\" }) irc({ \"op\": \"send\", \"replyTo\": \"0a4...\", \"body\": \"Applied the operator direction; re-running closeout.\" }) irc({ \"op\": \"inbox\", \"sinceSeq\": 120, \"limit\": 10 }) The drone tool cannot DM another drone or a Commander mailbox. Foreground direction uses the exact run's generated CNet action; the Commander role can invoke that action during an operator turn."},{"id":"safety","heading":"Safety model // refusal before argv","text":"Safety model // refusal before argv Discriminated ops. Every rig_* tool is a Zod discriminated union on op . Unknown ops return NEEDS_CLARIFICATION . Strict params. Each op schema is strict; stray flags are validation errors, not best-effort argv. No shell passthrough. Successful parses build string arrays and call spawn(binary, argv, { shell:false }) . Run-bound workspace. Command cwd comes from RIG_TASK_WORKSPACE , falling back to the session cwd. Raw help nuance. The raw rig-agent command has a help subcommand, but no --help discovery surface. The in-session tools are the discoverable surface: schema, labels, renderers, and refusal messages. rig_git({ \"op\": \"rm-rf\", \"path\": \"/\" }) // NEEDS_CLARIFICATION: rig_git refuses unknown op `rm-rf` rig_task({ \"op\": \"artifact-write\", \"filename\": \"x.txt\", \"file\": \"a.txt\", \"content\": \"b\" }) // NEEDS_CLARIFICATION: artifact-write requires exactly one source"},{"id":"receipts","heading":"Receipts","text":"Receipts packages/run-plugin/src/worker/extension.ts registers irc first, then the five rig_* tools, and activates irc when RIG_RUN_PROCESS=1 . packages/run-plugin/src/worker/rig-agent-tools.ts defines the op schemas, argv builders, refusal behavior, env/cwd resolution, shell:false spawn, and tool registration. packages/run-plugin/src/worker/irc-tool.ts defines drone irc params, journal binding, send, inbox, and v1 operator-only delivery. packages/omp-extension-plugin/src/cnet/extension.ts exposes foreground operations as generated discoverable CNet tools rather than a second IRC control deck."},{"id":"see-also","heading":"See also","text":"See also Operator guide — how runs are steered from ground control. Drone messages — the narrow operator/drone journal lane. Swarm commander — model-role tools around the same run read model. Merge gate — where validation and review verdicts stop the sortie. Plugin examples — custom validator, task source, and Pi extension package."}]},{"url":"docs/gate/index.html","title":"The strict merge gate","sections":[{"id":"","heading":"","text":"The strict merge gate Nothing merges on vibes. A run grinds the same PR until GitHub says approved — green CI, resolved threads, no unresolved review — or it dies; the merge then ships the exact SHA the gate verified, nothing else. The policy lives in @rig/pr-review-plugin and is swappable via review.provider ."},{"id":"conditions","heading":"The gate, stated","text":"The gate, stated On every Rig-owned merge — unconditionally, independent of the completion-review policy — the merge stage refuses to call gh pr merge until all of the following hold for the current head SHA , read fresh from GitHub: strict merge gate · PR #418 review GitHub reviewDecision APPROVED ci checks 14/14 green (minus merge.allowedFailures) threads all review threads resolved head sha read & pinned @ 9f3c2e1 merge gh pr merge --match-head-commit 9f3c2e1 closed issue #418 · branch deleted"},{"id":"decision","heading":"The decision, as code // evaluateStrictPrMergeGate","text":"The decision, as code // evaluateStrictPrMergeGate The gate is one pure function over freshly-read GitHub evidence: evaluateStrictPrMergeGate(evidence) . It approves only when the reason list is empty — zero blocking reasons fire : // pr-review-gate.ts — approved iff no reason fired const approved = evaluated.reasonDetails.length === 0 ; Each reason is produced by reading the PR fresh: read errors, a missing head SHA, failed or pending CI checks, a blocking GitHub reviewDecision , and unresolved review threads. Every reason carries a reasonClass ( pending vs reject ) and a suggestedAction ( wait , fix , needs_attention ). A gate that is all-pending means \"keep watching\" (the loop polls); any reject reason means the head needs work before it can pass. GitHub evidence (read fresh, every cycle) ↓ evaluateStrictPrMergeGate (evidence) ↓ reasonDetails.length > 0 ? yes → all pending ? wait/poll : reject → give direction / re-request review no → merge --match-head-commit <sha>"},{"id":"evidence","heading":"The evidence the gate reads","text":"The evidence the gate reads The gate trusts nothing it didn't read. It is built around the surfaces GitHub actually exposes on a PR — read live from the GitHub API/CLI, not assumed: surface behavior reviewDecision GitHub's own PR-level review state: APPROVED , CHANGES_REQUESTED , or REVIEW_REQUIRED . Only APPROVED (with no other blocking reason) clears the gate. check runs / statuses Every CI check on the current head, classified success / pending / failure. Failures block; pending holds; success (or an allowlisted name) passes. review threads Every review conversation on the PR. Any unresolved thread holds the merge, regardless of the review decision. head SHA The current head commit. The merge is pinned to it; if it can't be read, the gate refuses to merge at all."},{"id":"ci","heading":"Review, CI, threads","text":"Review, CI, threads GitHub review must be APPROVED for the current head. A CHANGES_REQUESTED or REVIEW_REQUIRED decision blocks. CI must be green for the current head, minus the explicit merge.allowedFailures allowlist. A pending check always counts as pending — the gate waits rather than merging on an incomplete run. Every review thread resolved. Unresolved threads hold the merge regardless of the review decision."},{"id":"reason-codes","heading":"Why a head was held // the reason codes","text":"Why a head was held // the reason codes When the gate withholds a merge it emits a structured reasonDetails list, one entry per failed condition. Each entry has a stable machine code, a reasonClass , and a suggestedAction . These are the codes the gate can emit: code class plain English read_error reject A required PR evidence surface (PR view, checks, comments, or threads) could not be read in full. The gate refuses to merge on partial data. missing_head_sha reject The PR head SHA could not be read, so the merge cannot be pinned to the current head at all. ci_failed reject A CI check failed on the current head (after subtracting merge.allowedFailures ). check_pending pending A CI check is still running/queued on the current head. The gate waits. review_decision_blocking reject GitHub's reviewDecision is CHANGES_REQUESTED or REVIEW_REQUIRED — a required review is unresolved. review_thread_unresolved reject An open review thread (any author) is unresolved. Resolve it or address the comment. The pending-vs-reject split is what makes the loop patient instead of brittle. Pending reasons mean \"wait and poll\"; reject reasons mean \"this head needs action\" — Pi receives direction on the ac"},{"id":"pinned","heading":"The SHA-pinned merge","text":"The SHA-pinned merge Passing the gate is necessary but not sufficient — the merge must ship the code the gate verified : $ gh pr merge --match-head-commit <gate-verified-sha> … If a commit races in between gate-pass and merge, --match-head-commit makes the merge fail instead of shipping unreviewed code. The run loops back to gate evaluation for the new head. Branch protection and repo rules are obeyed by default ( merge.bypass is an explicit, off-by-default escape hatch)."},{"id":"bypass-allowed-failures","heading":"merge.bypass and merge.allowedFailures // the two escape hatches","text":"merge.bypass and merge.allowedFailures // the two escape hatches Two merge fields adjust what the final gh pr merge tolerates. Neither one weakens the strict gate — the gate has already passed by the time the merge command runs. field type / default effect merge.bypass boolean · off When true , Rig appends --admin to gh pr merge . That is GitHub's administrator override: it merges past GitHub branch-protection rules (required reviewers, required status checks, linear-history rules) using the operator's admin privileges. merge.allowedFailures string[] · [] A check-name allowlist. Checks whose name matches an entry are not counted as a CI failure by the gate. Entries support * wildcards, matched case-insensitively against the full check name. merge: { mode: \"auto\" , bypass: false , // keep GitHub branch protection in force allowedFailures: [ \"license/cla\" , \"codecov/*\" ], // known-flaky / non-gating checks }; Security implication of bypass. --admin overrides GitHub's server-side protection — the very rules a security team configures to require human review. What it does not override is Rig's own machinery: the run still only reaches the merge after evaluateStrictPrMerge"},{"id":"stale","heading":"A moved head triggers action, not a shrug","text":"A moved head triggers action, not a shrug When the branch moves past the reviewed SHA, the gate doesn't merge on the old approval and doesn't silently wait forever — GitHub dismisses or re-requires review on the new head, and the gate holds: the head SHA changes, so any prior APPROVED decision no longer clears the current head, the gate waits for a current-head review decision and green current-head checks, there is a short window between a push and CI/review completing where the gate correctly reports pending — that's the system working, not stuck."},{"id":"re-review-window","heading":"Watching the gate re-arm // cnet + /drone","text":"Watching the gate re-arm // cnet + /drone Here is what an operator sees when Pi pushes a fix and the gate re-arms. The branch moved past the last reviewed SHA, so the gate holds on the new head, waits for current-head CI and a fresh review decision, and does not merge on the old approval . Follow it live: run projection · PR #418 › push head a1b2c3d → origin › gate check_pending (CI re-running @ a1b2c3d) · class pending // old approval no longer counts — branch moved past it › gate review_decision REVIEW_REQUIRED · waiting on current-head review // CI goes green and the review approves on the new head › ci 14/14 green @ a1b2c3d › review reviewDecision APPROVED @ a1b2c3d › gate approved · 0 blocking reasons › merge gh pr merge --match-head-commit a1b2c3d Nothing here is a stuck state. The check_pending and review_decision_blocking lines are followed by a re-poll — the loop is watching, not failing — and the merge fires when a green, approved current head lands. Materialize /fleet to find the run or /drone [ref] for its current status and recent timeline; detailed gate and diff diagnostics remain in the owning run "},{"id":"config","heading":"Configuring the gate","text":"Configuring the gate review: { mode: \"required\" , // \"off\" | \"advisory\" | \"required\" provider: \"github\" , // GitHub reviews + checks (the default provider) }; merge: { mode: \"auto\" , // \"auto\" | \"off\" | \"pr-ready\" method: \"repo-default\" , // \"repo-default\" | \"squash\" | \"merge\" | \"rebase\" deleteBranch: \"repo-default\" , allowedFailures: [], // explicit allowlist of acceptable failing checks // bypass: true // admin-merge past branch protection (off by default) }; review.mode governs the completion review gate (the AI verdict on task completion); the strict merge gate above is always on for Rig-owned merges. The review provider is github (GitHub reviews + status checks); it is swappable via review.provider by contributing a different @rig/pr-review-plugin -shaped policy. Inspect or change the completion policy live: rig review show / rig review set <off|advisory|required> [--provider github] . Full field reference: rig.config.ts → . Dogfooded. Rig gates and merges PRs into its own repository with this exact machinery — the gate chain described above is the one that shipped the gate."},{"id":"see-also","heading":"See also","text":"See also The run lifecycle → — where the merge gate sits in the run, and how to follow a run and give direction while it waits. rig.config.ts → — the full review , merge , pr , and automation field reference. Security & trust → — merge identity, GitHub tokens, branch protection, and what merge.bypass hands over. Environment variables → — the GitHub auth the gate reads when fetching PR evidence."}]},{"url":"docs/troubleshooting/index.html","title":"Troubleshooting","sections":[{"id":"","heading":"","text":"Troubleshooting Start from the CLI diagnostic and the CNet. rig doctor checks discovery, relay/web, package pins, and sandbox seams; /fleet materializes the aggregate, and /drone materializes one run's current status and controls."},{"id":"first-moves","heading":"First moves","text":"First moves Run rig doctor from the CLI. Run /fleet to materialize the current fleet projection inline. Run /drone [ref] to materialize one exact run and use its enabled controls or attach actions. For attach problems, confirm the detached run is live and address it by its exact run id."},{"id":"symptoms","heading":"Common symptoms","text":"Common symptoms symptom likely cause fix /fleet , /tasks , or /drone is missing Rig extension was not loaded into the OMP session Relaunch with bare rig ; then run rig doctor . Run attach fails Run is not live, is stale, or has not registered its private attach session Refresh /drone <run-id> , then retry rig attach <run-id> [--web] . Do not substitute a transport credential. Capabilities differ between operators Packages were added locally instead of pinned for the team Commit runtime.pi.packages or .pi/settings.json . A run needs input An approval or question is pending Use the corresponding inline attention item; /drone [ref] shows the run's current state and controls. Automation sees stale run data Reading an old run-plugin projection Refresh the CNet query; the detached drone's OMP JSONL session journal is authoritative."},{"id":"cli-diagnostics","heading":"CLI diagnostics","text":"CLI diagnostics The headless CLI and its --json projections are first-class for CI, release checks, and agents. Treat them as views of run state, not as the source of live OMP session state. Still stuck? Capture rig doctor output, the run id from /fleet , and the exact state and package facts materialized by /drone ."}]},{"url":"docs/architecture/index.html","title":"Architecture","sections":[{"id":"","heading":"","text":"Architecture Rig is a plugin-based product over a 3-package mechanism floor. Bare rig opens foreground OMP with the CNet extension; dispatch creates supervised, detached OMP drone sessions. The Swarm Commander is a model role inside the foreground session."},{"id":"runtime-authority","heading":"The mechanism floor // three packages, nothing else","text":"The mechanism floor // three packages, nothing else Only three packages are not plugins. @rig/contracts is the dependency root (schemas, types, IPC/panel shapes). @rig/core is the config + plugin-composition library ( defineConfig / definePlugin , the plugin host); it is harness-agnostic, not a product host or runtime. @rig/kernel-seed is the irreducible bootstrap seed — the plugin ABI and capability resolver. Everything else is composed above the floor: the Rig application apps/rig ( @rig/rig ) owns the process entrypoints, argv routing, and the standard plugin graph, and every product surface — session extensions, runs, tasks, transport, config — is a plugin it composes. rig // apps/rig (@rig/rig) — process entrypoints · argv routing · plugin graph · official build ↳ @rig/kernel-seed // bootstrap seed · plugin ABI · capability resolver ↳ @rig/product-entrypoint-plugin // bare rig · foreground OMP launch/resume · help ↳ @rig/omp-extension-plugin // CNet runtime · inline cards · model control · generated tools · attach effects /fleet · /tasks · /drone [ref] // materialize inline CNet cards rig attach <run-id> [--web] ↳ exact detached drone ⇒ private credential resolved internally ↳"},{"id":"process-map","heading":"Process map // what runs where","text":"Process map // what runs where piece role authority rig The Rig application ( apps/rig , @rig/rig ). Owns the process entrypoints ( bin/rig ), argv routing, the standard plugin graph, and the official build invocation; every command comes from the domain plugin that owns it. Composes and dispatches into the plugin graph. @rig/executable-binary-target-plugin Build-time executable assembly + target-specific standard/tailored ArtifactSet compiler that apps/rig invokes. It reads a build descriptor and never imports the app — it is not the CLI launcher. None at runtime; produces the compiled binary. Foreground OMP + Rig Bare rig opens the interactive OMP runtime for the workspace and loads the bundled Rig extension. OMP owns cwd, transcript, tools, prompts, participants, and native history. The foreground conversation and CNet authority. Swarm Commander A model role inside that foreground OMP session. The Rig extension supplies bounded current context and generated discoverable tools; it is not a process or attachable session. The same foreground OMP turn and journal. Detached drone One supervised OMP run session ( runId === sessionId ) placed locally or remotely. Its journal drives its"},{"id":"packages","heading":"Package layering // floor + ~30 plugins","text":"Package layering // floor + ~30 plugins The Bun workspace keeps imports flowing one way: floor packages out to plugins, never back. @rig/core owns defineConfig , definePlugin , and the plugin host; @rig/kernel-seed owns the plugin ABI. The Rig application apps/rig owns the process entrypoints, argv routing, first-party graph, and official build. @rig/executable-binary-target-plugin only compiles target executables from that app. Product entrypoint, OMP CNet runtime, run lifecycle, and transport behavior remain owned by their focused plugins. The application-owned first-party graph is always present. Project-specific plugins may be added through .rig/rig.config.ts ; OMP/Pi packages declared in runtime.pi.packages supply live-session tools and commands. Nothing is auto-scanned."},{"id":"plugins","heading":"Plugin contributions // everything is a plugin","text":"Plugin contributions // everything is a plugin Plugins contribute validators, hooks, skills, agent roles, repo sources, task fields, task sources, CLI commands, lifecycle stages, capabilities, read models, blocker classifiers, session extensions, CNet queries/actions, and seed entrypoints. They are explicit packages composed by config and validated at construction. CNet query/action registrations become inline operator surfaces and generated model tools; unrelated OMP tools and slash commands still belong in OMP/Pi extension packages."},{"id":"state","heading":"State model // derived, not stored","text":"State model // derived, not stored A run is a detached OMP (Pi) session — runId === sessionId and run state is folded from that session's JSONL journal. @rig/run-plugin owns the journal codec and read-model presentation. Task state is derived from the source on read — GitHub Issues or Linear — so there is no local task database. CNet items carry operator actions; attach targets the selected run by id while the underlying credential remains private transport data."}]},{"url":"docs/entities/index.html","title":"Entities & data model","sections":[{"id":"","heading":"","text":"Entities & data model Rig stores almost nothing of its own. A run is an OMP (Pi) session; task state is derived from GitHub Issues or Linear on read; and CNet, /fleet , /tasks , and /drone render projections owned by the plugins behind them. There is no local task database."},{"id":"current-entities","heading":"Current entities","text":"Current entities entity what owns it what it means Run OMP/Pi runtime + @rig/run-plugin A run is a detached OMP drone session: runId === sessionId . run-plugin folds its OMP JSONL journal into the read-model projection. Task The source (GitHub Issues / Linear) via @rig/tasks-plugin Task state is derived from the source on read. No local task DB — the issue tracker is the store. Session surface @rig/omp-extension-plugin + owning plugins The bounded foreground CNet, generated tools, model control, and inline /fleet , /tasks , /drone cards. Each card reads from its owning plugin projection. Attach target @rig/transport-plugin + operator attach capability One exact live detached run, selected by runId . The private relay credential is resolved internally for terminal or web attach. Attention item @rig/run-plugin + @rig/omp-extension-plugin An inline CNet item for a run, approval, or question that currently needs operator input, with actions owned by the contributing plugin. Package composition rig.config.ts / rigfig.toml / .pi/settings.json Explicit plugin and OMP/Pi package inputs; no package is discovered by accident."},{"id":"derived-tasks","heading":"Derived task state","text":"Derived task state Task state is not persisted by Rig. On read, @rig/tasks-plugin pulls the current state from the configured source — GitHub Issues or Linear — and projects it into /tasks . There is no local task database and nothing on disk that holds task state: change an issue in GitHub or Linear and Rig reflects it on the next read."},{"id":"status","heading":"Statuses","text":"Statuses Status labels describe operator-visible state: an active detached run, local or remote placement, an attached terminal or web client, a pending gate, or disconnected transport. Task status is whatever the source reports at read time. Nothing here is a second stored record that can drift from the run session or source issue."},{"id":"packages","heading":"Plugin/package state","text":"Plugin/package state The Rig application supplies the first-party plugin graph. Executable project extras are composed through rig.config.ts ; declarative projects select plugin-owned data in rigfig.toml . OMP/Pi packages are declared through runtime.pi.packages and materialized into .pi/settings.json . Skills materialize to .pi/skills/ for OMP/Pi to read natively."}]},{"url":"docs/server/index.html","title":"OMP relay + web","sections":[{"id":"","heading":"","text":"OMP relay + web The relay transports encrypted detached-drone sessions. The public product surface is run-addressed attach: choose one exact run in the CNet or use rig attach <run-id> [--web] ; Rig keeps the raw collab credential internal."},{"id":"relay-authority","heading":"Relay, not authority","text":"Relay, not authority The relay moves encrypted frames between a detached drone and its attached clients. It does not own transcript, cwd, tools, packages, or run state. If a browser tab closes or a network link drops, that drone's OMP session remains authoritative; foreground Rig rediscovers it by run id and rebuilds the CNet projection. Dispatch, placement, discovery, and attach resolution are owned by @rig/transport-plugin . surface purpose Selected endpoint .rig/state/connection.json chooses local or remote placement for a detached drone; RIG_REMOTE_ALIAS overrides it for one launch. rig attach <run-id> Attaches this terminal to the exact live detached drone after Rig resolves its private credential. rig attach <run-id> --web Opens web attach for that exact detached drone without exposing its private transport credential. wss://where.rig-does.work Default encrypted transport for detached-run discovery and attach; private credentials remain client-side runtime state."},{"id":"multiroot","heading":"Multi-root hosting","text":"Multi-root hosting A running server can host multiple project roots concurrently. Select an endpoint in .rig/state/connection.json or set RIG_REMOTE_ALIAS to place a detached run on another host. Placement changes where the drone executes, not the CNet workflow, model role, or attach semantics."},{"id":"doctor","heading":"Readiness checks // rig doctor","text":"Readiness checks // rig doctor rig doctor probes the pieces operators actually depend on: OMP session discovery, Rig extension load, relay reachability, package pins, and sandbox seams. It is a CLI command; inside the session use /fleet to inspect the aggregate, /drone for one exact run, and inline attention items for pending operator input."},{"id":"web","heading":"Install and web endpoints","text":"Install and web endpoints The same domain serves the install script and the collab web viewer. Product copy should point operators to curl -fsSL https://where.rig-does.work/install | bash , then bare rig . It should not ask them to choose or start a separate long-lived service before using the Rig session."},{"id":"headless-service","heading":"Headless service commands","text":"Headless service commands The rig server and rig run command groups are first-class headless entry points for automation, scripts, and remote deployments. The interactive model is foreground OMP plus the CNet runtime, with detached drones supervised behind it and the Swarm Commander acting only as a model role."}]},{"url":"docs/security/index.html","title":"Security & trust","sections":[{"id":"","heading":"","text":"Security & trust Security follows the current architecture: OMP owns live session state, the collab relay carries encrypted links, Rig packages are explicit, and the exact CLI runs the same scoped operations as the session."},{"id":"session-authority","heading":"Session authority","text":"Session authority Live authority is the OMP session: cwd, prompts, tools, transcript, participants, and loaded OMP/Pi packages. The Rig session surfaces display and route that state instead of replacing it with a separate live service."},{"id":"relay","heading":"Relay trust","text":"Relay trust Shared sessions use wss://where.rig-does.work . Room secrets belong in join-link fragments, not server logs or repo files. The relay coordinates encrypted transport and presence; it is not the source of truth for work state."},{"id":"packages","heading":"Explicit packages","text":"Explicit packages The apps/rig application owns the first-party plugin graph. Projects add executable extras through .rig/rig.config.ts or select plugin-owned data in .rig/rigfig.toml ; nothing is discovered by scanning arbitrary package directories. OMP/Pi packages are pinned in runtime.pi.packages or .pi/settings.json for live-session capabilities. Task sources are GitHub Issues and Linear only; task state is derived from the source on read, with no local task database."},{"id":"sandbox","heading":"The execution harness","text":"The execution harness Every run — /fleet dispatch, Swarm Commander dispatch, and ad-hoc agent runs alike — goes through the same fully-provisioned harness: a per-run isolated git worktree, isolated runtime home, and isolated credentials, so parallel runs never share writable state (the isolation backend is owned by the swappable isolation plugin); per-run compiled binaries materialized into the runtime — the rig-agent toolbelt, controlled-bash, and hooks — so the agent never invokes ambient host tooling directly; an OS sandbox (macOS seatbelt / Linux bwrap), enforced by default ( workspace.sandbox = \"enforce\" ). It fails closed: if the host cannot provide a backend, the run refuses rather than silently downgrading. Runtime command policy is enforced through a single policy-guard hook channel owned by the guard plugin. Tokens stay in per-project private state or scoped per-run injection, never task payloads or committed files."},{"id":"gate","heading":"Merge gate","text":"Merge gate The merge gate is GitHub reviews plus status checks, owned by the swappable PR-review plugin (default provider github ). The merge is pinned to the exact reviewed commit via gh pr merge --match-head-commit <sha> — a commit pushed after the gate clears makes the merge fail."},{"id":"headless-cli","heading":"The headless CLI","text":"The headless CLI The exact task/run/service commands and terminal diagnostics are a first-class headless adapter over the same scoped operations, for release checks, scripts, CI, and agents. Interactively, the same work runs through bare rig , inline CNet items, /fleet , /tasks , /drone , exact-run attach, and rig doctor ."},{"id":"see-also","heading":"See also","text":"See also Trust mechanics connect to OMP relay + web , environment variables , OMP/Pi extensions , and the strict merge gate ."}]},{"url":"docs/environment/index.html","title":"Environment variables","sections":[{"id":"","heading":"","text":"Environment variables Most Rig behavior is configured in .rig/rigfig.toml (or .rig/rig.config.ts ) and through the Rig session. Environment variables are escape hatches for launch, placement, relay, package, sandbox, and the exact headless CLI. Anything settable by env can also be set from config via the generic env map; ambient env always wins."},{"id":"launcher","heading":"Launcher and workspace","text":"Launcher and workspace variable meaning RIG_PROJECT_ROOT Project-root override used when a script cannot launch from the workspace directory. Prefer rig --workspace <path> for humans. RIG_STATE_DIR / RIG_LOGS_DIR Override where Rig writes private per-project state and run logs. RIG_REMOTE_ALIAS Override remote placement for dispatch. Without it, Rig uses the selected endpoint in .rig/state/connection.json ."},{"id":"relay","heading":"Relay and web","text":"Relay and web Detached run sessions use wss://where.rig-does.work by default. Deployment-specific env ( RIG_BACKBONE_HOST , or the explicit RIG_RELAY_URL / RIG_REGISTRY_URL ) can point relay and discovery at another trusted host. Private attach credentials remain runtime transport state. These mirror the [runtime.server] config section."},{"id":"packages","heading":"Packages","text":"Packages runtime.pi.packages is the team-facing package pin. The Pi harness env knobs ( RIG_PI_BINARY , RIG_PI_MODEL , RIG_PI_PROVIDER ) are for release/testing only; normal projects should commit package intent in config or .pi/settings.json ."},{"id":"sandbox","heading":"Sandbox and security","text":"Sandbox and security The OS sandbox is workspace.sandbox ( enforce by default). RIG_RUNTIME_SANDBOX and RIG_SANDBOX_BACKEND are fail-closed diagnostics for the sandbox axis: if a host cannot provide the configured backend, the run must not silently downgrade. GitHub, model, and other credentials are supplied to runs through scoped, per-run injection (the RIG_BAKED_* family), never task payloads or committed files."},{"id":"older-variables","heading":"Older variables","text":"Older variables Some older environment variables remain readable by the exact CLI and CI probes. Prefer config and the current variables above for new setups."}]},{"url":"plugins/index.html","title":"Plugin authoring","sections":[{"id":"","heading":"","text":"Plugin authoring Almost everything in Rig is a plugin — the CLI launcher and the Rig session included. The apps/rig application composes the first-party graph; a project adds explicit executable packages through rig.config.ts . Live OMP capability comes from OMP/Pi packages such as runtime.pi.packages . Rig never scans disk, so if a package is not composed, it does not exist."},{"id":"walkthrough","heading":"The walkthrough: definePlugin → published","text":"The walkthrough: definePlugin → published Six steps from empty package to a composable plugin a consumer can bun add . No registry, no marketplace — npm is the distribution; live OMP capability still comes from OMP/Pi packages. step what you do 1. scaffold A plain Bun/npm package: bun init , add @rig/core (and @rig/contracts types) as deps. One default export. 2. declare definePlugin({ name, version, contributes }) — validated against the RigPlugin schema at call time; invalid shapes throw immediately, not at dispatch time. 3. implement Executable contributions (validators, task sources, hooks…) carry their function on the same entry as their metadata — one object, no separate runtime channel. See the single-channel model . 4. test Contract tests with bun:test : definePlugin throws on bad shape, ids namespace, and you call the executable fns directly off the returned object — no session required. 5. wire In a consuming project: plugins: [myPlugin(options)] in rig.config.ts . rig plugin list shows what it contributes; rig plugin validate --task <id> exercises its validators. 6. publish bun publish like any npm package. Consumers bun add it and import it in their config — no registry"},{"id":"worked-examples","heading":"Worked examples // copyable packages","text":"Worked examples // copyable packages The walkthrough is the contract. The examples are the sortie brief: three complete packages you can copy into a repo, wire through real config, and verify against the loaded plugin host. They cover the three extension seams teams usually need first: example what it proves load path validator plugin A custom check enters the same validation gate as std:typecheck . .rig/rig.config.ts — executable plugin code. task-source plugin A custom taskSource.kind resolves through the same factory path as the standard adapters. .rig/rig.config.ts — executable factory. OMP/Pi package A live-session tool is pinned with runtime.pi.packages and materialized into .pi/settings.json . rig.config.ts or declarative rigfig.toml — package data, not a Rig plugin. Go to plugin examples → for the full file listings, config, verification steps, and the exact source seams each one relies on."},{"id":"entry","heading":"The entry point","text":"The entry point import { definePlugin } from \"@rig/core\" ; export default definePlugin ({ name: \"my-plugin\" , version: \"0.1.0\" , contributes: { /* any subset of the eight types below */ }, }); definePlugin validates the shape against the RigPlugin schema in @rig/contracts and throws on invalid input. The return value is what you pass to plugins: [...] . Effect Schema is the runtime schema/validation library Rig's contracts are written in — the same one used across @rig/contracts . Every RigPlugin field ( name , version , each contribution type) is an Effect Schema.Struct , so definePlugin decodes your object against it and rejects the bad shape before anything boots. The whole plugin contract lives in one file: packages/contracts/src/plugin.ts ."},{"id":"where-contributions-land","heading":"Where each contribution lands // metadata in, registry out","text":"Where each contribution lands // metadata in, registry out Every contribution is two things: metadata at config-load time, a registry entry at boot. The plugin host ( @rig/core ) flattens all composed plugins, dedupes the ids, then routes each contribution type to its own registry: // appPluginGraph + rig.config.ts project extras ↓ createPluginHost (plugins) // flatten + dedupe ids, enforce namespacing ↓ validators → createValidatorRegistry // in-process run(ctx) hooks → harness settings file // managed files skills → .pi/skills/<id>/ // materialized SKILL.md repoSources → createRepoRegistry agentRoles → createAgentRoleRegistry // role:<id> → model taskFieldSchemas → createTaskFieldRegistry taskSources → buildTaskSourceRegistry // resolved by kind panels → read-model presentation cargo // display metadata for session surfaces capabilities → capability resolver // provides / requires / replaces cliCommands → the rig command surface Every registry above lives in @rig/core (kernel capabilities resolve through @rig/kernel-seed ). Contributions carry their executable fn on the same entry as their metadata — see the single-channel model ."},{"id":"namespacing","heading":"Namespacing","text":"Namespacing Every registration id is <plugin-name>:<local-id> (e.g. my-plugin:typecheck ). The plugin host ( @rig/core ) throws at config-load time on duplicate plugin names, duplicate contribution ids, duplicate executable task-source kinds, and metadata/runtime drift."},{"id":"types","heading":"The contribution types","text":"The contribution types type what it adds validators Pass/fail gates that run at task completion (carry an executable run(ctx) ). hooks PreToolUse / PostToolUse / UserPromptSubmit / Stop / SessionStart / SessionEnd hooks, written into the active harness settings file. skills SKILL.md prompt context, materialized into .pi/skills/<id>/ . repoSources External repositories the runtime may clone/reference. agentRoles role:<id> → model resolution. taskFieldSchemas Structured fields parsed from issue-body blocks. taskSources kind -keyed task adapters (carry an executable factory ; even first-party ones resolve this way). cliCommands Commands callable directly as rig <command> (or rig plugin run <id> ). stages / stageMutations Run-lifecycle stages and mutations (validate / verify / commit / push / PR / merge…). capabilities Named product capabilities other plugins resolve through the capability model. panels Read-model presentation cargo — display metadata consumed by session surfaces. Reach for sessionExtensions when you need live in-session tools. blockerClassifiers Classifiers that label why a run is blocked. sessionExtensions Executable installers wired into the OMP/Pi session at creati"},{"id":"single-channel","heading":"The single-channel model: metadata + executable, one object","text":"The single-channel model: metadata + executable, one object A plugin is one object . Each contributes entry carries its schema-validated metadata and its executable fn together — there is no second definePlugin argument and no separate runtime channel. Effect Schema can't represent functions, so definePlugin decodes the object against the metadata manifest and simply ignores the fns (Schema strips excess), returning the original object unchanged with its fns intact: export default definePlugin ({ name: \"my-plugin\" , version: \"0.1.0\" , contributes: { validators: [{ id: \"my-plugin:typecheck\" , category: \"integration\" , // metadata… async run (ctx) { // …and the executable fn, same entry // ctx: { taskId, workspaceRoot, scope, monorepoRoot?, artifactsDir?, taskConfig? } return { id: \"my-plugin:typecheck\" , passed: true , summary: \"ok\" }; }, }], }, }); The plugin host auto-registers each executable into the ValidatorRegistry at boot; taskValidate dispatches in-process , no subprocess. Because the fn rides on the returned object, tests reach it directly ( plugin.contributes?.validators?.[0].run(ctx) ) without booting a session. Kernel capability implementations ride the same way, in a c"},{"id":"validators","heading":"Validators","text":"Validators Categories: boundary · contract · integration · regression · external · custom . Result shape: { id, passed, summary, details? } . Wire one to a task with a validator:<id> label, or exercise a plugin's validators against a task with rig plugin validate --task <id> . For how validators and hooks behave at run time — categories, the gate, hook events — see validators & hooks → ."},{"id":"hooks","heading":"Hooks","text":"Hooks hooks: [{ id: \"my-org:stop-check\" , event: \"Stop\" , // one of the six HookEvent literals matcher: { kind: \"all\" }, // \"all\" | { kind: \"tool\", name } | { kind: \"glob\", pattern } command: \"/repo/scripts/my-stop-check.sh\" , }] Written into the active harness settings file when Rig prepares the workspace. The materializer is idempotent — operator-authored entries (no _rigPlugin marker) are preserved; plugin-owned entries are replaced and tagged _rigPlugin: \"<plugin>\" . A hook may ship a shell command or a typed implementation ( { hooks: { [hookId]: fn } } on the same plugin object); the runtime materializes a shim command for typed hooks automatically."},{"id":"skills","heading":"Skills","text":"Skills skills: [{ id: \"my-plugin:boundary-analysis\" , path: new URL ( \"skills/boundary-analysis/SKILL.md\" , import.meta.url).pathname, description: \"Boundary analysis prompt\" , }] On boot the runtime validates each skill and materializes it into .pi/skills/<id>/SKILL.md — OMP/Pi's project-scope directory, scanned natively at session start (so plugin skills reach the live session too). Each materialized dir carries a .rig-plugin marker; re-materialization replaces only plugin-owned skills and never touches operator-authored ones. See skills → ."},{"id":"misc","heading":"repoSources, agentRoles, taskFieldSchemas","text":"repoSources, agentRoles, taskFieldSchemas repoSources: [{ id: \"my-plugin:main-repo\" , url: \"https://github.com/org/repo\" , defaultPath: \"repos/main-repo/\" }] agentRoles: [{ id: \"my-plugin:architect\" , defaultModel: \"team-default\" }] taskFieldSchemas: [{ id: \"my-plugin:browser-field\" , fieldName: \"Browser\" , schemaJson: \"{...}\" }] A Browser: block in an issue body is parsed as JSON and validated against schemaJson by createTaskFieldRegistry , then passed to the agent under the field name. Agent roles resolve role:<id> task labels to models; repo sources can optionally carry managed-mirror fields ( defaultBranch , remoteEnvVar , checkoutEnvVar ) when Rig should sync the repo on the plugin's behalf."},{"id":"schemajson-format","heading":"The schemaJson format // JSON in, presence out","text":"The schemaJson format // JSON in, presence out schemaJson is a JSON string — a JSON-serialized schema-like payload, not an object. Use JSON.stringify(…) when you author it: taskFieldSchemas: [{ id: \"my-plugin:browser-field\" , fieldName: \"Browser\" , schemaJson: JSON.stringify ({ type: \"object\" , properties: { url: { type: \"string\" }, viewport: { type: \"string\" } }, required: true , // top-level boolean — the presence signal Rig reads }), }] The payload is opaque to the engine : Rig does not deep-validate properties or JSON-Schema constraints. It reads exactly one thing — a top-level \"required\": true — as the canonical \"this field must be present on the task\" marker. With that set, a task whose fieldName value is missing, null , or empty fails validateTaskFields with a collected error. Without it, the extension is optional and the schema is yours to interpret however your plugin or agent prompt wants. Note this is a top-level boolean required: true , distinct from JSON Schema's per-property required: [\"url\"] array (which Rig ignores)."},{"id":"tasksources","heading":"taskSources","text":"taskSources taskSources: [{ id: \"my-plugin:linear\" , kind: \"linear\" , description: \"Linear issues adapter\" }] kind is an open string — name yours whatever you like ( \"linear\" , \"jira\" , \"my-org:queue\" ). buildTaskSourceRegistry(config, pluginHost) reads config.taskSource.kind , finds a matching executable factory on the plugin host, and instantiates the source; uniqueness is enforced at resolution. There are no hard-coded kinds : the application graph contributes GitHub Issues, and project plugin extras contribute Linear or any custom source through the same resolver. Source-specific config rides in the taskSource.options bag. The full first-party source shapes, label conventions, and write-back behaviour live on the task sources page →"},{"id":"clicommands","heading":"cliCommands","text":"cliCommands cliCommands: [{ id: \"my-plugin:report\" , command: \"report\" , description: \"Generate the weekly report\" }] Callable as rig plugin run report — or directly as rig report ; plugin-contributed commands join the top-level CLI surface."},{"id":"testing","heading":"Contract testing","text":"Contract testing Don't ship a plugin you haven't run through a contract test. There is no separate testkit — definePlugin is the contract check: it decodes your object against the RigPlugin schema in @rig/contracts and throws on a bad shape (missing/duplicate ids, bad name/version, under-declared effects). Since the executable fns ride on the same object, you call them directly — no session required: // my-plugin.contract.test.ts import { describe, it, expect } from \"bun:test\" ; import myPlugin from \"./src/index\" ; const plugin = myPlugin (); describe ( \"my-plugin contract\" , () => { // 1) construction itself is the metadata contract — definePlugin threw if the shape was bad it ( \"declares namespaced ids\" , () => { const v = plugin.contributes?.validators?.[ 0 ]; expect (v?.id). toMatch (/^my-plugin:/); }); // 2) call the executable validator directly, off the same object it ( \"validator runs and passes\" , async () => { const v = plugin.contributes?.validators?.[ 0 ]; const result = await v!. run ({ taskId: \"t-1\" , workspaceRoot: \"/tmp/x\" , scope: [ \"src/a.ts\" ] }); expect (result.passed). toBe ( true ); }); // 3) exercise a custom task-source factory by kind it ( \"task source reso"},{"id":"working-examples","heading":"Living references in the repo","text":"Living references in the repo The first-party plugins under packages/ are the reference implementations — real, composed, contract-tested code rather than toy snippets: packages/tasks-plugin/ — the single tasks capability: source access, source adapters, and read-only derived state. The canonical taskSources + read-model shape. packages/github-provider-plugin/ — a swappable SCM provider (auth, credentials, Projects, issue analysis) contributed as a capability. Good for seeing provides / requires and capabilityProviders in practice. packages/config-plugin/ — a compact plugin that contributes both a CLI command ( rig config get/set ) and read-model presentation cargo via a panel. A clean template for a command + display contribution. Copy the closest match, rename the <plugin-name>: id prefix everywhere, and fill in your own factory(config) and run(ctx) logic."},{"id":"see-also","heading":"See also","text":"See also Plugin examples — complete validator, task-source, and OMP/Pi extension packages with load and verification steps. Validators & hooks — run-time behaviour of the two channels you contribute most. Task sources — the first-party github-issues / linear adapters and their conventions. Skills — SKILL.md authoring and how plugin skills reach the OMP/Pi session. OMP/Pi extensions — the other half: extend the live OMP session, not the workflow automation Rig runs around it. rig.config.ts — where plugins: [...] and taskSource are declared. Plugins extend the workflow automation Rig runs around a session; OMP/Pi extensions extend the live session. If what you want is a new tool or slash command inside OMP , you want an OMP/Pi extension , not a Rig plugin. The two compose: a Rig plugin declares workflow metadata; an OMP/Pi package acts at session start."}]},{"url":"plugins/examples/index.html","title":"Plugin examples","sections":[{"id":"","heading":"","text":"Plugin examples Three copyable packages. Each one shows the real load path: executable Rig contributions ride in .rig/rig.config.ts ; live OMP/Pi packages are pinned as package data through runtime.pi.packages and materialized into .pi/settings.json ."},{"id":"map","heading":"Sortie map","text":"Sortie map package what it proves where it enters Rig validator plugin A custom check is dispatched by id inside the validation gate. plugins: [scopeGuard()] in .rig/rig.config.ts ; task records name the validator id. task-source plugin A custom taskSource.kind resolves through the same factory path as standard adapters. plugins: [fileQueue()] plus taskSource: { kind, options } . OMP/Pi package A live-session tool is installed by OMP/Pi, not by the Rig plugin registry. runtime.pi.packages in rig.config.ts or .rig/rigfig.toml ."},{"id":"validator-plugin","heading":"Validator plugin // validation gate check","text":"Validator plugin // validation gate check Use this when the merge sortie needs a local rule that is not a subprocess: scope fences, release-note policy, owned-file checks. The validator contributes metadata and an executable run(ctx) on the same object; the lifecycle validator dispatches by check id."},{"id":"validator-files","heading":"Files","text":"Files packages/rig-scope-guard/package.json { \"name\": \"@acme/rig-scope-guard\", \"version\": \"0.1.0\", \"type\": \"module\", \"exports\": { \".\": \"./src/index.ts\" }, \"dependencies\": { \"@rig/core\": \"workspace:*\" }, \"devDependencies\": { \"typescript\": \"^5.8.0\" } } packages/rig-scope-guard/src/index.ts import { definePlugin, type ValidatorContext, type ValidatorResult } from \"@rig/core\"; const VALIDATOR_ID = \"acme-scope-guard:scope-boundary\"; export type ScopeGuardOptions = { forbidden?: readonly string[]; }; function blockedFiles(scope: readonly string[], forbidden: readonly string[]): string[] { return scope.filter((file) => forbidden.some((prefix) => file === prefix || file.startsWith(`${prefix}/`))); } export default function scopeGuardPlugin(options: ScopeGuardOptions = {}) { const forbidden = options.forbidden ?? [\"secrets\", \"infra/prod\"]; return definePlugin({ name: \"acme-scope-guard\", version: \"0.1.0\", contributes: { validators: [ { id: VALIDATOR_ID, category: \"boundary\", description: \"Fails when task scope crosses forbidden directories.\", async run(ctx: ValidatorContext): Promise<ValidatorResult> { const blocked = blockedFiles(ctx.scope, forbidden); if (blocked.length === 0) { return { i"},{"id":"validator-verify","heading":"Verify the load","text":"Verify the load rig plugin list should show acme-scope-guard and validator acme-scope-guard:scope-boundary . rig plugin validate --task GC-417 or the run closeout validator should dispatch the same id. A task scoped to secrets/api.env fails with the validator summary. Unit-test the function directly: construct the plugin, take plugin.contributes.validators[0] , and call run({ taskId, workspaceRoot, scope }) . There is no second runtime channel to mock."},{"id":"validator-seams","heading":"How it works","text":"How it works definePlugin(plugin) decodes metadata and returns the original object, so executable functions remain attached. ValidatorRegistration is { id, category, description? } ; @rig/core extends it with run(ctx) for executable validators. The plugin host lists executable validators, builds the validator registry for the harness, and lifecycle validation dispatches configured check ids through that registry before falling back to subprocess checks. Limit: validators only run when a task or closeout path asks for their id. Registering a validator is not a global policy switch."},{"id":"task-source-plugin","heading":"Task-source plugin // custom kind, same factory path","text":"Task-source plugin // custom kind, same factory path Use this when ground control has a task system Rig does not ship: a JSON queue, an internal tracker, a database-backed board. The important seam is taskSource.kind . Standard and custom adapters both resolve by kind through the plugin host, then receive the full TaskSourceConfig and { projectRoot } ."},{"id":"task-source-files","heading":"Files","text":"Files packages/rig-file-queue-source/package.json { \"name\": \"@acme/rig-file-queue-source\", \"version\": \"0.1.0\", \"type\": \"module\", \"exports\": { \".\": \"./src/index.ts\" }, \"dependencies\": { \"@rig/core\": \"workspace:*\", \"@rig/contracts\": \"workspace:*\" }, \"devDependencies\": { \"typescript\": \"^5.8.0\" } } packages/rig-file-queue-source/src/index.ts import { existsSync, readFileSync } from \"node:fs\"; import { resolve } from \"node:path\"; import { definePlugin, type TaskSourceFactoryContext } from \"@rig/core\"; import type { RegisteredTaskSource, TaskRecord, TaskSourceConfig } from \"@rig/contracts\"; const PLUGIN_NAME = \"acme-file-queue\"; const SOURCE_ID = \"acme-file-queue:source\"; const SOURCE_KIND = \"acme-file-queue\"; type QueueTask = { id: string; title: string; status?: string; body?: string; deps?: readonly string[]; scope?: readonly string[]; validation?: readonly string[]; }; function readQueue(path: string): QueueTask[] { if (!existsSync(path)) return []; const parsed = JSON.parse(readFileSync(path, \"utf8\")) as unknown; if (!Array.isArray(parsed)) throw new Error(`${path} must contain a JSON array`); return parsed.map((entry) => { if (!entry || typeof entry !== \"object\") throw new Error(\"t"},{"id":"task-source-verify","heading":"Verify the load","text":"Verify the load rig plugin list should show task source kind acme-file-queue . rig-agent lookup LOCAL-1 should return the normalized record from .rig/tasks.json . If taskSource.kind is mistyped, task IO fails before the sortie launches: No task source factory registered for kind ... ."},{"id":"task-source-seams","heading":"How it works","text":"How it works TaskSourceConfig reserves built-in adapter fields and gives plugin adapters one extension bag: options . The plugin host rejects duplicate task-source kind values. One kind, one factory. Task IO calls resolveTaskSourceFactoryByKind(config.taskSource.kind) , then invokes factory.factory(config.taskSource, { projectRoot }) . First-party github-issues and plugin kinds use the same route. Limit: this example implements read-only list and get . Add create or updateTask only if your tracker can safely project Rig's write intent back to the source."},{"id":"pi-extension-package","heading":"OMP/Pi package // live-session tool pin","text":"OMP/Pi package // live-session tool pin This is not a Rig plugin. It is an OMP/Pi package: the package manifest declares an extension, Rig pins the package in runtime.pi.packages , and the session asset materializer merges that package spec into .pi/settings.json . Pi installs and loads it when the live session starts."},{"id":"pi-package-files","heading":"Files","text":"Files packages/pi-branch-marker/package.json { \"name\": \"@acme/pi-branch-marker\", \"version\": \"0.1.0\", \"type\": \"module\", \"main\": \"dist/index.js\", \"pi\": { \"extensions\": [\"dist/index.js\"] }, \"scripts\": { \"build\": \"bun build src/index.ts --target bun --outdir dist\" }, \"dependencies\": { \"@oh-my-pi/pi-coding-agent\": \"^16.3.4\", \"@oh-my-pi/pi-tui\": \"^16.3.4\" }, \"devDependencies\": { \"typescript\": \"^5.8.0\" } } packages/pi-branch-marker/src/index.ts import type { ExtensionAPI, ExtensionContext } from \"@oh-my-pi/pi-coding-agent\"; import { Text } from \"@oh-my-pi/pi-tui\"; type BranchMarkerParams = { prefix?: string; }; interface LooseTool { name: string; label: string; description: string; parameters: unknown; approval?: \"read\" | \"write\" | \"exec\"; execute( toolCallId: string, params: BranchMarkerParams, signal: AbortSignal | undefined, onUpdate: unknown, ctx: ExtensionContext, ): Promise<unknown>; renderCall?: (args: unknown) => Text; } export default function branchMarkerExtension(api: ExtensionAPI): void { const z = api.zod; const register = api.registerTool.bind(api) as unknown as (tool: LooseTool) => void; register({ name: \"branch_marker\", label: \"Branch marker\", description: \"Report the curr"},{"id":"pi-package-verify","heading":"Verify the materialization","text":"Verify the materialization Build the package before a run: cd packages/pi-branch-marker && bun run build . Start a Rig Pi session. Rig asks the session asset materializer to run; .pi/settings.json should contain the package spec from runtime.pi.packages . In the live session, call the new tool with { \"prefix\": \"fpv\" } ; it returns fpv: <branch> ."},{"id":"pi-package-seams","heading":"How it works","text":"How it works runtime.pi.packages is part of RigConfig.runtime.pi . It names npm specs, git specs, or local file: packages for Pi to load. The plugin-host context asks the provider-owned session asset materializer to materialize both plugin skills and config Pi packages. Plugin-contributed skills go under .pi/skills/ ; Pi package specs merge into .pi/settings.json . Rig tracks ownership separately in .rig/state/pi-managed-packages.json , not by adding marker fields to Pi settings. The Pi loader reads package manifests from omp or pi and resolves extensions , tools , hooks , and commands from the package root. Limit: runtime.pi.packages pins live-session packages. It does not register a Rig validator, task source, hook, or stage. Put workflow metadata in a definePlugin package and live OMP behavior in a Pi package."},{"id":"receipts","heading":"Receipts","text":"Receipts packages/core/src/define-plugin.ts validates plugin metadata and returns the original object with executable fields intact. packages/core/src/plugin-host.ts rejects duplicate task-source kinds; packages/tasks-plugin/src/io/service.ts resolves the configured kind through the plugin host and calls the factory with { projectRoot } . packages/lifecycle-plugin/src/control-plane/validation/validator.ts dispatches configured check ids to registered executable validators. packages/contracts/src/config.ts defines taskSource.options and runtime.pi.packages . packages/harness-plugin/src/session-asset-materializer-service.ts , skill-materializer.ts , and pi-settings-materializer.ts define the .pi/skills/ and .pi/settings.json handoff. node_modules/@oh-my-pi/pi-coding-agent/src/extensibility/plugins/loader.ts resolves package manifest extension entries for Pi."},{"id":"see-also","heading":"See also","text":"See also Plugin authoring — the contribution contract and single-channel model. Task sources — first-party adapter behavior and conventions. Validators & hooks — validation and hook runtime behavior. Skills — how contributes.skills becomes .pi/skills/ . OMP/Pi extensions — live-session package model."}]},{"url":"docs/pi-extensions/index.html","title":"OMP/Pi extensions","sections":[{"id":"","heading":"","text":"OMP/Pi extensions OMP/Pi extensions are installable live-session behavior: tools, slash commands, subagents, and session integrations. They are community npm packages loaded into the live Pi session — distinct from Rig plugins, which are definePlugin packages in the product plugin graph."},{"id":"install","heading":"Install and pin","text":"Install and pin Pi is @oh-my-pi/pi-coding-agent , the agent harness a run drives. The rig pi command group (owned by @rig/harness-plugin ) manages the extension packages a session loads: $ rig pi search subagents $ rig pi add pi-subagents $ rig pi list rig pi add writes the package into the project's .pi/settings.json . Team-wide packages are declared in runtime.pi.packages — npm specifiers or git sources — which Rig materializes at session start. Foreground OMP and detached drones receive the configured set regardless of where a drone is placed."},{"id":"capabilities","heading":"Session reporting","text":"Session reporting /drone reports the capabilities that exact detached OMP session loaded: tools, prompts, skills, hooks, extension commands, and packages. Foreground-only capabilities are never projected onto a drone, and placement does not change that boundary."},{"id":"plugin-split","heading":"Rig plugin or OMP/Pi extension?","text":"Rig plugin or OMP/Pi extension? Use a Rig plugin (a definePlugin package in the product plugin graph) for validators, task sources, hooks, roles, skills, and workflow metadata — the workflow automation Rig runs around a session. Use an OMP/Pi extension (a community npm package) for behavior inside the live Pi session itself. The two are separate layers that meet only where the session reports what it loaded."}]},{"url":"skills/index.html","title":"Skills","sections":[{"id":"","heading":"","text":"Skills A skill is standing prompt context for an OMP/Pi session — a SKILL.md file, YAML frontmatter plus a markdown body, that a plugin ships and Rig materializes verbatim into .pi/skills/ where OMP/Pi reads it."},{"id":"what-a-skill-is","heading":"What a skill is // reusable prompt context","text":"What a skill is // reusable prompt context An OMP/Pi session starts from a base prompt plus whatever context the work carries. A skill injects standing context on top of that — a procedure, a checklist, a house rule — so you brief the session once instead of pasting it into every issue. The format is deliberately boring: a markdown file with a small YAML header. No build step, no runtime, no code path. The frontmatter names the skill; the body is the prompt. Skills reach OMP/Pi by being copied into the directory it scans. Rig's job is to take the SKILL.md files a plugin declares, validate them, and place them where OMP/Pi looks. Discovery is native from there. A skill is content, not behavior. If you need to add a tool, a slash command, or a hook , that's a Pi extension or a hook , not a skill. See skills vs. extensions vs. hooks below for the dividing line."},{"id":"skill-md-format","heading":"The SKILL.md format // frontmatter + body","text":"The SKILL.md format // frontmatter + body One file. The frontmatter block is delimited by --- lines; everything after the closing delimiter is the body. Here is a real, non-trivial skill — a boundary-analysis procedure an OMP/Pi session can follow when asked to touch module edges: --- name: boundary-analysis description: How to reason about module boundaries before editing across them owner: platform-team priority: high --- # Boundary analysis Before editing code that crosses a package or module boundary, do this: 1. Identify the boundary. Name the two sides and the contract between them (the exported types, the function signatures, the event shapes). 2. Decide which side owns the change. A change is "upstream" if it alters the contract, "downstream" if it only consumes it. Prefer downstream. 3. If the change is upstream, list every downstream consumer first. Do not edit the contract until you have the full blast radius written down. 4. Keep the diff on one side of the boundary per commit where possible. A commit that edits both the producer and three consumers is hard to review and hard to revert. When in doubt, widen the type on the producer and narrow on the "},{"id":"frontmatter-fields","heading":"Frontmatter fields","text":"Frontmatter fields The loader parses the header line-by-line into key/value pairs. name is the only required field. Everything else is optional and free-form. field required meaning name yes The skill's human name. Must be a string, or the skill is skipped with a warning at materialization. description no One-line summary. Carried on the plugin's skill registration, not required in the frontmatter. any other key no Arbitrary extra keys are tolerated (e.g. owner , priority ). The parser coerces true / false , integers, and decimals and unquotes quoted strings while validating, then copies the file through unchanged. The frontmatter parser is intentionally small — it is line-based, not a full YAML engine. Keep each field on its own key: value line. Lines starting with # and blank lines inside the header are ignored. Nested YAML structures are not parsed. Keep the header flat."},{"id":"contributing-a-skill","heading":"How a plugin contributes a skill // the registration","text":"How a plugin contributes a skill // the registration A plugin-contributed skill reaches OMP/Pi through explicit Rig materialization — nothing is scanned from arbitrary packages. A plugin declares each one in its contributes.skills array with a namespaced id and a path to the SKILL.md on disk: contributes: { skills: [{ id: \"my-plugin:boundary-analysis\" , path: new URL ( \"skills/boundary-analysis/SKILL.md\" , import.meta.url).pathname, description: \"Boundary analysis prompt\" , }] } The id follows the standard <plugin-name>:<local-id> namespacing convention and must be unique across loaded plugins. For the path : use an absolute path (via import.meta.url ) for npm-installed plugins, since the package can be unpacked anywhere; a relative path is resolved against the project root. The full plugin shape lives in plugin authoring → ."},{"id":"the-loader","heading":"Validation // what the materializer checks","text":"Validation // what the materializer checks The skill materializer inside Rig's provider plugin turns each declared SKILL.md into a materialized directory. Before it copies a file it does one check: split the frontmatter from the body, parse the header, and enforce that a string name exists. If that passes, the whole file is copied through byte for byte — frontmatter and body together, exactly as the author wrote it."},{"id":"loader-behavior","heading":"What validation guarantees","text":"What validation guarantees name is mandatory. If the parsed frontmatter has no string name , the materializer throws SKILL.md missing required field \"name\" for that file, skips it with a warning, and keeps going. A file with no frontmatter at all fails the same way — no header means no name . the file is copied verbatim. The materialized SKILL.md is the source file unchanged — frontmatter, body, whitespace and all — so the session sees exactly what the author wrote. the header parser is lenient. Extra keys beyond name and description are tolerated, not rejected. The parser is line-based, not a full YAML engine. scalars are coerced while parsing. true / false become booleans; integer and decimal strings become numbers; single- or double-quoted strings are unquoted — this feeds the name check, and does not alter the copied file. This is the same validation Rig runs on every boot. There is no second, hidden validator."},{"id":"materialization","heading":"Materialization // placing skills where OMP/Pi looks","text":"Materialization // placing skills where OMP/Pi looks This is where the skill becomes a file the OMP/Pi session can read. During explicit Rig materialization, the plugin host flattens every configured plugin's contributes.skills into one list and writes it into the workspace. Each skill becomes a directory under .pi/skills/ holding the verbatim SKILL.md plus a marker file. The directory name is the skill id with every run of non-directory-safe characters replaced by - — so my-plugin:boundary-analysis lands at .pi/skills/my-plugin-boundary-analysis/SKILL.md . // project root .pi/ skills/ my-plugin-boundary-analysis/ // <sanitized-id> SKILL.md // copied verbatim from the plugin .rig-plugin // { \"plugin\": \"my-plugin\", \"skillId\": \"my-plugin:boundary-analysis\" } This is the same directory OMP/Pi scans natively. OMP/Pi looks in <cwd>/.pi/skills/ (project scope) and ~/.pi/agent/skills/ (user scope). Rig only ever writes the project-scope copy; the user-scope directory is yours. Correction worth stating plainly: the project-scope skill directory is .pi/skills/ — not .agents/skills/ . The materialized location and OMP/Pi's scan path are the same .pi/skills/ tree. Current boot path: Rig resol"},{"id":"idempotency-and-the-marker","heading":"Idempotency & the .rig-plugin marker","text":"Idempotency & the .rig-plugin marker Materialization is idempotent and safe to run on every boot. Before writing the current set, Rig scans .pi/skills/ and deletes only directories that carry a .rig-plugin marker, then re-inserts the current plugin skills. The marker is a small JSON file recording the owning plugin and the skill id: { \"plugin\" : \"my-plugin\" , \"skillId\" : \"my-plugin:boundary-analysis\" } Because the delete is gated on the marker, the cycle converges: a plugin you removed from rig.config.ts loses its materialized directory on the next boot, and a plugin you kept gets exactly one fresh copy."},{"id":"operator-sovereignty","heading":"Operator sovereignty","text":"Operator sovereignty Any skill directory without a .rig-plugin marker is treated as operator-authored and is never deleted, never overwritten. You can drop your own SKILL.md into .pi/skills/ by hand and it will sit alongside plugin skills untouched across every re-materialization. Plugin-owned is replaceable; operator-owned is sovereign. The same discipline applies to hooks. Plugin hooks are written into the active harness settings file with their own plugin marker; operator-authored hook entries are preserved on re-materialization. One rule across skills and hooks: marked = managed by Rig, unmarked = yours. See validators & hooks → ."},{"id":"boot-safety","heading":"Boot-safety — a bad skill never blocks a run","text":"Boot-safety — a bad skill never blocks a run Materialization is non-fatal by design. If a declared SKILL.md is missing on disk, or fails frontmatter validation (no string name ), Rig skips that one skill with a warning and continues — it does not abort the OMP/Pi session boot. The validated file is then copied verbatim, so the session sees exactly what the author wrote, byte for byte."},{"id":"reproducibility","heading":"Reproducibility, honestly // what \"pinned\" does and doesn't mean","text":"Reproducibility, honestly // what \"pinned\" does and doesn't mean Skills are reproducible in the plain sense: the SKILL.md is a file checked into the plugin (or the project), and materialization copies it verbatim. Two operators on the same plugin version get the same skill body. That is the whole story. There is no separate version-lock or pin mechanism for an individual skill beyond the plugin's own version. Reproducibility comes from the plugin being pinned in rig.config.ts and the file being copied unchanged — not from any per-skill pin. If you want a skill frozen, freeze the plugin version that ships it."},{"id":"loading-a-skill-in-tests","heading":"Testing a skill","text":"Testing a skill Verify a skill the same way Rig does — materialize it into a scratch project root and assert the file landed. A valid skill writes .pi/skills/<sanitized-id>/SKILL.md and is reported in the returned list; an invalid one is skipped and the list comes back empty: // materialize into a temp root, then assert on the result const written = await materializeSkills (root, [ { pluginName: \"my-plugin\" , skill: { id: \"my-plugin:boundary-analysis\" , path: \"SKILL.md\" } }, ]); expect (written). toHaveLength ( 1 ); expect (existsSync(resolve(root, \".pi\" , \"skills\" , \"my-plugin-boundary-analysis\" , \"SKILL.md\" ))). toBe ( true );"},{"id":"session-skills-in-the-palette","heading":"Session skills in the native palette // what the Rig session reports","text":"Session skills in the native palette // what the Rig session reports Bare rig and every detached OMP drone report only the skills their own runtime loaded. Foreground-only capabilities are not projected as drone capabilities, regardless of local or remote placement. Fleet work materializes through inline /fleet , /tasks , and /drone cards; skills remain prompt context. Full live UX in OMP/Pi extensions → ."},{"id":"skills-vs-extensions-vs-hooks","heading":"Skills vs. extensions vs. hooks // pick the right tool","text":"Skills vs. extensions vs. hooks // pick the right tool Three mechanisms extend what an OMP/Pi-backed workflow can do. They are not interchangeable: mechanism what it adds where it lives skill Standing prompt context — procedures, checklists, house rules. Content only. .pi/skills/<sanitized-id>/SKILL.md OMP/Pi extension Installable package: tools, slash commands, behaviors. Code. .pi/settings.json packages , auto-installed at session start hook A guard that fires on a lifecycle event (e.g. block edits outside scope). Active harness settings file, with a plugin marker OMP/Pi extensions are discovered and added with rig pi search / rig pi add , or declared team-wide via runtime.pi.packages in rig.config.ts — Rig materializes the list into .pi/settings.json and OMP/Pi auto-installs at session start on every host. Reach for a skill when you need to tell the session something; reach for an extension when you need to give it a new capability; reach for a hook when you need to stop it doing something."},{"id":"see-also","heading":"See also","text":"See also Plugin authoring — the full contributes shape and how to ship a skill in a package. Plugin examples: OMP/Pi package — a small live-session tool pinned through runtime.pi.packages and materialized beside skills. OMP/Pi extensions — installable tools and slash commands, and the session/collab UX. Validators & hooks — the other side of the materialization discipline. rig.config.ts — where plugins and runtime.pi.packages are pinned."}]},{"url":"docs/task-sources/index.html","title":"Task sources","sections":[{"id":"","heading":"","text":"Task sources Where the work comes from. A task source turns whatever your team already tracks — GitHub Issues, Linear, your own kind — into TaskRecord s the swarm can dispatch. Task state is derived from the source on read ; there is no local task database. Config picks one source by kind ; the application graph and project extras contribute the factories."},{"id":"model","heading":"The dispatch model","text":"The dispatch model One source per rig. At boot, Rig resolves config.taskSource.kind against the executable factories contributed by loaded plugins and instantiates exactly one. kind is an open string in the contract — \"github-issues\" and \"linear\" ship first-party (the tasks capability is owned by @rig/tasks-plugin , the GitHub source by @rig/github-provider-plugin , and Linear by @rig/linear-plugin ), and any plugin can contribute a \"jira\" , \"asana\" , or your own on equal footing. If no loaded plugin contributes the configured kind, dispatch fails loudly ( common-symptoms checks → ). import { defineConfig } from \"@rig/core\" ; export default defineConfig ({ taskSource: { kind: \"github-issues\" , owner: \"acme\" , repo: \"api\" }, }); GitHub Issues is part of the application-owned graph. Other sources, including Linear, are ordinary project plugin extras selected by kind . Whatever the backend, every source produces the same TaskRecord shape — id, status, deps, scope, validators, role — so the rest of the engine never knows where a task came from."},{"id":"task-record","heading":"The TaskRecord contract // what a source must produce","text":"The TaskRecord contract // what a source must produce A task source has one job: turn whatever your team tracks into TaskRecord s. The contract ( @rig/contracts , task-source.ts ) is deliberately small — three required fields and an open bag for everything else. The engine reads the three it knows about; the rest passes through onto the task for plugins, the operator surfaces, and your own validators to use. field type required meaning id string yes Stable task identifier. github-issues uses the issue number; Linear uses the issue identifier. deps string[] yes Ids this task is blocked on. The queue won't schedule a task until its deps clear. Defaults to [] when a source omits it. status string yes Open string, but the lifecycle understands ready , in_progress , blocked , open , closed and the rest of the task status set → . [extra] unknown no Anything else — title , body , scope , role , validators , url , sourceIssueId , your own fields — rides along untouched. The structured fields the standard sources populate from labels and body markers: field derived from scope Every scope:<glob> label — where the worker is expected to operate. role The first role:<id> label — resolved to a m"},{"id":"linear","heading":"The Linear source","text":"The Linear source Kind \"linear\" ( @rig/linear-plugin ): issues on a Linear team become tasks over the Linear API. options.teamKey is required — the factory throws task source linear: options.teamKey is required otherwise. The same scope: / validator: / role: label conventions the GitHub source uses apply here too, so the same task file can move between trackers unchanged. option semantics options.teamKey Required — the Linear team key, e.g. \"ENG\" . options.apiKey Linear API key; defaults to LINEAR_API_KEY resolved at call time. options.states Filter: workflow-state names, e.g. [\"Todo\", \"In Progress\"] . options.assignee Filter by assignee — email (anything containing @ ) or display name. options.labels Filter: only issues carrying the listed labels are tasks. options.listLimit Max issues. At the limit Rig fails loudly (\"refusing to silently truncate matching issues\") rather than dropping tasks — narrow states / labels / assignee or raise the cap. Status is derived from the issue's workflow-state type — Linear's state taxonomy has no blocked/failed concept, so those Rig statuses are surfaced via comments instead of mangling the workflow state."},{"id":"github-issues","heading":"The github-issues source","text":"The github-issues source Kind \"github-issues\" : issues in owner / repo (both required) become tasks, via the gh CLI. Pull requests are filtered out of issue lists. The same scope: / validator: / role: label prefixes apply, plus a few issue-native conventions: a depends-on: #12, #14 line in the body becomes deps , a parents: #3 line becomes parent links, type: labels (or the epic label) set the issue type, and the issue number is the task id. config semantics owner , repo Required — the factory throws task source github-issues: owner is required otherwise. labels Filter: only issues carrying all listed labels are tasks. state \"open\" (default), \"closed\" , or \"all\" . options.assignee Filter by assignee; falls back to RIG_GITHUB_ASSIGNEE . options.timeoutMs Timeout for every gh call. Default 15 000. options.listLimit Max issue-list rows, default 1 000. At the limit Rig fails loudly (\"refusing to silently truncate matching issues\") instead of dropping tasks. Task status reads from labels — ready , blocked , in-progress , under-review , failed , cancelled — and a closed issue is status closed regardless of labels."},{"id":"state-all","heading":"What state: \"all\" does // re-including done work","text":"What state: \"all\" does // re-including done work The state field maps straight to gh issue list --state . The default \"open\" hides anything closed. Set \"all\" and the list re-includes closed issues — including the ones Rig already finished and closed with the rig:done label. Nothing dedupes or filters them: a closed issue is mapped to status closed by statusFor() before labels are even consulted, so a re-included rig:done issue arrives as a closed task, not as ready work. The queue treats closed as terminal, so it won't re-dispatch them — but they will show up in rig task list . Use \"all\" when you want completed history in the graph (reporting, dependency resolution against finished work); keep \"open\" for a clean active queue. Auth. The application-owned GitHub provider supplies both credential paths. createStateGitHubCredentialProvider reads the signed-in token Rig stores ( github-auth.json under RIG_STATE_DIR , or an explicit RIG_GITHUB_AUTH_STATE_FILE ). createEnvGitHubCredentialProvider reads RIG_GITHUB_SELECTED_TOKEN for normal repo operations — public repos work with no token at all, and ambient host GH_TOKEN / GITHUB_TOKEN are deliberately cleared for selected-repo calls so h"},{"id":"writeback","heading":"Explicit source-field writeback","text":"Explicit source-field writeback Task sources aren't read-only. As a run progresses and completes (or fails), the server posts projected source fields and comments back through the source's updateTask method. For GitHub issues that means: Status labels swap atomically — the managed plain set ( ready … cancelled ) plus the rig: lifecycle set ( rig:running → rig:pr-open → rig:ci-fixing → rig:merging → rig:done , or rig:needs-attention ). Missing labels are created on the fly. One sticky status comment , found by its rig:status-comment marker and updated in place — no comment spam. A Rig-owned metadata block between markers in the issue body, updated without touching human-authored content. Issue closed when the task reaches closed . The mapping from task status to the two GitHub label sets is fixed — each status resolves to at most one plain status label and at most one rig: lifecycle label: task status plain label rig: label issue action in_progress in-progress rig:running — under_review under-review rig:pr-open — ci_fixing under-review rig:ci-fixing — merging under-review rig:merging — failed / blocked / needs_attention blocked rig:needs-attention — ready ready (none) — cancelled ca"},{"id":"custom","heading":"Custom kinds","text":"Custom kinds Bring your own backend. A task source is a plugin contribution with two halves on a single entry: declarative metadata (what schemas can represent) and an executable factory that builds the source. The factory receives the full TaskSourceConfig — pull plugin-specific fields from the options bag — and the first-party Linear plugin follows exactly this shape: import { definePlugin } from \"@rig/core\" ; export default function jiraPlugin () { return definePlugin ({ name: \"rig-jira\" , version: \"0.1.0\" , // single-channel: metadata AND the executable factory on one entry contributes: { taskSources: [{ id: \"jira:issues\" , kind: \"jira\" , description: \"Jira issues\" , factory (config) { // plugin-specific fields ride in config.options; top-level fields are reserved const project = config.options?.project; if (!project) throw new Error ( \"task source jira: options.project is required\" ); return createJiraSource ({ project }); } }] }, }); } The application's sources don't know about \"jira\" ; this plugin's factory does, and because kind is an open string the engine dispatches it with no special-casing — add it as a project plugin and set taskSource: { kind: \"jira\", options: { proje"},{"id":"see-also","heading":"See also","text":"See also Plugin authoring — the full contribution surface and how definePlugin 's declarative + runtime channels fit together. Entities & data model — where a TaskRecord lands in the task graph, and the full task-status set. rig.config.ts — the taskSource block, the options bag, and every adjacent config field. Environment variables — the GitHub token and assignee variables the credential providers read. Validators & hooks — where the validator: labels resolve to the gates a run must pass."}]},{"url":"docs/validators/index.html","title":"Validators & hooks","sections":[{"id":"","heading":"","text":"Validators & hooks The drone says \"done.\" Validators are the deterministic gate that decides whether it's telling the truth; hooks are shell commands that watch every move it makes mid-session. Two enforcement channels, both plugin-contributed, neither one the drone can argue with."},{"id":"two-channels","heading":"Two channels, two moments // gate vs intercept","text":"Two channels, two moments // gate vs intercept Rig pins down the work at two moments, with two mechanisms. Validators judge the finished worktree — they run once the agent says \"done\", at the Validate stage , and their pass/fail decides whether the work advances toward a PR. Hooks intercept the work as it happens — the harness fires them on session events (a tool call, a prompt, the Stop signal) and a blocking hook can stop a write before it lands. The agent cannot argue with either: a validator only passes when the check is true, and a hook either exits clean or feeds its message straight back into the session. // where each channel sits relative to the run agent works in worktree PreToolUse / PostToolUse / UserPromptSubmit hooks fire here ← intercept ↓ agent declares ready Stop hook fires here ↓ VALIDATE stage — server runs the task's validators ← gate all pass → Commit → Open PR → the merge gate any fail → steer agent with the summary, retry (up to the attempt cap) Validators gate; hooks intercept. A validator decides \"is the finished work acceptable?\" A hook decides \"is this single action allowed right now?\" Use a validator for end-state truth, a hook for in-flight guardrails."},{"id":"model","heading":"What a validator is","text":"What a validator is A deterministic check the server runs against the drone's worktree at the Validate stage of the lifecycle — after the drone says \"done\", before any commit reaches a PR. The drone cannot talk a validator into passing; it can only make the check true. Every registration carries a category: category checks boundary Safety and drift — did the change stay where it belongs? contract Shape and ownership — do the artifacts have the structure policy demands? integration Cross-repo, CI/CD, and operator-workstation wiring. regression Things that were true and must stay true. external Checks against systems outside the worktree. custom Yours."},{"id":"contract","heading":"The contract","text":"The contract A plugin contributes a validator in two halves. The declarative half is the registration — { id, category, description? } . The executable half adds a run method: import { definePlugin } from \"@rig/core\" ; export default function myPlugin () { return definePlugin ( { name: \"my-plugin\" , version: \"0.1.0\" , contributes: { validators: [{ id: \"my:no-empty-changes\" , category: \"boundary\" }] } }, { validators: [{ id: \"my:no-empty-changes\" , category: \"boundary\" , async run (ctx) { // ctx.workspaceRoot is the run's worktree; ctx.scope the task's scope: labels return { id: \"my:no-empty-changes\" , passed: true , summary: `inspected ${ctx.scope.length} scope entries for ${ctx.taskId}` }; } }] }, ); } run(ctx) resolves to a ValidatorResult : { id, passed, summary, details? } . The summary is what the agent is steered with on failure — make it actionable, because it shows up verbatim in agent output and release gates. The context: ValidatorContext field meaning taskId The task under validation. workspaceRoot The isolated worktree the run produced — check this, not the main tree. scope The task's scope: entries (readonly string array). monorepoRoot? The enclosing monorepo root, whe"},{"id":"dispatch","heading":"How a validator runs // in-process, or a compiled binary","text":"How a validator runs // in-process, or a compiled binary Validation is check-ID only . Every entry in a task's validators / validation list must match category:check-name (for example boundary:no-empty-changes ); anything else is rejected as an invalid entry. For each id the server dispatches in two phases: phase what happens in-process If a plugin registered a validator for the id, the server builds one ValidatorContext for the task and calls .run(ctx) directly — no subprocess. binary fallback If nothing is registered for the id, the server runs a compiled validator binary, reads its ValidatorOutput JSON from stdout, and maps its exit code. Both paths return the identical ValidatorOutput shape ( { id, passed, summary, details? } ), so the caller sees no difference. A script-style binary just prints that JSON and exits — 0 pass, 1 fail, 2 error — and reads the worktree from RIG_TASK_WORKSPACE : // validators/no-empty-changes — a script-style validator binary const root = process.env. RIG_TASK_WORKSPACE ?? process.cwd(); const changed = gitDiffNames (root); // your own check const passed = changed.length > 0 ; console . log ( JSON . stringify ({ id: \"boundary:no-empty-changes\" , pas"},{"id":"selecting","heading":"How a task selects validators, and the retry loop","text":"How a task selects validators, and the retry loop A task picks its validators by id. validator: labels on the task source — a validator:boundary:no-empty-changes label on a GitHub issue or a Linear issue — populate the task's validators / validation fields. When validation fails, the server doesn't end the run — it steers the drone with the validator's actionable output and sends it back in, up to automation.maxValidationAttempts (default 30). Only exhausting the cap fails the run."},{"id":"hooks","heading":"Hooks","text":"Hooks Hooks are the other enforcement channel: plugin registrations fired by the worker harness on session events, declared as { id, event, matcher, command?, description? } . A plugin implements a hook either as a raw shell command (resolved as-is by the harness's hook runner) or as a typed function registered via definePlugin(meta, { hooks: { [id]: impl } }) that returns { decision: \"allow\" | \"block\", reason?, systemMessage? } . Either way, where validators judge the finished worktree, hooks intercept the work as it happens — a PreToolUse hook can block a write before it lands. field values event PreToolUse , PostToolUse , UserPromptSubmit , Stop , SessionStart , SessionEnd . matcher { kind: \"all\" } · { kind: \"tool\", name } · { kind: \"glob\", pattern } . command A shell command, resolved as-is by the harness's hook runner. Prefer absolute paths or the project-root environment variable exposed by the active harness. hooks: [{ id: \"my:stop-banner\" , event: \"Stop\" , matcher: { kind: \"all\" }, command: \"echo '[my-plugin] task complete'\" , }] Two hook systems, one word. These registrations are harness hooks. The extension events an OMP/Pi package handles inside the live se"},{"id":"hook-exit-codes","heading":"The exit-code contract","text":"The exit-code contract A hook command is just a process; what the harness does next is decided by how that process exits. The convention is simple — exit 0 means \"allow\" , and a non-zero exit means \"block\" , with the command's stdout fed back into the session as the reason. Rig's policy-guard hooks lean on exactly this: @rig/core/hook-protocol 's block() prints a BLOCKED: line and calls process.exit(1) . A hook that finds nothing to object to simply returns and exits 0. exit meaning from 0 Allow — the action proceeds; any stdout is informational. clean return, or no matched rule 1 Block — the stdout message (a BLOCKED: line) is surfaced to the agent and the action is rejected. block(name, msg, root) This is the hook channel's exit contract, and it is distinct from a script-style validator binary, where the process uses 0 / 1 / 2 (pass / fail / error) and the server reads the ValidatorOutput JSON on stdout rather than treating non-zero as \"block this action.\" Hooks gate one action; validators report on the whole worktree."},{"id":"project-root-commands","heading":"Project-root commands // portable script paths","text":"Project-root commands // portable script paths Hook command strings are resolved as-is by the harness's hook runner — there is no implicit working directory you can rely on. Point commands at an absolute path or at a project-root variable supplied by the active harness; never use a bare relative path. hooks: [{ id: \"my:stop-check\" , event: \"Stop\" , matcher: { kind: \"all\" }, command: \"/repo/scripts/my-stop-check.sh\" , }]"},{"id":"hook-trips-log","heading":"hook_trips.log // the escalation ledger","text":"hook_trips.log // the escalation ledger Every call to block() appends a line — <hookName> <ISO-timestamp> — to hook_trips.log in the run's state directory ( .rig/state/hook_trips.log ; the dir is the baked state dir if one was compiled in, else RIG_TASK_WORKSPACE 's .rig/state , else the project root's). Its purpose is twofold: it is an audit trail of every guardrail the agent hit, and it drives escalation — on the 3rd trip of the same hook, block() appends a sharper message (\"REPEATED VIOLATION\", re-read the full project instructions, and record the stuck approach in the run's failure log). A drone that keeps slamming…"},{"id":"scope-guard-example","heading":"A worked scope-guard hook","text":"A worked scope-guard hook The first-party scope-guard is the canonical pattern: a PreToolUse hook that reads the tool call, pulls the file paths out of it, and blocks anything that strays outside the task's scope: globs — before the edit ever touches disk. Stripped to its spine, using only hook-protocol helpers: // scope-guard hook — PreToolUse, matcher { kind: \"all\" } import { readHookInput, block, resolveProjectRoot, resolveTaskIdForHook, extractToolFilePaths, resolveTaskScopes, } from \"@rig/core/hook-protocol\" ; const projectRoot = resolveProjectRoot (); const parsed = await readHookInput (); if (!parsed.valid && parsed.hadPayload) block ( \"scope-guard\" , \"Failed to parse hook input. Blocking for safety.\" , projectRoot); const taskId = resolveTaskIdForHook (projectRoot); const scopes = await resolveTaskScopes (projectRoot, taskId); if (scopes.length === 0 ) process.exit( 0 ); // no scope → nothing to guard const paths = extractToolFilePaths (parsed.input.tool_name ?? \"\" , parsed.input.tool_input ?? {}); for ( const p of paths) { if (! inScope (p, scopes)) block ( \"scope-guard\" , `${p} is outside this task's scope.` , projectRoot); } // fell through → exit 0, the tool call procee"},{"id":"hook-kit","heading":"@rig/core/hook-protocol","text":"@rig/core/hook-protocol A deliberately thin toolbox for writing hook commands as standalone scripts or compiled binaries. What it actually contains: readHookInput() — parses the JSON hook payload ( tool_name , tool_input , command ) from stdin or RIG_HOOK_INPUT_FILE , with parse-success flags. block(hookName, message, projectRoot) — the guard primitive: prints a BLOCKED: <message> line to stdout (so the agent sees it immediately), appends a trip record to hook_trips.log in the resolved state dir, escalates the message after 3 trips of the same hook, then exits 1. Context resolution — resolveProjectRoot , resolveTaskIdForHook , resolveTaskConfig , resolveTaskScopes , resolvePolicyContent , backed by BAKED_* constants: build-time config injected into compiled hook binaries, with an env-var fallback ( RIG_BUILD_CONFIG_JSON ) for uncompiled runs. A hook always knows whose project and task it's guarding. Tool-payload utilities — extractToolFilePaths pulls file paths out of a tool call, isTestFilePath classifies them. Together with block , that's a scope-guard or no-test-tampering hook in a few lines. resolveBunCli / resolveBunCliInvocation and escapeRegExp — runtime plumbing. That is th"},{"id":"see-also","heading":"See also","text":"See also Plugin authoring — how the declarative registration and the executable run / command halves are wired by the plugin host. The run lifecycle — the Validate stage these validators gate, and where it sits among the lifecycle stages. rig.config.ts — automation.maxValidationAttempts (the retry cap) and the rest of the run-policy knobs. Skills — the other plugin-contributed channel materialized into the OMP/Pi session."}]},{"url":"cli/index.html","title":"CLI reference","sections":[{"id":"","heading":"","text":"CLI reference Rig has two first-class faces over one set of CNet and domain operations. Interactively, bare rig opens foreground OMP with the bundled Rig extension; work streams into the cnet and /fleet , /tasks , and /drone materialize focused cards inline. Headlessly, the exact rig command tree runs those same operations non-interactively for scripts, CI, and agents. The interactive session: rig opens foreground OMP with Rig. The Swarm Commander is a model role in that chat; /fleet , /tasks , and /drone [ref] write CNet cards into the transcript, while inline attention items carry pending operator actions. rig doctor stays a terminal diagnostic command."},{"id":"global-options","heading":"Global options // launcher and headless CLI flags","text":"Global options // launcher and headless CLI flags These flags apply to the launcher and to the exact CLI command groups that run the same domain operations headlessly. flag what it does --workspace <path> Open the Rig session for an explicit workspace root instead of auto-discovering from the cwd. Useful when operating a project without cd-ing into it. --json Structured machine output: subcommands that support it emit a structured record instead of a rendered human view, for scripts, CI, and agents. --dry-run Deterministic plan where supported: prints the plan a subcommand would execute without mutating state. --version Print the installed Rig CLI version."},{"id":"start-here","heading":"Start here // session first","text":"Start here // session first command / action what it does rig Open the Rig session for the discovered workspace. The day-to-day entry point. rig --workspace <path> Open the same session against an explicit workspace root instead of the current dir. rig attach <run-id> [--web] Attach a terminal or browser to one exact live detached drone. Rig resolves its private credential internally. /fleet Inside the session, materialize the current fleet as inline CNet cards. /tasks Inside the session, materialize configured tasks with their create, rename, and close actions inline. /drone [ref] Inside the session, materialize one exact run and its controls inline. With no ref, it targets the newest projected run. rig doctor Diagnose sandbox backend and collaboration seams from the terminal. rig --version Print the installed Rig CLI version. Attach boundary: the relay carries the detached run session, but raw room credentials are transport data, not public UX. Operators select a run id; foreground Rig and the Swarm Commander role are not attach targets."},{"id":"session-surfaces","heading":"Session surfaces // one transcript, inline CNet cards","text":"Session surfaces // one transcript, inline CNet cards The Rig session is one chat-first surface. CNet maintains a bounded current projection, human-needed work appears as inline attention items, and the Swarm Commander model role can query or act through generated discoverable tools. Query aliases and exact receipts append to this same transcript. surface command what it shows CNet automatic One current fleet aggregate plus active or attention-requiring run items, with explicit query snapshots and receipts in OMP history. attention inline items Runs, approvals, or questions that currently need operator input, each with actions from its owning plugin. Swarm Commander chat Ask for state or an operation; the role receives bounded current context and discovers generated query/action tools. fleet /fleet One aggregate fleet card with counts, bounded rows, and a fleet-owned dispatch action. tasks /tasks One bounded task summary with task-owned create, rename, and close actions; exact dispatch is the /fleet action or rig run dispatch <taskId> . drone /drone [ref] One run's status, placement, progress, recent timeline, controls, and exact-run attach actions inline. doctor rig doctor Termina"},{"id":"headless-cli","heading":"Headless CLI // scripts, CI, and agents","text":"Headless CLI // scripts, CI, and agents The exact CLI command families are a first-class headless adapter over the same CNet and domain operations the session surfaces interactively — reach for them from scripts, CI, release checks, and agents, and add --json for structured output. command surface what it does rig task headless Read the configured task source non-interactively — the same task facts /tasks renders in the session. rig run start / start-serial / start-parallel headless Batch run starters for scripted dispatch; the interactive path is the /fleet action or rig run dispatch <taskId> . rig inbox / rig stats / rig inspect headless Inspect run and fleet state non-interactively; the same state lands as inline CNet items and /drone in the session. rig doctor diagnostic Terminal setup diagnostics for sandbox backend and collaboration seams. rig server task-run / rig server notify-test headless Server-owned task execution and event-notification checks for automation and CI. rig plugin headless Plugin introspection over the composed plugin graph."},{"id":"configuration","heading":"Configuration // what the session reads","text":"Configuration // what the session reads The declarative .rig/rigfig.toml (or the power-path .rig/rig.config.ts ) declares project identity, loaded plugins/packages, the task source, workspace checkout + sandbox, runtime defaults, review policy, merge policy, and GitHub sync. It does not store OMP collab keys, relay credentials, or live session state. $ rig --workspace . $ rig attach 8a31e7 $ rig attach 8a31e7 --web $ rig doctor $ rig --version"},{"id":"advanced-groups","heading":"More CLI groups // the full headless surface","text":"More CLI groups // the full headless surface Interactive UX: rig , rig --workspace <path> , rig attach <run-id> [--web] , /fleet , /tasks , /drone , rig doctor , and rig --version . Every group below is a first-class headless entry point to the same operations. Run rig help --advanced for the live listing and rig <group> --help for per-group help. group summary rig pi <list|add|remove|search> Manage Pi extension packages for this project (community extensions from npm/git). rig profile Runtime profile/model defaults. rig agent Runtime agent workspace helpers. rig setup Setup bootstrap/check helpers for automation and CI. rig test <unit|e2e|all> Project test wrappers. rig review <show|set> Inspect or change the completion review gate policy. More top-level groups round out the headless surface: pipeline , graph , status , summary , blockers , doctor , plan , repo , github , triage , init , config , plugin , server , run , inbox , stats , inspect , task , and drift . Each runs the same domain operations non-interactively; add --json for structured output and --dry-run for a plan where supported. Interactively, the same work flows through the Rig session, /fleet , /tasks , /drone , an"},{"id":"boot-banner","heading":"The launcher","text":"The launcher On an interactive TTY, rig resolves the workspace, starts foreground OMP, loads the bundled Rig extension, and drops you into the CNet chat with the first fleet projection warming. Detached drone sessions start only when work is dispatched."},{"id":"see-also","heading":"See also","text":"See also Getting started — install Rig, open the Rig session, and dispatch your first run. Operator guide — driving CNet, attention items, the Swarm Commander role, inline queries, and exact-run attach. config — project identity, task source, package list, runtime, gate, and merge policy. Environment variables — runtime-only knobs outside config."}]},{"url":"config/index.html","title":"rig.config.ts","sections":[{"id":"","heading":"","text":"rig.config.ts Rig configures a project two ways. The happy path is declarative .rig/rigfig.toml plain data. The power path is .rig/rig.config.ts TypeScript through defineConfig for project-specific plugin extras. Both resolve to the same RigConfig ; the Rig application supplies one first-party graph in either mode."},{"id":"where-it-lives","heading":"Where it lives // discovery & validation","text":"Where it lives // discovery & validation Config lives under .rig/ in the project root — the directory bare rig opens, or the one you hand it with rig --workspace <path> . The schema is RigConfig from @rig/contracts ; defineConfig validates it at load time and throws a descriptive message on an invalid field. rig init scaffolds a rigfig.toml for you. Two forms are accepted: .rig/rigfig.toml — declarative project data consumed with the application-owned plugin graph. .rig/rig.config.ts — TypeScript for executable project plugin extras that plain data cannot carry. Session, attach, transport, and auth state are not in this file. Private run-attach credentials are resolved at runtime and never enter config. The OS sandbox is in this schema — it is the workspace.sandbox field, enforce by default (see sandbox host requirements )."},{"id":"defaults","heading":"Field defaults at a glance // what you get if you omit it","text":"Field defaults at a glance // what you get if you omit it The application graph is always present. Project-specific plugins default to an empty list; every optional policy block below uses the displayed schema default. field required? default when omitted project required — ( name required; repo? optional) plugins optional [] project extras; first-party plugins come from the application graph taskSource required — ( kind required) workspace.checkout optional \"worktree\" workspace.sandbox optional \"enforce\" (OS sandbox on by default) runtime.harness optional \"pi\" (the only accepted value) runtime.mode optional \"yolo\" planning.mode optional \"auto\" automation.maxValidationAttempts optional 30 automation.maxPrFixIterations optional 100500 (effectively unbounded) review.mode optional see review · provider \"github\" The strict merge gate is always on regardless of these defaults. Even with review.mode: \"off\" , every Rig-owned merge still demands passing GitHub reviews and status checks, and pins the merge to the exact reviewed commit via gh pr merge --match-head-commit <sha> — a commit pushed after the gate clears makes the merge fail. See the merge gate ."},{"id":"minimal-github-project","heading":"Minimal GitHub project","text":"Minimal GitHub project The declarative .rig/rigfig.toml is what rig init writes. Each plugin owns the section it needs: # .rig/rigfig.toml [project] name = \"demo\" repo = \"you/your-repo\" [taskSource] kind = \"github-issues\" owner = \"you\" repo = \"your-repo\" state = \"open\" [workspace] mainRepo = \".\" checkout = \"worktree\" sandbox = \"enforce\" The equivalent power-path .rig/rig.config.ts expresses the same project data in TypeScript. The Rig application always supplies its one first-party plugin graph; plugins contains only project-specific extras: import { defineConfig } from \"@rig/core\" ; export default defineConfig ({ project: { name: \"demo\" , repo: \"you/your-repo\" }, taskSource: { kind: \"github-issues\" , owner: \"you\" , repo: \"your-repo\" , state: \"open\" }, workspace: { mainRepo: \".\" , checkout: \"worktree\" , sandbox: \"enforce\" }, });"},{"id":"top-level-fields","heading":"Top-level fields","text":"Top-level fields field type meaning project { name, repo? } The project identity. name (string) is required; repo? (\"owner/repo\" slug) is the canonical task/workflow identity for GitHub-backed projects and Rig session entries. plugins RigPlugin[] Project-specific plugin extras. The application-owned first-party graph is always present; Rig does not scan disk for additional plugins. taskSource { kind, … } Where work comes from: github-issues , linear , or a plugin-contributed kind. The factory for the kind must come from a loaded plugin. workspace { mainRepo, checkout, sandbox, … } Git checkout strategy, OS sandbox mode, and scope normalization. Defaults to { mainRepo: \".\", checkout: \"worktree\", sandbox: \"enforce\" } . runtime object Harness, model, mode, Pi/OMP packages, agent roles, timeouts. planning / automation / pr / merge / review / issueAnalysis / github object Optional policy blocks (below). Duplicate plugin ids are caught early: if two loaded plugins register the same id for the same registration type, the engine throws a duplicate-id error at startup. See plugin authoring for what a plugin can contribute."},{"id":"task-source","heading":"taskSource // required · where work comes from","text":"taskSource // required · where work comes from taskSource: { kind: \"github-issues\" , owner: \"your-org\" , repo: \"your-repo\" , state: \"open\" , // \"open\" | \"closed\" | \"all\" (default \"open\") // labels: [\"task\"], // all required; default [] // options: { assignee: \"@me\" }, // source-side assignee filter } The only required key is kind (a string). It selects the canonical task adapter for dispatch and runtime provisioning — every kind resolves through executable factories contributed by loaded plugins. The first-party github-issues fields: field type default meaning owner string required GitHub org or user. repo string required Repository name, without the owner prefix. labels string[] [] Only issues carrying all these labels become tasks. state \"open\" | \"closed\" | \"all\" \"open\" Which issue states to enumerate. It requires the gh CLI authenticated with read access: gh issue list enumerates tasks, gh issue edit updates labels as tasks progress. See task sources for the full label convention ( scope:<glob> , role:<id> , validator:<id> , lifecycle labels). The second first-party source is Linear , contributed by @rig/linear-plugin . Add linear() to plugins and put its config under the"},{"id":"workspace","heading":"workspace // checkout & sandbox","text":"workspace // checkout & sandbox workspace: { mainRepo: \".\" , checkout: \"worktree\" , // \"worktree\" | \"directory\" sandbox: \"enforce\" , // \"enforce\" (default) | \"auto\" | \"off\" // scopeNormalization: { ... } // optional glob normalization } mainRepo is the path to the repo the agent works in; relative paths resolve from the project root. checkout selects the git strategy. It is hygiene for parallel work, not the security boundary; sandbox controls that boundary. checkout behavior \"worktree\" The default. Creates a git worktree per run; agents work in the worktree, the main repo stays clean, and parallel runs never collide. Requires mainRepo to be a git repository. \"directory\" Copies the repo into a temp directory. Slower, no shared git history. Useful when worktrees are unavailable. Docker/container isolation was declared but never implemented, so it is not in the public schema. sandbox behavior \"enforce\" The default. Runs the agent inside the OS sandbox (macOS seatbelt / Linux bwrap) and refuses to run unsandboxed when no backend is available — fail-closed. \"auto\" Degrades to unsandboxed with a warning when no backend is available. \"off\" Explicitly opts out of the OS sandbox."},{"id":"scope-normalization","heading":"workspace.scopeNormalization // optional · nested-monorepo paths","text":"workspace.scopeNormalization // optional · nested-monorepo paths Most projects don't need this — flat layouts ( src/foo/bar.ts ) match scope rules directly. You need it only when the engine sees paths like repos/inner-repo/src/foo/bar.ts but the operator wrote scope rules like src/foo/** . The shape: workspace: { mainRepo: \".\" , checkout: \"worktree\" , scopeNormalization: { // strip these prefixes when normalizing a path for scope-match; // applied in order, each strips at most once stripPrefixes: [ \"repos/inner-repo/\" ], // re-attach these prefixes when probing alternative paths searchPrefixes: [{ prefix: \"repos/inner-repo/\" , matchStartsWith: [ \"packages/\" , \"services/\" ], matchExact: [ \"CODEOWNERS\" , \"docker-compose.yml\" ], }], }, } key type meaning stripPrefixes string[] Prefixes stripped (in order, once each) before scope matching. searchPrefixes[].prefix string Prefix re-attached when a file might exist under either the stripped or prefixed form. searchPrefixes[].matchStartsWith string[] Apply the prefix only to paths starting with one of these. searchPrefixes[].matchExact string[] Apply the prefix to these exact paths. Without scopeNormalization , paths flow through un"},{"id":"runtime","heading":"runtime // optional · OMP harness & model","text":"runtime // optional · OMP harness & model runtime: { harness: \"pi\" , // \"pi\" — the only accepted value (default \"pi\") mode: \"yolo\" , // \"yolo\" | \"approval-required\" (default \"yolo\") model: \"team-default\" , // optional model override (string) pi: { packages: [ \"pi-subagents\" , \"pi-web-access@1.2.0\" ] }, agentRoles: { \"std:coder\" : { model: \"team-default\" } }, timeouts: { /* Record<string, number> — accepted but NOT consumed */ }, } field type default meaning harness \"pi\" \"pi\" The coding-agent executor. Pi is the only supported live harness — older Codex/Claude adapter names are deliberately not accepted, since dispatched runs execute on Pi regardless. mode \"yolo\" | \"approval-required\" \"yolo\" Autonomous vs. approval-gated execution. model string unset Optional model override for the harness. pi.packages string[] unset Pi extension packages every session loads (see below). agentRoles Record<string, unknown> unset Map of plugin-registered role id → config (see below). timeouts Record<string, number> unset Schema-accepted, not consumed (see below). The generated starter config defaults to the OMP/Pi happy path ( harness: \"pi\" , mode: \"yolo\" ). runtime.pi.packages is written into "},{"id":"planning","heading":"planning // optional · plan-stage policy","text":"planning // optional · plan-stage policy planning: { mode: \"auto\" , // \"auto\" | \"always\" | \"off\" (default \"auto\") // requireForLabels: [\"epic\"], // always plan for these labels // skipForLabels: [\"chore\"], // never plan for these labels } auto lets the workflow engine classify whether a task needs an explicit plan from issue metadata, risk, and labels before the OMP session starts work. always forces a plan stage; off skips it."},{"id":"automation-pr-merge","heading":"automation / pr / merge // optional · the autonomous loop","text":"automation / pr / merge // optional · the autonomous loop automation: { maxValidationAttempts: 30 , // validation retry cap (default 30) maxPrFixIterations: 100500 , // PR fix loop cap — effectively unbounded by default }; pr: { mode: \"auto\" , // \"auto\" | \"ask\" | \"off\" watchChecks: true , autoFixChecks: true , autoFixReview: true , // pendingTimeoutMs / pendingPollMs — how long and how often to wait on pending checks }; merge: { mode: \"auto\" , // \"auto\" | \"off\" | \"pr-ready\" (stop once the PR is gate-ready) method: \"repo-default\" , // \"repo-default\" | \"squash\" | \"merge\" | \"rebase\" deleteBranch: \"repo-default\" , // \"repo-default\" | boolean allowedFailures: [], // explicit allowlist of acceptable failing checks // bypass: true // admin-merge past branch protection (off by default) }; field type default meaning automation.maxValidationAttempts number 30 Validation retry cap before a task is marked needs-attention. automation.maxPrFixIterations number 100500 PR-fix loop cap — effectively unbounded, for Rig-owned automerge convergence. pr.mode \"auto\" | \"ask\" | \"off\" — Whether Rig opens / drives the PR. pr.watchChecks boolean — Watch CI checks after opening the PR. pr.autoFixChecks"},{"id":"review","heading":"review // optional · completion gate","text":"review // optional · completion gate review: { mode: \"required\" , // \"off\" | \"advisory\" | \"required\" provider: \"github\" , // GitHub reviews + status checks (the default provider) } The completion review gate — the verdict on task completion: off skips it, advisory reports without blocking, required makes a failing review block completion. The strict merge gate is separate and always on : every Rig-owned merge demands passing GitHub reviews and status checks before gh pr merge is even attempted, and pins the merge to the reviewed head SHA, regardless of this setting. The gate policy is owned by the swappable @rig/pr-review-plugin (default provider github ). Inspect or change the completion policy with rig review show / rig review set . The full evidence model is on the merge gate page ."},{"id":"github","heading":"github // optional · issue & project sync","text":"github // optional · issue & project sync github: { projects: { enabled: true , projectId: \"PVT_...\" , statusFieldId: \"PVTSSF_...\" , statuses: { running: \"In Progress\" , prOpen: \"In Review\" , done: \"Done\" }, }, } GitHub Projects status sync is a separate, optional switch — first-class when enabled. The workflow engine updates the configured Project v2 Status field at run start, PR open, CI/review fixing, merging, done, and needs-attention, using the statuses map you provide (any subset of running , prOpen , ciFixing , merging , done , needsAttention ). Project status sync is driven solely by projects.enabled ."},{"id":"issue-analysis","heading":"issueAnalysis // optional · continuous triage","text":"issueAnalysis // optional · continuous triage issueAnalysis: { enabled: true , harness: \"pi\" , mode: \"continuous\" , // \"continuous\" | \"off\" } Turn this on and the workflow engine keeps a drone on triage: it reads GitHub issues through OMP/Pi, maintains Rig-owned metadata blocks and labels, and proposes or creates rig:generated issues — never touching the human-authored body."},{"id":"declarative-toml","heading":"Declarative rigfig.toml // the happy path","text":"Declarative rigfig.toml // the happy path .rig/rigfig.toml is plain data with no executable plugin logic. The application supplies the one first-party graph; the file carries project sections such as [project] , [taskSource] , [workspace] , [runtime] , [planning] , [automation] , [merge] , [pr] , [github] , and [issueAnalysis] . Reach for .rig/rig.config.ts only when project-specific executable factories, task sources, or validators are required."},{"id":"sandbox-host","heading":"Sandbox host requirements // what workspace.sandbox needs","text":"Sandbox host requirements // what workspace.sandbox needs The OS sandbox is the workspace.sandbox field, \"enforce\" by default — it fails closed when the host lacks a usable backend rather than silently degrading. Check the host that will execute runs with rig doctor : host strict backend requirement Linux bubblewrap / bwrap , with bwrap --ro-bind / / --proc /proc --dev /dev -- true passing. Ubuntu 24.04 / AppArmor-restricted hosts may need kernel.apparmor_restrict_unprivileged_userns=0 . macOS the built-in seatbelt ( sandbox-exec ) available on PATH. sandbox: \"auto\" , sandbox: \"off\" , RIG_RUNTIME_SANDBOX=off , and RIG_SANDBOX_BACKEND=none are degraded mode — useful while diagnosing host setup, but unsandboxed and not strict run evidence. See security & trust and environment variables ."},{"id":"see-also","heading":"See also","text":"See also Entities & data model — the Task, Run, and Worktree shapes the config feeds into. The strict merge gate — the always-on verdict that merge and review sit on top of. Runs & OMP sessions — the runtime path this config feeds: bounded CNet projection, Swarm Commander model role, and /fleet / /tasks / /drone . Environment variables — the runtime knobs (timeouts, tokens, adapters) that live outside the schema. Plugin authoring — how the plugins array contributes task sources, validators, roles, and CLI commands."}]},{"url":"docs/recipes/index.html","title":"Recipes","sections":[{"id":"","heading":"","text":"Recipes Current copy-paste flows. Install, open foreground OMP with bare rig , work another workspace, attach to one exact detached drone, and use the exact headless CLI when scripting."},{"id":"first-session","heading":"Install and open the Rig session","text":"Install and open the Rig session $ curl -fsSL https://where.rig-does.work/install | bash $ cd ~/work/app $ rig Bare rig opens foreground OMP with the Rig extension. Use /tasks , /fleet , and /drone [ref] to materialize CNet cards inline; the Swarm Commander model role can answer from the same fleet snapshot."},{"id":"workspace","heading":"Open another workspace","text":"Open another workspace $ rig --workspace ~/work/other-repo Drive a project without cd -ing into it."},{"id":"collab","heading":"Attach to one drone","text":"Attach to one drone $ rig attach 8a31e7 $ rig attach 8a31e7 --web Terminal and web attach target one exact live detached run. Rig resolves the private collab credential from run discovery; raw links are transport data and are not part of the public workflow."},{"id":"operate","heading":"Operate a run","text":"Operate a run Use /fleet to materialize one fleet aggregate and /drone <run-id> for one exact drone card. Control and attach actions execute from CNet; approvals and questions appear as separate inline attention items."},{"id":"headless-cli","heading":"The headless CLI","text":"The headless CLI Beyond the interactive session, the exact CLI runs the same operations non-interactively — for diagnostics, scripting, CI, and agents: $ rig doctor $ rig config get workspace.sandbox $ rig config set review.provider github $ rig test all $ rig pi search <query> $ rig pi add <package> These run the same operations non-interactively — reach for the headless CLI from scripts, CI, and agents. Interactive day-to-day work flows through bare rig , /fleet , /tasks , /drone , and the Swarm Commander."}]},{"url":"docs/glossary/index.html","title":"Glossary","sections":[{"id":"","heading":"","text":"Glossary The current vocabulary. Bare Rig opens foreground OMP with its extension, renders a bounded current projection into the CNet, and lets the operator direct detached runs through inline cards or the Swarm Commander model role."},{"id":"actors","heading":"Actors & surfaces","text":"Actors & surfaces term definition operator The human using bare rig , the Rig session, /fleet , /tasks , /drone , and rig doctor . Rig session The OMP conversation opened by bare rig for a workspace, with the bundled Rig extension loaded. OMP session The coding-agent runtime that owns transcript, cwd, tools, prompts, and participants. Bare rig opens the foreground operator session; each run is a separate detached OMP session. CNet The typed Rig layer inside the foreground OMP transcript: bounded current feed items, explicit query snapshots, action requests, and exact receipts. attention item An inline CNet item for a run, approval, or question that currently needs operator input. Swarm Commander The configured model role inside foreground OMP. It receives bounded current CNet context and uses generated discoverable tools; it is not a separate process or session. CNet receipt The durable outcome of a typed query or action. A transport send alone is not an applied receipt. drone card The inline CNet item materialized by /drone [ref] : one run's status, placement, progress, recent timeline, controls, and attach actions. run One unit of agent work. A run is a detached supervised OMP se"},{"id":"extension","heading":"Mechanism & extension terms","text":"Mechanism & extension terms harness The fully-provisioned environment every run gets: a per-run isolated git worktree, isolated runtime home and credentials, materialized compiled binaries, and an OS sandbox enforced by default. package floor The only non-plugin packages: @rig/contracts (schemas/types), @rig/core (config + plugin composition), and @rig/kernel-seed (bootstrap seed + capability resolver). Everything else, including the CLI and session surface, is a plugin. Rig plugin An explicit definePlugin package composed in rig.config.ts ; it contributes commands, lifecycle stages, validators, hooks, capabilities, and other product behavior. OMP/Pi extension An installable live-session package for tools, slash commands, subagents, and behaviors. Declared in runtime.pi.packages and materialized into .pi/settings.json . skill A SKILL.md prompt-context file a plugin ships. Rig materializes it verbatim into .pi/skills/ , the directory OMP/Pi scans."},{"id":"headless-cli","heading":"The headless CLI","text":"The headless CLI The exact CLI groups ( doctor , config , test , pi , server , run , and others) are a first-class headless adapter over the same operations, for scripts, CI, and agents. Interactive product copy should prefer foreground OMP, CNet, inline card, detached drone, Swarm Commander model role, exact-run attach, placement, harness, task source, plugin, and package-floor terms. /fleet , /tasks , and /drone are query aliases that materialize inline cards."}]},{"url":"docs/faq/index.html","title":"FAQ","sections":[{"id":"","heading":"","text":"FAQ Straight answers for the current Rig surface."},{"id":"start","heading":"How do I start?","text":"How do I start? Install the binary, then run bare rig in a project. It opens foreground OMP with the Rig extension for that workspace: bounded CNet projection, generated model tools, model control, and inline /fleet , /tasks , /drone cards. Use rig --workspace <path> for another project."},{"id":"share","heading":"Can I attach to a running drone?","text":"Can I attach to a running drone? Yes. Use rig attach <run-id> for terminal attach or add --web for web attach. Both target that exact detached drone; Rig resolves its private credential internally. Foreground Rig and the Swarm Commander role are not attachable sessions."},{"id":"tasks","heading":"Where do tasks come from?","text":"Where do tasks come from? From GitHub Issues or Linear. Task state is derived from the source on read — there is no local task database."},{"id":"packages","heading":"How do extensions load?","text":"How do extensions load? Rig plugins are explicit definePlugin packages composed in rig.config.ts . OMP/Pi extensions are live-session packages declared in runtime.pi.packages ; Rig materializes them into .pi/settings.json and OMP/Pi auto-installs them at session start. Manage them with rig pi add/list/search ."},{"id":"gate","heading":"How does the merge gate work?","text":"How does the merge gate work? The default review provider is github — GitHub reviews plus status checks. The gate pins the merge to the exact reviewed commit, so a commit pushed after the gate clears makes the merge fail. Swap the policy with review.provider ."},{"id":"headless-cli","heading":"Is the exact CLI just for scripts?","text":"Is the exact CLI just for scripts? No — the exact CLI is a first-class headless adapter over the same operations, for scripts, CI, and agents: rig doctor , rig config get/set , rig test , rig server , rig run , and more (add --json for structured output). Interactively, the same work runs through bare rig , /fleet , /tasks , /drone , and the Swarm Commander."}]},{"url":"docs/packages/index.html","title":"Packages","sections":[{"id":"","heading":"","text":"Packages Rig is a 3-package mechanism floor with ~30 plugins composed on top. The floor never depends on a plugin; plugins depend on the floor and reach each other through capabilities."},{"id":"floor","heading":"The mechanism floor","text":"The mechanism floor These three packages are the only mechanism floor. Everything else — including the CLI launcher and CNet surface — is composed above it. package purpose @rig/contracts Schemas, types, IPC/panel shapes. The dependency root. @rig/core Config + plugin-composition library ( defineConfig / definePlugin , the plugin host). Harness-agnostic — not a product host or runtime. @rig/kernel-seed Irreducible bootstrap seed, plugin ABI, and capability resolver."},{"id":"applications","heading":"Applications","text":"Applications Three deployable applications sit above the floor and plugins. apps/rig ( @rig/rig ) is the Rig application: the sole product plugin graph, the process entrypoints ( bin/rig , rig-agent , rig-browser-tool ), argv routing, and the official build invocation. apps/rig-relay-registry ( @rig/rig-relay-registry ) is a standalone, content-blind, in-memory collab relay + run-projection registry that depends only on @rig/contracts . apps/rig-collab-web ( @rig/collab-web ) is the browser guest app for collab live sessions, vendored from @oh-my-pi/collab-web and served at where.rig-does.work."},{"id":"plugins","heading":"The plugins","text":"The plugins The ~30 @rig/* plugins carry every product surface. A representative slice: plugin role @rig/executable-binary-target-plugin Build-time executable assembly + target-specific standard/tailored ArtifactSet compiler, invoked by apps/rig 's build. Reads a build descriptor and never imports the app — not the CLI launcher. @rig/product-entrypoint-plugin Bare rig commands, help, and foreground OMP launch/resume with OMP-native session history. @rig/omp-extension-plugin OMP CNet runtime: bounded transcript projection, inline cards, model control, generated discoverable tools, and client-side exact-run attach effects. @rig/run-plugin Run journal codec, run read model, current fleet/run/attention CNet projections, controls, and exact-run attach actions. @rig/tasks-plugin Task source access, derived state, bounded /tasks projection, and task-owned create/rename/close actions. @rig/transport-plugin Detached-drone transport: placement, run dispatch, supervision, room registration, discovery, exact-run attach resolution, and relay/registry URL resolution. @rig/config-plugin rig config get/set and config read/write for the headless CLI. @rig/github-provider-plugin Swappable GitHub SCM"},{"id":"pinning","heading":"Pinning","text":"Pinning The Rig application always supplies its first-party graph. Project-specific plugin extras are composed in .rig/rig.config.ts . OMP/Pi packages are declared in runtime.pi.packages and materialized into .pi/settings.json at session start. Nothing is auto-scanned."},{"id":"distribution","heading":"Distribution","text":"Distribution The install script ( curl -fsSL https://where.rig-does.work/install | bash ) installs the global rig binary. That application carries its first-party graph and process entrypoints; a fresh install needs no per-project first-party package install."}]},{"url":"index.html","title":"Rig — overview","sections":[{"id":"","heading":"","text":"Rig overview docs plugins skills cli config github Air superiority, as a binary. rig — OMP session cnet live $ curl -fsSL https://where.rig-does.work/install | bash copy scroll to descend Arm. Install once, type bare rig — ground control opens with the cnet live, the fleet linked, and every verb loaded. open the loop → Command. Say it in chat — the Swarm Commander reads the cnet, picks the verb, and moves the fleet. Receipts, or it didn't happen. meet the swarm commander → Clear. The cnet lives in your transcript — /fleet , /tasks , and /drone drop live cards inline; the one that needs you says so. the ops story → the night shift Yesterday's backlog. This morning's merges. Ammunition comes from your real tracker — GitHub Issues, Linear, or your own source. A drone takes the task, flies the sortie in its own sealed session, fights the review, and lands a SHA-pinned merge. Close ground control — the sorties keep flying; reopen it and the fleet is right there in the cnet. the loop One terminal command. One live session. Install Rig, type rig , and run the war from the transcript. The cnet streams what the fleet is doing; every move you make comes back with a receipt. 01 Install The on"}]}]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
:root{--panel-2:#101115}
|
|
2
|
+
/* ---------- Pi terminal player — animated Pi session, Pi-accented (cyan) ---------- */
|
|
3
|
+
.pi-term{position:relative;background:#090d13;border:1px solid rgba(86,216,255,.16);border-radius:12px;overflow:hidden;margin:26px 0}
|
|
4
|
+
.pi-term-bar{display:flex;align-items:center;gap:10px;padding:11px 18px;border-bottom:1px solid rgba(86,216,255,.1);background:rgba(86,216,255,.035)}
|
|
5
|
+
.pi-term-bar .t{font-family:var(--mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:#7ea3c7}
|
|
6
|
+
.pi-term-bar .dot{width:6px;height:6px;border-radius:50%;background:var(--ac-2);box-shadow:0 0 8px var(--ac-2);animation:pulse 2.4s ease-in-out infinite;margin-left:auto}
|
|
7
|
+
.pi-term-body{padding:18px 22px 8px;font-family:var(--mono);font-size:13px;line-height:1.85;color:var(--ink-2);overflow:hidden;display:flex;flex-direction:column;justify-content:flex-end}
|
|
8
|
+
.pi-term-body .ln{white-space:pre-wrap;word-break:break-word;min-height:1.85em}
|
|
9
|
+
.pi-term-body .ln.u{color:var(--ink)}
|
|
10
|
+
.pi-term-body .ln.think{color:#7ea3c7;font-style:italic;animation:pithink 1.6s ease-in-out infinite}
|
|
11
|
+
@keyframes pithink{50%{opacity:.4}}
|
|
12
|
+
.pi-term-body .ln.out{color:var(--ink-2)}
|
|
13
|
+
.pi-term-body .ln.dim{color:var(--ink-4)}
|
|
14
|
+
.pi-term-body .ln.add{color:#9be8c5;background:rgba(60,190,130,.1)}
|
|
15
|
+
.pi-term-body .ln.del{color:#e8a3a3;background:rgba(220,90,90,.1);text-decoration:line-through;text-decoration-color:rgba(232,163,163,.5)}
|
|
16
|
+
.pi-term-body .ln.code{color:#7ea3c7}
|
|
17
|
+
.pi-term-input{border-top:1px solid rgba(86,216,255,.14);margin:10px 22px 0;padding:10px 0;font-family:var(--mono);font-size:13px;color:var(--ink)}
|
|
18
|
+
.pi-term-input .cur{display:inline-block;width:1ch;background:var(--ink);color:#090d13;animation:blink 1.1s steps(1) infinite}
|
|
19
|
+
.pi-term-status{display:flex;flex-direction:column;gap:2px;padding:8px 22px 14px;font-family:var(--mono);font-size:12px;color:#55779b;border-top:1px solid rgba(86,216,255,.14);margin-top:10px}
|
|
20
|
+
.pi-term-status .row{display:flex;justify-content:space-between;gap:18px;white-space:nowrap;overflow:hidden}
|
|
21
|
+
.pi-term-status .row .right{color:#55779b}
|
|
22
|
+
@media(max-width:760px){.pi-term-status .row{font-size:10.5px}}
|
|
23
|
+
|
|
24
|
+
/* ---------- docs search ---------- */
|
|
25
|
+
.nsearch{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--line-2);background:var(--panel);color:var(--ink-3);font-family:var(--mono);font-size:12px;border-radius:7px;padding:6px 12px;cursor:pointer;transition:.15s;letter-spacing:.02em}
|
|
26
|
+
.nsearch:hover{border-color:var(--ink-3);color:var(--ink)}
|
|
27
|
+
.nsearch kbd{background:var(--panel-2);border:1px solid var(--line);border-radius:4px;padding:1px 5px;font-size:10px;color:var(--ink-4)}
|
|
28
|
+
.search-overlay{position:fixed;inset:0;z-index:200;display:none;background:rgba(5,6,7,.7);backdrop-filter:blur(6px)}
|
|
29
|
+
.search-overlay.open{display:flex;align-items:flex-start;justify-content:center;padding:12vh 20px 0}
|
|
30
|
+
.search-box{width:100%;max-width:640px;background:var(--panel);border:1px solid var(--line-2);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px rgba(0,0,0,.5)}
|
|
31
|
+
.search-box input{width:100%;box-sizing:border-box;background:transparent;border:none;outline:none;color:var(--ink);font-family:var(--mono);font-size:16px;padding:18px 22px;border-bottom:1px solid var(--line)}
|
|
32
|
+
.search-box input::placeholder{color:var(--ink-4)}
|
|
33
|
+
.search-results{max-height:52vh;overflow-y:auto;padding:8px}
|
|
34
|
+
.search-results .sr{display:block;padding:12px 16px;border-radius:8px;color:inherit;transition:.12s}
|
|
35
|
+
.search-results .sr:hover,.search-results .sr.sel{background:var(--panel-2)}
|
|
36
|
+
.search-results .sr .pg{font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-4)}
|
|
37
|
+
.search-results .sr .hd{color:var(--ink);font-size:15px;margin-top:3px;font-weight:500}
|
|
38
|
+
.search-results .sr.sel .hd{color:var(--ac)}
|
|
39
|
+
.search-results .sr .sn{color:var(--ink-3);font-size:13px;margin-top:3px;line-height:1.5;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
|
|
40
|
+
.search-results .sr .sn mark{background:transparent;color:var(--ac);font-weight:600}
|
|
41
|
+
.search-empty{padding:26px 22px;color:var(--ink-4);font-family:var(--mono);font-size:13px;text-align:center}
|
|
42
|
+
.search-hint{display:flex;gap:16px;padding:10px 18px;border-top:1px solid var(--line);font-family:var(--mono);font-size:11px;color:var(--ink-4)}
|
|
43
|
+
|
|
44
|
+
/* ---------- companion drone (every page) ---------- */
|
|
45
|
+
#dronecv{position:fixed;left:0;top:0;width:200px;height:200px;pointer-events:none;z-index:55;will-change:transform}
|
|
46
|
+
|