Configuring an agent’s identity and rules across harnesses
Status: proposed, not yet implemented. This guide documents the target design from
docs/research/cross-harness-identity-and-rules.md— written before the code, as a way to sanity-check the design by describing it the way a user would experience it. Nothing below works today. See that spec for the research this is based on, anddocs/research/harness-rules-directory-support.mdfor the per-harness native-support findings.
Why this exists: containerize configuration, not storage
Everything in this guide serves one rule: your conversations, usage, and stats must persist and
stay coherent no matter which stack (tool configuration) you ran — switching from
claude_dev-core to claude_dev-methodology shouldn’t fragment your Claude history into two
silos, because both mount the same host history, keyed by project path, not by stack name. The
same idea makes team-shared configurations possible: a stack (its recipes, rules, and the
instructions: file this guide introduces) is the shareable, version-controlled unit a team
standardizes on — your auth and history stay strictly personal and host-linked, never baked into
that shared config. instructions:/rules: isolate what tools and identity this stack gives the
agent; they never touch where your conversations live.
If you’re authoring a stack, watch for
state.session_state: volume. It’s a valid sibling of the defaulthostin the schema, but it does the opposite of everything above — it isolates session state per-stack instead of sharing your host history. Fine if you deliberately want a throwaway, non-continuous stack; a real footgun if it ends up in a team-shared config by accident (e.g. copy-pasted from an example). See the stacks guide for the full field.
The two-tier model
Two different things get configured here, owned by two different authors:
- Identity — “what is this assembled agent, as a whole.” One per stack, written once by whoever builds the stack. This is CLAUDE.md’s role today; the goal is the same thing for every harness.
- Rules — topic-scoped, composable instructions. Contributed by whichever recipes are in the
stack, N per stack. Already shipped as the
rules:recipe field — this guide extends it to more harnesses, it doesn’t replace it.
flowchart TD
subgraph recipes["Recipes in the stack"]
R1["beads recipe<br>rules/bd-usage.md"]
R2["superpowers recipe<br>rules/workflow.md"]
end
subgraph stack["Stack author"]
SI["stack.yaml<br>instructions: identity.md"]
end
R1 --> Compose["Composed rules<br>.claude/rules/*.md"]
R2 --> Compose
SI --> Identity["Stack identity<br>.claude/CLAUDE.md"]
Compose --> Build["harnessed build"]
Identity --> Build
Build --> Derived["Per-harness derived files<br>(one path per target harness)"]
classDef recipe fill:#dfe6e9,stroke:#636e72
classDef stack fill:#d4edda,stroke:#28a745
class R1,R2,Compose recipe
class SI,Identity stack
Trying to solve both with one mechanism is what caused the original design confusion — “just append everything to CLAUDE.md” breaks down the moment two recipes want to append to it. Rules compose (fail-fast on name collision); identity doesn’t — it’s one file, one owner.
Declaring stack-level identity
A new instructions: field on the stack manifest, pointing at one markdown file relative to the
stack’s own directory:
# catalog/stacks/claude_superpowers/stack.yaml
name: claude_superpowers
harness: claude
recipes: [superpowers]
instructions: identity.md
<!-- catalog/stacks/claude_superpowers/identity.md -->
You are a focused pair-programmer for this repo. Prefer small, reviewable diffs. Always run the
test suite before declaring a task done. When in doubt about scope, ask rather than guess.
This is deliberately a single file, not a list — identity isn’t composable the way rules are. If
you need different identities for different harnesses of the “same” stack, author separate stack
manifests (claude_myproject, omp_myproject, …) each with their own instructions:.
Declaring recipe-level rules
Unchanged in shape from today’s rules: field — a recipe ships flat .md files, each one a
composable topic:
# catalog/recipes/beads/recipe.yaml
rules:
- {path: rules/bd-usage.md}
(This guide also covers the fix to how these fan out — see Known limitations below for the flat-file correction.)
Per-harness resolution
Not every harness has a literal “modify the system prompt” primitive. Where one exists that adds to the default behavior, it’s used directly. Where the only option replaces the harness’s built-in behavior wholesale, that’s treated as an opt-in escape hatch, not the default — the same caution a stack author should apply themselves before reaching for it.
flowchart LR
Start{"Does the harness have a<br>system-prompt hook?"}
Start -->|"No hook exists"| Fallback["Append into the harness's<br>own global instructions file"]
Start -->|"Yes — additive"| Additive["Use it directly"]
Start -->|"Yes — but full-replace"| Replace["Opt-in escape hatch only,<br>not the default target"]
Fallback --> Claude["claude → .claude/CLAUDE.md<br>(the only surface it has)"]
Fallback --> CodexD["codex → ~/.codex/AGENTS.md<br>(default)"]
Additive --> Omp["omp → ~/.omp/agent/APPEND_SYSTEM.md<br>(guarded, shared host file)"]
Replace --> CodexR["codex → model_instructions_file<br>(only if explicitly requested)"]
Start -->|"Requires a launch-time change"| OC["opencode → custom agent + --agent flag"]
Start -->|"Bake alongside existing MCP config"| AG["antigravity → gemini.md / settings.json"]
classDef additive fill:#d4edda,stroke:#28a745
classDef caution fill:#fff3cd,stroke:#ffc107
classDef fallback fill:#dfe6e9,stroke:#636e72
class Additive,Omp additive
class Replace,CodexR caution
class Fallback,Claude,CodexD,OC,AG fallback
| Harness | Identity target | Rules target | Notes |
|---|---|---|---|
| claude | .claude/CLAUDE.md | .claude/rules/*.md (native, conditional via paths:) | The reference implementation — everything else approximates this |
| omp | ~/.omp/agent/APPEND_SYSTEM.md (guarded append) | ~/.omp/agent/RULES.md (guarded append) | Shared across every omp stack on this host, not stack-scoped — see limitations |
| codex | ~/.codex/AGENTS.md (default) | Same file, concatenated in | model_instructions_file available but not default (full-replace) |
| opencode | Custom agent in opencode.json, launched via --agent <name> | instructions: glob at an absolute container path | Confirmed safe — a custom agent with only a prompt field inherits default permissions |
| antigravity | gemini.md / settings.json’s context.fileName | .agents/rules/*.md (native, flat files) | Baked alongside the existing mcp_config.json, same mechanism |
Build-time flow
Everything except omp’s guarded append happens at harnessed build — generated once, written into
the stack’s profile, no container involved yet:
sequenceDiagram
participant Author as Stack author
participant CLI as harnessed build
participant Assembler
participant Profile as Profile dir (host, per-stack)
Author->>CLI: harnessed build my-stack
CLI->>Assembler: load stack.yaml + every recipe
Assembler->>Assembler: fan recipe rules -> .claude/rules/*.md
Assembler->>Assembler: render stack instructions -> .claude/CLAUDE.md
alt harness is codex
Assembler->>Profile: write ~/.codex/AGENTS.md (identity + concatenated rules)
else harness is opencode
Assembler->>Profile: merge a custom agent into opencode.json
else harness is antigravity
Assembler->>Profile: bake settings.json + a flat identity.md
end
Assembler->>Profile: write the rest of the profile tree
CLI-->>Author: build complete
Launch-time flow — the omp exception
omp’s target files (~/.omp/agent/{APPEND_SYSTEM,RULES}.md) aren’t profile-scoped — they’re the
real host directory, shared with your native host omp and every other omp-harness stack
(_omp_agent_mount). Writing them can’t happen at build time into a per-stack profile; it happens
at launch, directly on the host, guarded so repeated launches don’t duplicate content:
sequenceDiagram
participant User
participant CLI as harnessed <stack>
participant Host as Host filesystem (~/.omp/agent/)
participant Container
User->>CLI: harnessed my-omp-stack
alt harness is omp
CLI->>Host: check for this stack's delimiter block
alt block already present
CLI->>Host: no-op
else block missing or stale
CLI->>Host: write/replace the block, markers intact
end
Note right of Host: <!-- harnessed:stack:my-omp-stack --><br/>...content...<br/><!-- /harnessed -->
end
CLI->>Container: mount the profile's .claude/, compose the pod
Container-->>User: attach
Known limitations
These are accepted tradeoffs, not bugs to file later:
- No conditional rule loading outside Claude. Claude Code rules can carry
paths:frontmatter to load only when a matching file enters context. Every other harness gets the concatenated rules always-on — a rule scoped to*.pyfiles becomes globally active forcodex/omp/etc. This is a deliberate, permanent limitation. - omp’s identity/rules are host-shared, not per-stack. Multiple omp-harness stacks (and your
native host omp) all read the same
~/.omp/agent/{APPEND_SYSTEM,RULES}.md. The guarded-append markers keep different stacks’ blocks from clobbering each other, but the content still lives in one shared file — there’s no container-level isolation the way.claude/CLAUDE.mdhas. - Codex has a 32 KiB instruction budget (
project_doc_max_bytes), shared between identity and concatenated rules if both land inAGENTS.md. A stack with a long identity file and many recipe-contributed rules can silently truncate — files closer to the cap get cut, not warned about. - The Claude Code
rules:fan-out had a real bug this design also fixes: rules are flat.mdfiles (.claude/rules/<name>.md), not one directory per rule — the originalrules:implementation copy-pasted theskills:directory-fan pattern without checking the shape. Fixed as part of landing this design, independent of everything else here.
Worked example
Starting from the existing claude_superpowers stack, adding an identity file and a second
harness:
# catalog/stacks/claude_superpowers/stack.yaml — unchanged shape, one new field
name: claude_superpowers
harness: claude
recipes: [superpowers]
instructions: identity.md
# catalog/stacks/omp_superpowers/stack.yaml — same identity content, different harness
name: omp_superpowers
harness: omp
recipes: [superpowers]
instructions: identity.md
Building both:
harnessed build claude_superpowers # writes .claude/CLAUDE.md into this stack's profile
harnessed build omp_superpowers # writes .claude/CLAUDE.md into this stack's profile too
harnessed omp_superpowers # LAUNCH TIME: guarded-appends identity.md's content into
# ~/.omp/agent/APPEND_SYSTEM.md, then attaches
Note the asymmetry: claude_superpowers’s identity is fully self-contained in its own profile.
omp_superpowers’s identity only actually reaches the agent at launch, and lands in a file
shared with every other omp stack you run — that’s the tradeoff called out above, made concrete.
See also
docs/research/cross-harness-identity-and-rules.md— the full design spec, including the research this guide simplifies for a user-facing audience.docs/research/harness-rules-directory-support.md— per-harness native rules-directory research.- Recipe-authoring guide — the
rules:field, recipe-side. - Stacks guide — the
stack.yamlschema this addsinstructions:to.