A Codex seat that hands over to a new occupant keeps OpenRig's guidance. Since #2441, a fresh Codex launch carries the guidance as launch developer instructions instead of writing AGENTS.md, but a fresh or rebuild handover successor launches without them, so it started with no guidance (a fork very likely too; the put-back covers it). Before the successor launches, the handover now puts the seat's missing guidance blocks back in the repository AGENTS.md from its saved startup context, using the same steps restore uses: only missing blocks are written, the person's text around them is unchanged, and a seat that already has them, or has no saved context, is left alone. A discovered handover launches nothing and is skipped. The startup guide lists a handover successor among the cases that still use the repository AGENTS.md. Part of #875, which stays open.
58 KiB
Agent Startup Guide
Last validated: 2026-10-05, against main 9b88b118
Applies to: OpenRig 0.6.8
This guide teaches you how to think about what goes into an agent's startup experience — what files to write, where to put them, and how the layering model delivers them. It is an authoring guide, not a schema reference. For field-level details, see rig-spec.md and agent-spec.md.
Continue after a native consent prompt
If a fresh Claude seat is waiting at its bypass-permissions warning, review and
answer that warning in the seat's native pane. Unless the rig was launched with
--non-interruptive (see non-interruptive mode),
OpenRig does not accept it for you, and it never sends configured startup context
into the active dialog. The seat reports attention_required and retains that
context for the same occupant.
After accepting, run rig seat continue <seat> (or press c on the seat in the
TUI, shown while its context is pending). This delivers the pending context
without relaunching Claude, and its first prompt reminds the seat that this is a
fresh conversation and how to find its work. It does not replay a delivery that
already started. A timeout, or a refusal because nothing is pending, reports an
unknown outcome; inspect rig seat status <seat> before taking another action.
It is also refused while the prompt is still showing (attention_required), or
when the seat's binding or runtime has changed since launch.
Runtime readiness, startup delivery, and orientation proof remain separate;
a missing proof does not block this continuation.
Other fresh-launch prerequisites, such as login or workspace trust, use the same
continuation: resolve the prerequisite in the native pane, then run the displayed
command. rig ps --nodes shows it under "Startup details ()", and
rig up and rig bundle install print it as "Startup attention ()":
"After resolving it in , run: rig seat continue ".
The attention codes that stop startup this way are trust_gate,
hook_trust_gate, update_gate, login_required, mcp_gate,
bypass_consent_gate, codex_auth_refusal and codex_client_incompatible.
They are recognized from each harness's English prompt text.
Continue a Codex fork whose child ID arrived late
For a member with session_source.mode: fork, OpenRig waits for Codex's new
child thread ID before sending its role and other startup prompts. It saves the
ID and sends the role only for a verified child: the bound pane's foreground
process forks the requested parent, and its own native record, matched to its
start time, supplies the ID. This capture uses runtime.readiness_timeout_seconds
(30 seconds by default, configurable from 1 to 600 seconds), as does the later
readiness check. The two waits have separate budgets. A seat handover with
--source fork:<seat> is different: its fork carries the conversation itself,
and a missed ID capture leaves the ID pending rather than stopping the handover.
If the ID is still unavailable, the seat reports attention_required and keeps
its startup context pending. Wait for the child to finish starting, resolve any
native prompt, then run rig seat continue <seat>. The continuation verifies
that the bound pane is running a fork of the requested parent and reads the
child's own native identity before delivering the saved context. It does not
launch another fork or resume the parent. The parent ID is never saved as the
child's resume token.
An unavailable or conflicting identity leaves the context pending. Once prompt
delivery has been attempted, it cannot be replayed by continue, including
after a daemon restart: inspect rig seat status <seat> and the native pane
before deciding how to recover an uncertain delivery.
Two Categories of Startup
Everything an agent receives at boot time falls into one of two categories:
Category 1: Context Loading
The agent reads markdown files into its context window. This is the primary mechanism in OpenRig today and the one you should invest most of your authoring effort in.
Context loading shapes what the agent knows, believes, and is capable of:
- Who it is (role, identity, pod membership)
- How the team works (culture, communication norms, coordination protocols)
- What the project is (codebase context, architecture, domain knowledge)
- What it can do (skills, SOPs, operational procedures)
- What its environment looks like (services, access credentials, tools)
Category 2: Deterministic Configuration
The rig spec declaratively installs things into the agent's runtime environment:
- Hooks (shipped inside plugins)
- Permissions (
.claude/settings.jsonallowlists, approval modes) - MCPs (Model Context Protocol servers)
- System dependencies (tools, packages)
See the Current Support Matrix section at the end of this guide for what is reliable today vs experimental.
The recommendation: Put as much of your setup logic as possible into Category 1 (context loading via markdown files). Describe the desired end state in startup files and let the agent handle the configuration. The deterministic path exists in the spec and will become more reliable over time, but for now the context-loading path is the one that works consistently across runtimes.
Context Loading: What Goes Where
Skills vs Startup Files — The Key Distinction
Skills are reusable SOPs. They teach an agent HOW to do something — how to use OpenRig, how to do TDD, how to run a code review, how to operate Vault. Skills transfer across rigs. A skill you write once can be used by any agent in any rig.
Startup/guidance files are rig-specific and role-specific. They tell an agent WHO it is, WHAT it's working on, and HOW this particular team operates. They are authored per-rig and often per-pod or per-member.
| Put in a skill when... | Put in a startup/guidance file when... |
|---|---|
| The knowledge is reusable across rigs | The knowledge is specific to this rig or project |
| It teaches a procedure or methodology | It teaches identity, role, or team context |
| It could be useful to any agent of this type | It's only useful to agents in this specific topology |
Examples: openrig-user, test-driven-development, vault-user |
Examples: role.md, CULTURE.md, startup/context.md |
The Belt-and-Suspenders Pattern for Skills
Skills are delivered to agents through two parallel paths:
- Spec declaration: The agent spec's
resources.skills+ profileuses.skillsensures the skill files are projected to the agent's workspace (installed before harness boot) - Startup instruction: The guidance/startup files tell the agent to actually read and load those skills
Both paths are important. The spec projection ensures the files are physically present. The startup instruction ensures the agent knows to read them. This redundancy is intentional — it handles cases where one path fails.
Example: An implementer agent's startup guidance says "Load the following skills: openrig-user, test-driven-development, systematic-debugging" — AND the agent spec's profile uses those same skill IDs. The agent gets the files from projection and the instruction to read them from guidance.
The File Types
Role guidance (guidance/role.md)
Tells the agent who it is and what its responsibilities are:
- Title and primary function
- Specific responsibilities (bulleted list)
- Working rhythm (how the agent operates day-to-day)
- Principles (behavioral guidelines)
- Relationship to other team members
This is the agent's identity document. Every agent should have one.
Rig culture (CULTURE.md)
The rig-wide constitution. Applied to ALL agents in the rig:
- Communication norms (use
rig send/chatroom, not raw tmux) - Coordination protocol (how work flows between pods)
- Quality standards (what "done" means)
- Commit/merge policy
- Escalation rules
Think of this as the team operating manual that every team member reads on day one.
Startup context (startup/context.md)
Boot-time grounding information specific to this agent's operating environment:
- Identity recovery instructions (
rig whoami --json) - Environment details (service URLs, access credentials, ports)
- System check instructions (what should be running, how to verify)
- Role-specific delegation information (who to ask, who delegates to you)
This is especially important for managed-app specialists (e.g., a Vault specialist needs to know the Vault address and dev token).
Project documentation
Rig-level startup files that teach the agent about the project:
- Architecture overview
- Key conventions
- Domain vocabulary
- Recent context (what's been happening, what's in progress)
These go in rig-level or pod-level startup blocks and are delivered to relevant agents.
The Layering Model
Startup content is merged additively through layers. Each layer adds to what the previous layers provided. Later layers do NOT replace earlier layers — they append.
The Layers (in delivery order)
1. Agent layer — from the AgentSpec's top-level startup block
2. Profile layer — from the active profile's startup block
3. Culture layer — OpenRig's default culture, then the RigSpec's culture_file
4. Rig layer — from the RigSpec's top-level startup block
5. Pod layer — from the pod's startup block
6. Member layer — from the member's startup block in the RigSpec
7. Operator layer — added by OpenRig: openrig-start.md and, on a fresh start, the onboarding pack
A member with a starter_ref gets that agent starter's files in front of the agent layer. Terminal seats
(builtin:terminal) skip the agent and profile layers.
What Each Layer Is For
| Layer | Authored by | Purpose | Example content |
|---|---|---|---|
| Agent | Agent spec author | Core identity and capabilities that travel with this agent type | Role guidance, default skills |
| Profile | Agent spec author | Profile-specific variations | Different skill sets for "default" vs "minimal" profiles |
| Culture | OpenRig, then the rig spec author | Team norms | OpenRig's CULTURE-default.md for every seat, then the rig's own CULTURE.md |
| Rig | Rig spec author | Rig-wide context for all agents | Project documentation |
| Pod | Rig spec author | Pod-specific coordination context | Pod SOP, intra-pod workflow |
| Member | Rig spec author | Individual member overrides | Member-specific instructions, cwd-specific context |
| Operator | OpenRig system | System-injected runtime content | openrig-start.md (optional delivery); on a fresh start, openrig-onboarding-01.md and -02.md unless onboarding.default_pack.enabled is false |
After the files, OpenRig sends a session-identity message (rig, pod, member, logical ID) once the seat is ready.
Practical Guidance
Most rigs only need three layers: agent (role.md), culture, and the built-in operator layer. Start simple. Add pod and member layers only when agents in the same pod need different startup content.
The rig's own culture file is high-value and often skipped. Every seat gets OpenRig's default culture, but that says nothing about how this team communicates and coordinates. Write a CULTURE.md. Even a short one dramatically improves team coherence.
Member-level startup is for exceptions, not the rule. If every member has its own startup block, the layering model is being used as a configuration dump. Refactor shared content up to the pod or rig level.
Delivery Mechanisms
How Files Reach the Agent
| Delivery Hint | When | How | Use For |
|---|---|---|---|
auto |
Depends on the file | Resolved per file: a SKILL.md (or content starting # SKILL) is skill_install, any other .md is guidance_merge, and any other file is send_text |
Default — let OpenRig decide |
guidance_merge |
Before harness boot | Merged as a managed block into the repository AGENTS.md (Codex, OMP), a fresh Pi seat's own agent-directory AGENTS.md (see Runtime Differences), or CLAUDE.md (Claude; CLAUDE.local.md with the rig's managed_blocks: { claude-code: CLAUDE.local.md }). A fresh launch of Codex 0.164.0 or later can carry it as launch developer instructions instead (see Codex guidance at launch) |
Role guidance, culture, project context |
skill_install |
Before harness boot | Copied to runtime's skill directory | Skills |
send_text |
After harness is ready | Sent as text to agent's terminal via tmux | Boot-time grounding, identity hints, instructions to read skills |
Delivery Timing Matters
Files delivered via guidance_merge and skill_install happen BEFORE the agent's harness boots. The agent sees them immediately when it starts — they're part of the initial context.
Files delivered via send_text happen AFTER the harness is ready. The agent receives them as messages in its terminal. Use this for:
- Identity grounding (agent reads and processes the instructions)
- Instructions to load skills (the files are already projected, the message tells the agent to read them)
- Context that should feel like an operator briefing, not pre-loaded content
On a fresh launch the first message is one turn: the session identity, the first send_text file and, when a startup
proof is selected, its challenge (below).
A file's required field defaults to true: a required file that can't be delivered fails startup, and an optional
one is dropped without a message.
Readiness, startup proof and failure
Readiness. After launch OpenRig polls the pane until the harness is interactive (backing off 1, 2, 4, 8 and 16
seconds), for up to 30 seconds by default. Change the window with
rig config set runtime.readiness_timeout_seconds <1-600> (or OPENRIG_RUNTIME_READINESS_TIMEOUT_SECONDS); it
applies to the next launch. A recognized prompt (the attention codes above) ends the wait at once as
attention_required. A timeout ends startup as failed ("Readiness timeout after Ns — harness did not become
interactive"), and the post-launch files aren't sent. A Codex fork that the rig launches (a member with
session_source.mode: fork, or an agent image's fork) is the exception: its timeout ends as attention_required,
with its startup context kept for rig seat continue (see below). A seat handover's fork has its own readiness check.
Fresh Claude handovers. rig handover --source fresh keeps the previous agent's scrollback. Before sending the
handover context, OpenRig waits for the latest prompt inside Claude's complete input frame, and confirms that the
process in the pane belongs to the new launch. A shell's ❯ prompt or echoed launch command is not enough, even
with an old Claude header or input frame above it and Claude already running. A header alone or a partially drawn
input frame also keeps the check pending. If the current screen or process cannot be read, or the process identity is unconfirmed, the handover keeps
waiting within the readiness window and fails if that window expires. Old screen text or the claude process label
alone cannot complete this check. A healthy agent can therefore remain unconfirmed while those observations are
unavailable; inspect its pane before deciding how to recover.
Claude submission check. For each startup message to a Claude seat, OpenRig makes a bounded check that the prompt
was submitted. When the capture can't tell, it records the submission as unverified and carries on. When it sees the
same prompt still sitting in the input, it presses Enter once more; if it's still there, it warns "Startup prompt still
staged in ; press Enter in that pane." The seat can still end ready with its submission unverified or
staged, so ready doesn't prove the prompt was submitted. After all files and actions are delivered, readiness is
checked once more, so a prompt that appears by then gives attention_required.
Startup proof. A startup_proof action selects authenticated or none (the default is none; the last applicable
one wins, and it needs idempotent: true). With authenticated, a fresh launch of an agent seat includes a challenge,
and the seat answers it with rig startup-proof submit --challenge-id <id> --answer <answer>. rig ps --nodes --full
shows the result in the ORIENTED column: verified, missing, rejected or n-a. A missing proof doesn't block startup.
After a failure. Delivery, launch and action failures, and readiness timeouts other than a Codex fork's, give
failed. Every 30 seconds the context monitor marks a failed or attention_required seat ready once its pane reads
ready, unless its fresh or Codex fork context is still pending for rig seat continue. A seat that timed out can therefore read ready without having received its
post-launch files. If it needs them, first look at what that occupant has done; then
rig seat launch <seat> --fresh --reason <text> starts a blank conversation for the seat. If the occupant is still
live, the command refuses with session_live unless you also pass --stop, which replaces that occupant deliberately.
Preview before replacing a seat. rig seat launch <seat> --fresh --plan inspects the selected
seat without starting tmux, stopping or launching a process, projecting startup files, or changing
lifecycle/queue records. Add --json for structured output. --fresh is still required;
--reason is optional for the preview and required when executing. Pass --stop to preview the
intended replacement of a live managed occupant. Adopted and unmanaged occupants remain blocked.
The plan identifies the target, current occupant, stop requirements, blockers, startup counts, and each sibling seat's unchanged effect. Siblings stay attached and untouched. Launch composes a new command through the runtime adapter using current settings; it does not replay a stored wrapper command. The seat's launch environment and managed executable selection still apply. The plan does not construct or display the launch command.
A preview is a snapshot, not a reservation or a native-readiness check. Unreadable tmux produces
complete: false and launchable: null (unknown), unless a known blocker already proves
launchable: false. A successful preview exits 0, including blocked or incomplete plans; scripts
must inspect launchable === true before deciding to execute. Request failures still exit nonzero.
Execution always resolves and checks the current state again. An older daemon without the preview
endpoint refuses the request; the CLI never falls back to executing a launch.
Change an existing seat's working directory
Editing cwd in an already applied RigSpec doesn't update an existing seat's saved launch directory. Use set-cwd to
save a new one for the seat's next launch:
rig seat set-cwd planning-lead@project \
--cwd /work/project/seats/planning-lead --reason "isolate planning"
rig seat status planning-lead@project
The path: it must be absolute on the daemon's host. OpenRig creates a missing directory, resolves symlinks to its real path, and refuses a filesystem root.
What it saves: the directory and a node.cwd_changed audit event (from, to, reason, operator), together, in one
transaction. The seat's node, canonical address, queue, session history and skill selection stay as they are.
rig seat status shows Working directory for future launches (launch_cwd in JSON). That's saved configuration,
not where the running process is.
What it writes: it creates the folder if it's missing, and writes nothing else there. The seat's next fresh launch or rebuild handover writes its startup files there, as launch does today.
set-cwd's preflight policy: before saving, it reads, without writing, the enumerated resource copies (including
nested plugin files) and startup skills that launch would place in the new directory, at their actual destinations,
as they are at that moment. It's a point-in-time check, not a global overwrite guarantee or protection against later
changes; at adoption, launch applies its own policy. (The commit's re-check covers the saved startup configuration,
not the destination's file contents.)
- Refused, with the protected paths named (
cwd_destination_conflict): a file there that an operator edited, an unrecorded file with different content, or two sources for one destination. That's stricter than launch, which overwrites an unrecorded differing file with a warning; the refusal is deliberate, while the occupant still runs. - What to do: use a clean destination, or move or merge those files, then retry.
- Not checked: managed guidance blocks and native configuration merges keep the adapter's policy at launch, and
launch-owned files such as
.openrig/context-collector.cjsare written by launch as always. - A community harness takes no startup files, so none are checked for it.
The running occupant keeps its current directory. Adopt the new one with:
rig seat launch <seat> --fresh --stop --reason <text>, which starts a blank conversation; orrig handover <seat> --source rebuild --reason <text>, which carries durable context.
For Codex rebuilds, OpenRig also carries a complete supported native permission policy read from the live predecessor's own conversation and process, without adding fresh-launch grants. A later recorded seat permission or policy choice, or a differing declared successor profile, takes precedence. Missing, stale or unsupported observations keep the existing launch behaviour and are reported as unverified in the handover result; requested modes and profiles alone don't prove preservation. The result reports what was emitted, not native successor equivalence.
Adopt it promptly. The saved directory doesn't move the running process, but background work can use it before adoption. Saved configuration isn't the live process's directory, and these examples aren't the only consumers. For a running Claude seat:
- Collector provisioning writes
.openrig/context-collector.cjsand merges.claude/settings.local.jsonin the saved directory, under its existing preservation rules. - Managed compaction, manual (
POST /api/compaction/trigger) or automatic, asks the seat to write its restore map under it. That may be outside the seat's current workspace, so a seat without bypass permissions may meet a write prompt.
Adopting promptly is guidance. It doesn't protect against a poll or a compaction that runs before your next command.
Before adopting, don't rig down/rig up or hand over by fork. Those resume the existing conversation in the
saved directory, and Claude keeps conversations per project directory, so a resume there may not find it.
Cross-directory resume hasn't been run natively; this is the order to follow. rig seat restart --keep-conversation
(Codex) isn't adoption: it respawns in the directory it observes from the running process.
Failures: a refusal, missing or malformed saved startup context, or an audit-write failure leaves the prior
directory saved, and changes no seat, custody or startup file (a refusal may leave the new folder created). A concurrent configuration change is reported, not overwritten. A CLI timeout means the outcome is
unknown; check rig seat status before retrying. Choosing the same directory again records no second event.
The old directory: files there are left as they are, since other seats may still use them. The source RigSpec declaration isn't changed either; update it separately if a future recreation should use the new directory.
The applies_on Field
Each startup file and action specifies when it applies:
fresh_start— delivered when a new conversation starts: the first launch,rig seat launch --fresh,rig seat continue, and seats restored with--freshrestore— delivered when a seat is restored without its earlier conversation: a restore whose resume falls back to a fresh conversation, or a retried start- Default:
[fresh_start, restore](both)
An exact resume delivers neither. When a restore reopens the seat's saved conversation (rig up <rig> --existing with a resume token), OpenRig types no startup actions and delivers no startup files, restore ones included: the conversation already has them, and typing them again would add turns to it. The one exception: a managed guidance block missing from the guidance file is put back before the harness starts.
Use this to avoid re-sending context that a fresh conversation doesn't need twice. For example, a one-time project briefing might only apply on fresh_start, while identity grounding should apply on both, so a seat that comes back fresh gets it again.
What a seat is set to, and what it's running
A seat's settings can disagree with its running agent: what's saved for the next launch, what the running process actually uses, and what its conversation has already read are three separate things. OpenRig reports each one separately. Without live evidence it says unknown (or not verified), never the saved value. A saved change reaches the running seat only through the action in the last column.
| What | Saved for the next launch | What the running seat has | How the running seat gets it |
|---|---|---|---|
| Model | rig seat set-model |
rig ps --nodes shows the configured model beside the last observed one, or UNKNOWN (<reason>) |
The next managed resume or successor launch |
| Reasoning effort | rig seat set-effort |
rig seat effort <seat>: requested, saved, sent at launch, and what the session recorded, which is not verified until it takes a turn |
Its next managed continuation; nothing restarts |
| Permissions | rig seat set-permissions (--mode, --rules, or for Codex --codex-permissions or --codex-profile), shown by rig seat status |
What it launched with; a status read doesn't prove the running process enforces the selection | Its next managed launch or resume (per-seat permission mode) |
| Working directory | rig seat set-cwd, shown by rig seat status as Working directory for future launches |
The directory it started in | rig seat launch --fresh --stop (a blank conversation) or rig handover --source rebuild (durable context); details |
| Startup files and skills | The spec's startup files, skills and actions | What its conversation read when it started; editing a file afterwards doesn't reach it | A fresh launch or a handover delivers them again; for the running seat, send or walk the change (rig walk <seat> --through <files>) |
| Tools (MCP servers, plugins) | Selected fragments, merged into the harness's own config (.mcp.json for Claude, config.toml for Codex) |
Unknown to OpenRig: registered isn't active, Claude asks before it uses a new server, and no OpenRig command reports which tools a running harness loaded | Check in the harness itself (Claude Code's /mcp); a running session may need a restart to pick up newly written config (runtime config disclosure) |
Per harness: Claude Code's and Codex's permission choices are in the setup reference (Claude Code, Codex), Codex homes are below, community harnesses' limits are in their presets, and moving a live seat to the other runtime is in the setup reference.
Deterministic Configuration
The AgentSpec and RigSpec allow declaring deterministic environment configuration:
What the Spec Supports
Hooks ship inside plugins: declare the plugin under resources.plugins[]. AgentSpec validation refuses
resources.hooks. OpenRig loads one plugin's hooks into a seat itself: the shipped openrig-core plugin, for a Claude seat
that selects it (see the refocus channel). The folder a declared plugin is projected into
is not one Claude Code or Codex loads, so a plugin of your own does not get its hooks run by that projection.
Runtime resources (in resources.runtime_resources):
resources:
runtime_resources:
- id: claude-settings
path: runtime/claude-settings.fragment.json
runtime: claude-code
type: claude_settings_fragment
Runtime-specific configuration files projected into the agent's runtime environment.
Startup actions (in startup.actions):
startup:
actions:
- type: send_text
value: "/install-mcp my-server"
phase: after_ready
idempotent: true
Each value is typed into the harness's input and submitted, so it is a prompt or a slash command, not a shell command.
Types are send_text, slash_command and startup_proof (see "Startup proof" above); shell is refused.
idempotent is required, and an action that isn't idempotent must not list restore in applies_on. Both phases
run after the harness is ready: after_files (the default) after the post-launch files, then after_ready.
Don't rename a managed Claude seat. OpenRig starts every Claude seat with --name <session>, on a fresh start and on
every resume, and its own reads of the seat use that name: context usage and managed compaction read the status-line
record Claude saves under it, and the input box is recognized by the name on its border. A startup /rename to another
name breaks those reads until the next resume puts the session name back.
Current Support Matrix
| Capability | Status | Notes |
|---|---|---|
Guidance file projection (guidance_merge) |
Supported | Reliable. Primary delivery mechanism. |
Skill projection (skill_install) |
Supported | Reliable. Skills are copied to workspace. |
send_text delivery after ready |
Supported | Reliable. Requires harness to be ready. |
| Hooks | Through plugins, limited | resources.hooks is refused. OpenRig loads the shipped openrig-core plugin's hooks into a Claude seat that selects it (Claude Code v2.1.280 or later); a plugin you declare under resources.plugins[] is projected, but its hooks are not loaded. |
| Runtime resource projection | Supported for recognized fragments | claude_settings_fragment, claude_mcp_fragment, and codex_config_fragment are applied to provider config. Unknown types are copied to runtime extension directories. |
| Permission configuration | Native settings plus managed launch flags | OpenRig launches Claude with acceptEdits and Codex with workspace-write unless an explicit supported selection changes them; a seat with no policy, selection or named Codex profile also gets the per-launch team default, and the kernel rig's seats get a wider operational default (rig spec, "At launch"). Neither is written to a settings file. Use the first-user permission guide for opt-in and custom choices. |
| MCP installation | Supported for Claude fragments | A selected claude_mcp_fragment is merged into the project's .mcp.json. Claude asks to approve new servers found there; that prompt stops startup as mcp_gate until someone answers it and runs rig seat continue. Otherwise use /mcp or claude mcp add, or describe the servers in startup files for the agent to configure. |
| System dependency installation | Not deterministic | Describe in startup files; agent handles via shell commands. |
| Recurring tasks / wake timers | Runtime-dependent | Claude Code supports recurring tasks via the /loop command. Codex does not have a confirmed equivalent. Orchestrators should include /loop instructions in startup for Claude Code agents. |
The Approach: Describe, Then Let the Agent Handle It
For anything beyond guidance_merge, skill_install, and send_text, the recommended approach is:
- Describe the desired end state in a startup file (e.g., "You need these MCP servers configured, these permissions set, these hooks installed")
- Include a system check in the startup instructions ("Verify your environment: check that X is installed, Y is configured, Z is accessible")
- Empower the agent to self-configure ("If any of these are missing, install/configure them")
- Optionally also declare it in the spec for when deterministic support improves — the spec serves as the blueprint, and the startup file serves as the fallback instruction
This way, when deterministic support becomes fully reliable, the agent will boot up, see that everything is already set up (by the deterministic path), run its system check, confirm everything looks good, and proceed. Until then, the agent reads the instructions and handles the setup itself.
Runtime Config Disclosure
OpenRig performs best-effort deterministic runtime configuration for managed sessions. Core bootstrap stays minimal; user/custom policy belongs in spec-selected runtime resources. These writes are intentionally invasive and should be disclosed plainly:
- Claude global config:
~/.claude/settings.jsonOpenRig no longer writes a coreBash(rig:*)permission allowance here. Older installations may retain one; existing user settings are not removed. - Claude global state:
~/.claude.jsonPurpose: pre-trust managed workspaces and mark onboarding complete for fresh managed sessions. Explicit permission modes use the launch-selectedHOME/.claude.json, or<CLAUDE_CONFIG_DIR>/.claude.jsonwhen that variable is set. Classic startup retains the daemon-home path. - Claude project-local config:
.claude/settings.local.jsonPurpose: apply context collector/activity hooks and selectedclaude_settings_fragmentresources inside the project without committing them to git. - Claude project-local MCP config:
.mcp.jsonPurpose: apply selectedclaude_mcp_fragmentresources for Claude in that project. - Codex global config:
$CODEX_HOME/config.toml, or~/.codex/config.tomlwhen unset Purpose: pre-trust managed workspaces and apply selectedcodex_config_fragmentresources. Codex currently has no equivalent project-local MCP config path for global profile settings. - Global skill folders:
~/.claude/skillsand~/.agents/skillsPurpose: daemon startup seeds theopenrig-skillsandrefocusingskills and the person-facingrigsskill. Each upgrade refreshes therigsskill; a copy installed another way is left as it is.
Two important caveats:
- these writes are best-effort and should still be paired with startup guidance so the local agent can verify and repair them if needed
- already-running adopted sessions may need restart before they pick up newly written config
Permission mode and runtime resource projection are separate. A selected fragment
can affect native configuration, but OpenRig's launch flags can override those
values. Recording a config-surface permission_policy is not proof that its
rules were translated or applied. See permission precedence and limits.
Separate Codex homes for separate installations
Set an absolute CODEX_HOME in the environment that starts the daemon when
you want a separate Codex configuration and state root. OpenRig uses that root
for its startup hook setup/removal and trust records, feature flag, workspace
trust and selected config fragments. Startup can write those files even when
the daemon has no seats.
The explicit selection also applies to future managed Codex fresh, resume and fork launches, their capability/profile/configuration probes, native thread and context reads, and plugin-cache discovery. Launch commands reassert it after pane shell startup, so an rc file cannot silently switch that launch to another Codex home. Executable and PATH selection stay the same. Existing running sessions are not moved or restarted; a thread in another home is not searched as a fallback for an explicitly selected home.
With CODEX_HOME unset, a second install still shares ~/.codex. Setting only
OPENRIG_HOME separates OpenRig state, not provider configuration. The default
launch behavior is unchanged, including existing pane-shell overrides.
OpenRig does not copy or migrate authentication, history, rules, trust or
settings into the selected home. A separate file-backed home needs its intended
authentication provisioned if absent; OS credential stores and other config
layers can behave differently, so a new login is not universally required.
Explicit Codex auth commands resolve their caller's CODEX_HOME, otherwise
$HOME/.codex; contacting a daemon with OPENRIG_URL does not retarget auth
save/switch/registry writes to that daemon's selection. Set the intended root
on those commands as well.
This separates provider files, not OS users or accounts. Project-local resources still use the selected project directory; shared project and system settings remain shared.
Codex guidance at launch
On a fresh launch of Codex 0.164.0 or later, OpenRig passes a seat's managed guidance (OpenRig's start file and the
rig's and project's context, culture and SOP files) with the launch, as -c developer_instructions=…, instead of
merging it into the repository AGENTS.md. Neither the tracked AGENTS.md nor the Codex home is written for it, and
both still load as usual. The text opens: "OpenRig seat guidance for this session follows. Where it conflicts with
AGENTS.md guidance in the repository or the Codex home, follow AGENTS.md."
A launch value replaces a configured one, so this happens only when no Codex config layer OpenRig reads sets
developer_instructions. OpenRig reads:
/etc/codex/config.tomland/etc/codex/managed_config.toml;- the Codex home's
config.toml, profiles included, and the selected profile's<profile>.config.toml; - each
.codex/config.tomlfrom the seat's working directory up.
The Codex home it reads is the seat's own (a CODEX_HOME in its env:, or a recorded account switch), else the
daemon's CODEX_HOME, else ~/.codex.
The guidance still goes to the repository AGENTS.md, as before, when:
- a layer OpenRig reads sets
developer_instructions, or can't be read. A launch warning names the file; - the launch resumes or forks a conversation. Developer instructions on a resume haven't been tested, and an exact
resume puts the guidance back in the repository
AGENTS.md; - the seat is restored, even when the restore starts a new conversation. A restore keeps the guidance file its launch
recorded, which for Codex is the repository
AGENTS.md; - a handover successor starts (fresh, fork or rebuild). Its launch carries no guidance of its own, so the handover
first puts the seat's missing blocks back in the repository
AGENTS.mdfrom its saved startup context; - Codex is older than 0.164.0, or its version is unknown.
A seat's role still arrives as startup text, and the seat-skills line, after the person's yes, still goes to
AGENTS.md.
If your shell sets CODEX_HOME. When neither the seat nor the daemon declares a CODEX_HOME, a seat's pane keeps
its own, and OpenRig reads ~/.codex. A CODEX_HOME that only your shell startup sets (in ~/.zshrc, for example),
with the daemon started without it, isn't seen. A developer_instructions set in that home is then replaced by the
launch value for the session. To have it checked, declare that same absolute path as CODEX_HOME in the member's or
rig's env: (see Launch environment, "A seat's own account"). OpenRig then reads
that home at the seat's next fresh launch, and a developer_instructions set there keeps the repository AGENTS.md.
The seat keeps the same home and login.
What it doesn't cover. OpenRig doesn't read Codex's packaged defaults, or MDM- or enterprise-managed settings. A
developer_instructions set there may be replaced for the session. Legacy MDM-managed config ranks above launch flags,
so it can override OpenRig's guidance, and the seat then gets none of it.
Not established. The order Codex assembles developer instructions and AGENTS.md in, and how it settles every
conflict, aren't established; the opening sentence asks it to follow AGENTS.md. The native behaviour was observed on
Codex 0.164.0 alone, on fresh launches without tools.
Runtime Differences
Claude Code:
- Reads from
CLAUDE.mdand.claude/directory - Recurring tasks via the
/loopcommand (e.g.,/loop 5m "check rig health") — this is NOT hooks; hooks are event-driven - MCP server management via
/mcpinteractive command orclaude mcp addfrom CLI - Event-driven hooks system in
.claude/settings.json— reacts to events likePreToolUse,PostToolUse,SessionStart, etc. (not time-based) - Permission allowlisting via
.claude/settings.json(permissions.allow,permissions.deny,permissions.defaultMode) - Can self-configure MCP servers, permissions, and hooks from startup instructions
Codex:
- Reads from
AGENTS.mdand.agents/directory - Recurring task support is limited — no confirmed equivalent of Claude Code's
/loopcommand - MCP configuration mechanism differs from Claude Code
- Approval policy and sandbox access are separate controls. OpenRig's default
-s workspace-writeselects the sandbox; it does not force-a. A member'scodex_config_profileselects native-p, distinct from the AgentSpecprofile. - On that plain
-s workspace-writelaunch (fresh, resume, fork or restore), a short-livedcodex app-serverfirst reads Codex's own configuration with the seat's executable, home and working directory. OpenRig adds-c sandbox_workspace_write.network_access=trueonly when no configuration layer sets network access and no managed requirement could restrict it. A timeout, error or unrecognized answer adds nothing. Named profiles and full bypass are not read. The read may write Codex's own state files inCODEX_HOME, read its login and fetch managed policy, as a Codex start does. - Managed launches pass
-c check_for_update_on_startup=false, so Codex skips its startup update check; your Codex config is not changed. - Can self-install dependencies from instructions but timer/recurring behavior is not reliably available
Pi and OMP:
- Read
AGENTS.mdin the working directory, added to Pi's own system prompt. Pi also reads the one in its agent directory: on a fresh launch, a Pi seat's guidance is merged there, under OpenRig's state, not into the repository's. When that file or its directory is a link, the repositoryAGENTS.mdis used instead. A restore keeps the file the seat launched with. OMP keeps its guidance in the repositoryAGENTS.md - Selected skills are projected into the seat's own agent directory under OpenRig's state, not into a shared workspace folder
For Pi tool-selection diagnosis and deliberate in-place recovery, see Pi tool-selection recovery. It distinguishes configured, currently intended, registered and active tools, including the TUI/RPC boundary.
Terminal seats (builtin:terminal) get no agent or profile layers and no startup proof.
Claude compaction preparation directory
Managed Claude compaction asks the seat to write a restore map before /compact. By default,
OpenRig uses <cwd>/.openrig/compaction/preparation/, falling back to its instance home when
that directory cannot be prepared. A filesystem write check cannot establish whether Claude's
file-edit permissions allow the seat to write there.
If the launch workspace is denied by Claude's permissions, select an absolute preparation root:
rig config set policies.claude_compaction.preparation_root /absolute/path/to/restore-maps
Grant the seat file-edit access to that root in its Claude configuration. OpenRig does not change
Claude permissions; choose a directory outside any matching deny rule. The corresponding environment
variable is OPENRIG_POLICIES_CLAUDE_COMPACTION_PREPARATION_ROOT.
New manual and automatic attempts use <root>/<session>/<attempt-id>/RESTORE-MAP.md; an in-flight
attempt keeps its original path through the post-compaction restore request. The root gets an
exclusive .gitignore containing *, and the daemon publishes the marked .tmp file with an
atomic rename. Use a dedicated root: a conflicting
ignore file or an unwritable configured directory stops preparation with
preparation_directory_unavailable, visible in rig compact <session> --state, without choosing
another location. Fix the directory and explicitly retry. --skip-map still skips the map once.
Reset with rig config reset policies.claude_compaction.preparation_root to restore the default.
The setting applies to new attempts without restarting the daemon.
The managed /compact command keeps its 600-character budget. When the selected map path or
the attempt's compaction instruction does not fit, the existing restore request carries it;
it never substitutes a newer preparation root or instruction. An explicitly selected root's
published map is also named in that request when the path fit in /compact. The default root
keeps the existing command/restore handoff. Under --skip-map, restore still recognizes an
optional map completed at that attempt's path, or says that no map was required.
The compaction preparation and command-submission tests cover manual and automatic attempts, long paths and instructions, settings changed during preparation, directory failures and late maps after cancellation or expiry. They use synthetic delivery and filesystem fixtures; they do not prove a live Claude session's permission choices or compaction behavior.
Codex compaction and sizing
OpenRig's managed compaction is for Claude Code seats only, by design. That covers rig compact, the
policies.claude_compaction.* automatic policy, and the watchdog jobs that the managed-compaction and
apprentice-handover strategies create. A Codex seat compacts with Codex's own automatic compaction, and OpenRig
leaves that to Codex.
rig compact <session>refuses a Codex seat: "not a Claude (claude-code) seat".rig compact-planlists a Codex seat as skipped (codex_not_managed_by_claude_compact_in_place).- The automatic policy never acts on a Codex seat.
- A Codex member with
compaction_strategy: managed-compactionorapprentice-handovergets no watchdog jobs.
What Codex compacts at. Codex describes model_auto_compact_token_limit as the "token threshold that triggers
automatic history compaction (unset uses model defaults)". Its configuration reference does not say what those
defaults are, and the model catalog's auto_compact_token_limit is null for the models we checked in Codex 0.162.1.
Measure a seat's own compactions before relying on an unset limit.
What window the model has. codex debug models prints the catalog. Each model carries context_window,
max_context_window and effective_context_window_percent. In Codex 0.162.1, gpt-5.6-sol has a context_window
of 272000 at 95%, which is 258400. That is the model_context_window its rollout token_count events report.
A limit only matters when it is below that effective window. Leave model_context_window alone: don't raise it toward
max_context_window to avoid compaction. Set the limit instead.
Setting the limit for seats only. Put model_auto_compact_token_limit in a Codex profile file,
$CODEX_HOME/<name>.config.toml (~/.codex/<name>.config.toml when CODEX_HOME is unset), and name it on each
Codex member that should use it:
# ~/.codex/seat-compaction.config.toml
model_auto_compact_token_limit = 150000 # choose your own value
codex_config_profile: seat-compaction
OpenRig passes -p seat-compaction on that seat's fresh, fork and resume launches. Codex layers the profile file on
top of the base user config, so only seats that name the profile see the lower limit. A Codex session started without
that profile keeps the base config.toml in the same Codex home ($CODEX_HOME/config.toml, or ~/.codex/config.toml
when CODEX_HOME is unset). The 150000 is an example, not a recommendation.
With the normal launch mode, codex_config_profile replaces OpenRig's explicit workspace-write sandbox flag, so the
profile must set the sandbox and approvals you want. See
permission precedence and limits, which also gives Codex's order
of precedence between a profile, project settings and CLI overrides.
The trade-off. Every model call carries the whole context so far, so a smaller context makes each call cheaper. The cost is that Codex compacts sooner and more often, so more of the conversation is carried as a summary instead of verbatim. A higher limit keeps more detail and makes each call larger.
Related keys. These also shape compaction. This guide doesn't recommend values for them. See Codex's configuration reference:
| Key | What Codex says it does |
|---|---|
model_auto_compact_token_limit_scope |
total (the default) counts the full active context; body_after_prefix counts only growth after the carried compaction-window prefix. |
compact_prompt |
Inline override for the history compaction prompt. |
experimental_compact_prompt_file |
Loads the compaction prompt override from a file (experimental). |
tool_output_token_limit |
Token budget for storing individual tool or function outputs in history. |
When authoring startup content, note which instructions are runtime-specific. For example, an orchestrator that needs a monitoring loop should include instructions like: "If running Claude Code, use /loop 3m to periodically check rig health. If running Codex, check rig health at the start of each task cycle instead."
Patterns and Anti-Patterns
Good Patterns
Start with role + culture + one skill
agent.yaml → guidance/role.md
rig.yaml → culture_file: CULTURE.md
profile → uses.skills: [openrig-user]
This is the minimum effective startup. The agent knows who it is, how the team works, and how to use the rig.
For seats sharing a working directory, keep each role in its own AgentSpec startup entry:
startup:
files:
- path: guidance/role.md
orientation: role
delivery_hint: send_text
required: true
rig queue whoami --json reports only the calling seat's explicitly marked role
bindings, independently of its current work. The role.state is no-record,
not-declared, missing, present, or unknown when identity, storage or the
observation is unavailable. Files retain their original path, absolutePath
and ownerRoot plus the currently resolved resolvedPath and
resolvedOwnerRoot. Built-in packaged paths follow the running installation;
existing development-checkout paths stay where they were recorded.
recordedAt is the startup-context record write/attempt, which happens before
launch succeeds. Exact resume can retain an older record. present means the
resolved file currently exists, not that its bytes still match the spec or that
the agent read it. Unmarked startup files and startup actions are not exposed.
Refocus includes the role path and a re-read reminder with topology or both trees,
even if the trace fails. Work-only mode omits it; disabling refocus still disables
the entire hook. Custom refocus prose keeps its existing precedence. The hook
reuses its work lookup, or makes one bounded lookup when the work node is explicit;
a failed or skipped lookup reports unknown.
The shipped kernel seats no longer project their role into shared guidance. Existing user files and legacy role blocks are left intact; this is not a cleanup of already-installed shared blocks. General user-authored guidance projection is unchanged.
The other shipped agent specs that start a seat with guidance/role.md mark that
file orientation: role too, so rig queue whoami --json reports their role and
refocus names it. They never projected a role into shared guidance; their startup
delivery is unchanged.
Removing a leftover shared role block by hand. A kernel installed before 0.6.5
may still hold one seat's old role in its working directory's CLAUDE.md (or the
rig's configured managed_blocks file) or AGENTS.md. Upgrading doesn't remove it.
rig down removes OpenRig's managed blocks from that file when no other running
rig uses it, so it stays until then. To remove it sooner, by hand:
- Find the exact block: the lines from
<!-- BEGIN OpenRig MANAGED BLOCK: role -->through<!-- END OpenRig MANAGED BLOCK: role -->(in older files,RIGGEDinstead ofOpenRig). Only the block whose id is exactlyrole; leaverig-role, other ids and everything outside the markers alone. - Confirm each seat that shares that directory has its per-seat replacement:
rig queue whoami --jsonrun in the seat reportsrole.stateaspresent. A seat started before it was upgraded still has the old content in its conversation; restart it fresh if you want it to start from the per-seat role. - Keep a copy of the file, remove only those lines, and compare the result with the copy: nothing else in the file should differ.
The kernel's own folder. A newly created default kernel works in
<workspace.root>/kernel, beside your projects (<workspace.root>/projects by
default) rather than above them. Claude reads CLAUDE.md from a seat's folder
and from every folder above it, so a kernel working in the workspace root put its
guidance into every Claude project seat below it. If that folder can't be made,
the new kernel works in the workspace root, as before.
A kernel that already exists keeps its folders, usually workspace.root.
Upgrading moves no seat, changes no restored conversation and removes no file.
To move an existing kernel yourself, if you want to:
- Save the new folder for each kernel seat with
rig seat set-cwd <seat> --cwd <workspace.root>/kernel --reason <text>, using the absolute path. It creates the folder (details). - Choose when each seat adopts it. A seat keeps its current folder until it
starts again, and the new start doesn't continue its current native
conversation:
rig seat launch <seat> --fresh --stop --reason <text>starts a blank one, andrig handover <seat> --source rebuild --reason <text>carries durable context into a new one. Nothing does this for you. - Tidy the old
<workspace.root>/CLAUDE.md(orAGENTS.md) by hand, once no running rig's seat still works in that folder: a seat there still reads those blocks, andrig downchecks the same before it removes them. Remove only OpenRig's managed blocks, each from its<!-- BEGIN OpenRig MANAGED BLOCK: … -->line through itsENDline, as in the steps above, and keep everything else, including your own text. Don't move or delete the whole file.
Separate project context from role
Don't put project documentation inside the role guidance. The role is about the agent's function; project context is about what the agent is working on. Use rig-level or pod-level startup files for project context.
Tell the agent about its skills explicitly
In a startup file or guidance, include a line like:
You have the following skills loaded: openrig-user, test-driven-development, systematic-debugging. Use them.
This prompts the agent to actually invoke the skills, not just have them as passive context.
Include a system check in startup context
## System Check
After identity recovery, verify:
1. `rig ps --nodes` shows your rig running (scoped to your session's rig by default; outside a managed session name it explicitly: `rig ps --nodes --rig <name>`)
2. `rig env status <rig>` shows services healthy (if applicable)
3. Your working directory is correct
4. Required tools are available (node, npm, git, etc.)
If anything is missing, fix it before starting work.
Anti-Patterns
Dumping everything into one giant CLAUDE.md
Don't. Use the layering model. Role goes in the agent spec. Culture goes in the rig spec. Project context goes in rig/pod startup files. If everything is in one file, you can't reuse any of it.
Duplicating skill content in guidance files
If you find yourself copying text from a skill into a guidance file, stop. Reference the skill instead. Skills are projected; guidance should point to them, not duplicate them.
Using send_text for content that should be pre-loaded
If the agent needs to know something BEFORE it starts reasoning, use guidance_merge (delivered before boot), not send_text (delivered after boot). send_text is for instructions the agent should process as a first task, not for foundational context.
Over-specifying member-level startup
If every member has a large startup block, the rig spec becomes a configuration dump. Refactor shared content up to the pod level. Member-level startup should be small overrides, not complete agent briefings.
Relying on deterministic hooks for critical setup
If your rig REQUIRES a hook to function and the hook installation fails silently, the agent won't know something is wrong. Always pair deterministic config with a startup instruction or system check that verifies the result.
Authoring Checklist
When creating a new agent's startup experience:
- Write a
guidance/role.md— who is this agent? - Reference it in
startup.fileswithorientation: roleanddelivery_hint: send_text. Avoid projecting distinct seat roles into one sharedAGENTS.mdorCLAUDE.md. - Write a rig
CULTURE.mdif the rig doesn't have one - Choose skills from the shared pool via profile
uses - Write a
startup/context.mdif the agent needs environment grounding - Include a system check in the startup context
- Verify the agent spec validates:
rig agent validate agent.yaml - Verify the rig spec validates:
rig spec validate rig.yaml - Run the advisory authoring check:
rig spec audit rig.yaml - Test by launching the rig and checking that the agent received the expected content