
Shared
- 1.2k installs
- 416 repo stars
- Updated August 5, 2026
- boshu2/agentops
shared is an internal AgentOps reference skill with contracts and CLI guides for other skills.
About
The shared AgentOps skill is an internal library tier reference directory consumed by other AgentOps skills rather than a directly invocable workflow. It provides validation-contract.md for accepting spawned work, Claude Code feature contracts, and backend spawn examples for Claude native teams, Codex subagents, background tasks, and inline single-agent mode. Additional references document verified Claude and Codex CLI command shapes plus a dated failure log with mitigations from live runs. Skills including council, crank, swarm, research, and implement load these JIT reference documents when needed. CLI availability patterns require graceful degradation when external CLIs are absent, treating inline mode as the baseline rather than a degraded fallback.
- Internal reference library; not directly user-invocable.
- validation-contract.md defines spawn work acceptance criteria.
- Backend references for Claude teams, Codex subagents, and background tasks.
- Verified CLI command shapes and dated failure mitigations.
- Consumed JIT by council, crank, swarm, research, and implement skills.
Shared by the numbers
- 1,219 all-time installs (skills.sh)
- +25 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #229 of 1,879 Documentation skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/boshu2/agentops --skill sharedAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.2k |
|---|---|
| repo stars | ★ 416 |
| Security audit | 3 / 3 scanners passed |
| Last updated | August 5, 2026 |
| Repository | boshu2/agentops ↗ |
What shared AgentOps contracts and CLI references apply to this spawn workflow?
Load shared AgentOps reference contracts for validation, CLI commands, and spawn backends.
Who is it for?
AgentOps skills needing validation contracts or CLI spawn backend guidance.
Skip if: Skip for direct end-user invocation; it is an internal library skill.
When should I use this skill?
Another AgentOps skill needs validation-contract or backend CLI reference docs.
What you get
Correct validation contract and backend spawn reference loaded for the calling skill.
- Multi-agent spawn prompt templates
- Council verdict markdown files
- Validated task_id polling handles
By the numbers
- Bundles 3 multi-agent backend reference documents plus validation and CLI failure logs
- Documents 5 documented limitations of background Task fallback spawns
Files
Shared References
This directory contains shared reference documents used by multiple skills:
validation-contract.md- Verification requirements for accepting spawned workreferences/claude-code-latest-features.md- Claude Code feature contract (slash commands, agent isolation, hooks, settings)references/backend-claude-teams.md- Concrete examples for Claude native teams (TeamCreate+SendMessage)references/backend-codex-subagents.md- Concrete examples for Codex CLI and Codex sub-agentsreferences/backend-background-tasks.md- Fallback:Task(run_in_background=true)references/backend-inline.md- Default single-agent mode (no spawn) — the baseline, not a degradationreferences/claude-cli-verified-commands.md- Verified Claude CLI command shapes and caveatsreferences/codex-cli-verified-commands.md- Verified Codex CLI command shapes and caveatsreferences/cli-command-failures-2026-02-26.md- Dated failure log and mitigations from live runs
These are not directly invocable skills. They are loaded by other skills (council, crank, swarm, research, implement) when needed.
---
CLI Availability Pattern
All skills that reference external CLIs MUST degrade gracefully when those CLIs are absent.
Check Pattern
# Before using any external CLI, check availability
if command -v bd &>/dev/null; then
# Full behavior with bd
else
echo "Note: bd CLI not installed. Using plain text tracking."
# Fallback: use TaskList, plain markdown, or skip
fiFallback Table
| Capability | When Missing | Fallback Behavior |
|---|---|---|
bd | Issue tracking unavailable | Use TaskList for tracking. Note "install bd for persistent issue tracking" |
ao | Knowledge flywheel unavailable | Write learnings to .agents/learnings/ directly. Skip flywheel metrics |
out-of-session substrate (ntm / ao agent) | Always-on orchestration unavailable | Run the loop in-session (/rpi, /evolve). A substrate (an NTM tmux swarm or managed-agents via ao agent) only adds always-on dispatch of whole operating-loop or /evolve skill runs — see agent-native and docs/3.0.md |
gt | Workspace management unavailable | Work in current directory. Skip convoy/sling operations |
gh | PR/CI automation unavailable | Open PRs via the web UI; skip automated PR status/merge steps |
go | Build-from-source unavailable | Install a prebuilt ao (Homebrew / install script / release binary); no Go needed |
codex | CLI missing or model unavailable | Fall back to runtime-native agents. Council pre-flight checks CLI presence (which codex) and model availability for --mixed mode. |
cass | Session search unavailable | Skip transcript search. Note "install cass for session history" |
jq | JSON parsing unavailable | Read --json output manually or use non-JSON output modes |
rg (ripgrep) | Fast search unavailable | Fall back to grep / git grep (slower) |
| Model tier config | .agentops/config.yaml missing | Use built-in defaults (quality=opus, balanced=sonnet, budget=haiku). Tier resolution falls through to "balanced". |
Full per-tool purpose, required-vs-optional, and fallback detail: docs/dependencies.md. The README "Requirements" section summarizes; this doc page is the canonical detail.
Required Multi-Agent Capabilities
Council, swarm, and crank require a runtime that provides these capabilities. If a capability is missing, the corresponding feature degrades.
| Capability | What it does | If missing |
|---|---|---|
| Spawn subagent | Create a parallel agent with a prompt | Cannot run multi-agent. Fall back to --quick (inline single-agent). |
| Agent-to-agent messaging | Send a message to a specific agent | No debate R2. Workers run fire-and-forget. |
| Broadcast | Message all agents at once | Per-agent messaging fallback. |
| Graceful shutdown | Request an agent to terminate | Agents terminate on their own when done. |
| Shared task list | Agents see shared work state | Lead tracks manually. |
Every runtime maps these capabilities to its own API. Skills describe WHAT to do, not WHICH tool to call.
After detecting your backend (see Backend Detection below), load the matching reference for concrete tool call examples:
| Backend | Reference |
|---|---|
| Claude feature contract | skills/shared/references/claude-code-latest-features.md |
| Claude Native Teams | skills/shared/references/backend-claude-teams.md |
| Codex Sub-Agents / CLI | skills/shared/references/backend-codex-subagents.md |
| Background Tasks (fallback) | skills/shared/references/backend-background-tasks.md |
| Inline (no spawn) | skills/shared/references/backend-inline.md |
Backend Detection
Use capability detection at runtime, not hardcoded tool names. The same skill must work across any agent harness that provides multi-agent primitives. If no multi-agent capability is detected, degrade to single-agent inline mode (--quick).
Selection policy (NTM > runtime-native > beads floor):
Global opt-out first: if AGENTOPS_ORCHESTRATION=off is set, skip all spawn backends and degrade to the beads floor (single-agent inline / --quick; workers' work is tracked through bd). This mirrors the AGENTOPS_HOOKS_DISABLED=1 convention. Otherwise, select in this order:
1. NTM (top tier). If ntm is on PATH, capability-probe it with ntm --robot-capabilities. When the probe confirms multi-agent primitives, use NTM as the primary backend. 2. Runtime-native. If NTM is unavailable: in a Claude session with TeamCreate/SendMessage, use Claude Native Teams; in a Codex session with spawn_agent, use Codex sub-agents. If both are technically available, pick the backend native to the current runtime unless the user explicitly requests mixed/cross-vendor execution. Only use background tasks when neither native backend is available. 3. Beads floor. If no multi-agent capability is detected, degrade to single-agent inline mode (--quick).
`gc` is NOT a selectable tier. AgentOps no longer references Gas City; out-of-session orchestration is delegated to a swappable substrate (NTM + MCP + managed-agents — see docs/3.0.md). Any residual gc-based dispatch prose in older swarm/crank reference files is historical only and is never selected.Output-contract parity is unchanged across all tiers: workers write results to .agents/swarm/results/*.json, and the lead verifies-then-trusts those artifacts. This invariant holds whether the backend is NTM, a runtime-native team, or the beads floor.
| Operation | Codex Sub-Agents | Claude Native Teams | OpenCode Subagents | Inline Fallback |
|---|---|---|---|---|
| Spawn | spawn_agent(message=...) | TeamCreate + Task(team_name=...) | task(subagent_type="general", prompt=...) | Execute inline |
| Spawn (read-only) | spawn_agent(message=...) | Task(subagent_type="Explore") | task(subagent_type="explore", prompt=...) | Execute inline |
| Wait | wait(ids=[...]) | Completion via SendMessage | Task returns result directly | N/A |
| Retry/follow-up | send_input(id=..., message=...) | SendMessage(type="message", ...) | task(task_id="<prior>", prompt=...) | N/A |
| Cleanup | close_agent(id=...) | shutdown_request + TeamDelete() | None (sub-sessions auto-terminate) | N/A |
| Inter-agent messaging | send_input | SendMessage | Not available | N/A |
| Debate (R2) | Supported | Supported | Not supported (no messaging) | N/A |
OpenCode limitations:
- No inter-agent messaging — workers run as independent sub-sessions
- No debate mode (
--debate) — requires messaging between judges --quick(inline) mode works identically across all backends
Backend Capabilities Matrix
Prefer native teams over background tasks. Native teams provide messaging, redirect, and graceful shutdown. Background tasks are fire-and-forget with no steering — only a speedometer and emergency brake.
| Capability | Codex Sub-Agents | Claude Native Teams | Background Tasks |
|---|---|---|---|
| Observe output | wait() result | SendMessage delivery | TaskOutput (tail) |
| Send message mid-flight | send_input | SendMessage | NO |
| Pause / resume | NO | Idle → wake via SendMessage | NO |
| Graceful stop | close_agent | shutdown_request | TaskStop (lossy) |
| Redirect to different task | send_input | SendMessage | NO |
| Adjust scope mid-flight | send_input | SendMessage | NO |
| File conflict prevention | Manual git worktree routing | Native isolation: worktree + lead-only commits | None |
| Process isolation | YES (sub-process) | Shared worktree | Shared worktree |
When to use each:
| Scenario | Backend |
|---|---|
| Quick parallel tasks, coordination needed | Claude Native Teams |
| Codex-specific execution | Codex Sub-Agents |
| No team APIs available (last resort) | Background Tasks |
Skill Invocation Across Runtimes
Skills that chain to other skills (e.g., /rpi calls /research, /validate calls /council) MUST handle runtime differences:
| Runtime | Tool | Behavior | Pattern |
|---|---|---|---|
| Claude Code | Skill(skill="X", args="...") | Executable — skill runs as a sub-invocation | Skill(skill="council", args="--quick validate recent") |
| Codex | N/A | Skills not available — inline the logic or skip | Check if Skill tool exists before calling |
| OpenCode | skill tool (read-only) | Load-only — returns <skill_content> blocks into context | Call skill(skill="council"), then follow the loaded instructions inline |
OpenCode skill chaining rules: 1. Call the skill tool to load the target skill's content into context 2. Read and follow the loaded instructions directly — do NOT expect automatic execution 3. NEVER use slashcommand syntax (e.g., /council) in OpenCode — it triggers a command lookup, not skill loading 4. If the loaded skill references tools by Claude Code names, use OpenCode equivalents (see tool mapping below)
Cross-runtime tool mapping:
| Claude Code | OpenCode | Notes |
|---|---|---|
Task(subagent_type="...") | task(subagent_type="...") | Same semantics, different casing |
Skill(skill="X") | skill tool (read-only) | Load content, then follow inline |
AskUserQuestion | question | Same purpose, different name |
TaskCreate, TaskUpdate, TaskList, TaskGet | todo | Task tracking (Claude uses 4 tools, OpenCode uses 1) |
Read, Write, Edit, Bash, Glob, Grep | Same names | Identical across runtimes |
Rules
1. Never crash — missing CLI = skip or fallback, not error 2. Always inform — tell the user what was skipped and how to enable it 3. Preserve core function — the skill's primary purpose must still work without optional CLIs 4. Progressive enhancement — CLIs add capabilities, their absence removes them cleanly
Reference Documents
- references/substring-rename-overreach.md — Pre-rename checklist for bulk sed across same-prefix concepts
- references/cross-harness-skill-parity.md — Knowledge parity beyond audit-codex-parity.sh; codex frontmatter strictness
- references/content-hash-cache.md
- references/compaction-signals.md
- references/backend-background-tasks.md
- references/backend-claude-teams.md
- references/backend-codex-subagents.md
- references/backend-inline.md
- references/claude-code-latest-features.md
- references/claude-cli-verified-commands.md
- references/codex-cli-verified-commands.md
- references/cli-command-failures-2026-02-26.md
- references/ralph-loop-contract.md
- references/orchestration-as-prompt.md
- references/stale-scope-validation.md — planning rule loaded by plan + pre-mortem: re-validate inherited scope against HEAD before acting on deferred beads or handoff docs.
- references/strict-delegation-contract.md — canonical contract loaded by /rpi, /discovery, /validate: strict sub-skill delegation is the default for top-level orchestrators.
Backend: Background Tasks (Fallback)
Concrete tool calls for spawning agents using Task(run_in_background=true). This is the last-resort fallback when neither Codex sub-agents nor Claude native teams are available.
When detected: Task tool is available but TeamCreate and spawn_agent are not.
Limitations:
- Fire-and-forget — no messaging, no redirect, no scope adjustment
- No inter-agent communication
- No debate mode (R2 requires messaging)
- No retry (must re-spawn from scratch)
- No graceful shutdown (only
TaskStop, which is lossy)
---
Spawn: Background Agents
Spawn agents with Task(run_in_background=true). Each call returns a task_id for later polling.
Council Judges
Task(
subagent_type="general-purpose",
run_in_background=true,
prompt="You are judge-1.\n\nYour perspective: Correctness & Completeness\n\n<PACKET>\n...\n</PACKET>\n\nWrite your verdict to .agents/council/2026-02-17-auth-judge-1.md\nThis is your ONLY output channel — there is no messaging.",
description="Council judge-1"
)
# Returns: task_id="abc-123"
Task(
subagent_type="general-purpose",
run_in_background=true,
prompt="You are judge-error-paths.\n\nYour perspective: Error Paths & Edge Cases\n\n<PACKET>...</PACKET>\n\nWrite your verdict to .agents/council/2026-02-17-auth-judge-error-paths.md",
description="Council judge-error-paths"
)
# Returns: task_id="def-456"Both Task calls go in the same message — they run in parallel.
Swarm Workers
Task(
subagent_type="general-purpose",
run_in_background=true,
prompt="You are worker-3.\n\nYour Assignment: Task #3: Add password hashing\n...\n\nWrite result to .agents/swarm/results/3.json\nDo NOT run git add/commit/push.",
description="Swarm worker-3"
)Research Explorers
Task(
subagent_type="Explore",
run_in_background=true,
prompt="Thoroughly investigate: authentication patterns...\n\nWrite findings to .agents/research/2026-02-17-auth.md",
description="Research explorer"
)---
Wait: Poll for Completion
Background tasks have no messaging. Poll with TaskOutput.
TaskOutput(task_id="abc-123", block=true, timeout=120000)
TaskOutput(task_id="def-456", block=true, timeout=120000)Or non-blocking check:
TaskOutput(task_id="abc-123", block=false, timeout=5000)After `TaskOutput` returns, verify the agent wrote its result file:
Read(".agents/council/2026-02-17-auth-judge-1.md")Timeout behavior: If timeout expires, TaskOutput returns with a timeout status — the agent may still be running. Recovery: 1. Check result file — agent may have written it but not finished cleanly 2. If result file exists → use it, TaskStop the agent 3. If no result file → agent failed silently. For council: proceed with N-1 verdicts, note in report. For swarm: add task back to retry queue, re-spawn a fresh agent. 4. Never assume TaskOutput completion means the result file was written — always verify
Fallback: If background tasks fail despite detection, fall back to inline mode. See backend-inline.md.
---
No Messaging
Background tasks cannot receive messages. This means:
- No debate R2 — judges get one round only
- No retry — if validation fails, re-spawn a new agent from scratch
- No scope adjustment — the prompt is final at spawn time
---
Cleanup
Background tasks self-terminate when done. For stuck tasks:
TaskStop(task_id="abc-123")This is lossy — partial work may be lost.
---
Key Rules
1. Filesystem is the only communication channel — agents write files, lead reads files 2. No messaging = no debate — --debate is unavailable with this backend 3. No retry = must re-spawn — failed agents get a fresh Task call, not a message 4. Always check result files — TaskOutput completion doesn't guarantee the agent wrote its file 5. Prefer native teams — this backend is strictly inferior; use it only as last resort
Backend: Claude Native Teams
Concrete tool calls for spawning agents using Claude Code native teams (TeamCreate + SendMessage + shared TaskList).
When detected: TeamCreate tool is available in your tool list.
---
Pre-Flight: Confirm Modern Claude Features
Before spawning teammates, verify feature readiness:
1. claude agents succeeds (custom agents discoverable) 2. Teammate profiles for write tasks declare isolation: worktree 3. Long-running teammates prefer background: true 4. Hooks include worktree lifecycle coverage (WorktreeCreate, WorktreeRemove) and config auditing (ConfigChange) where policy requires it
For canonical feature details, read: skills/shared/references/claude-code-latest-features.md.
---
Setup: Create Team
Every spawn session starts by creating a team. One team per wave (fresh context = Ralph Wiggum preserved; see skills/shared/references/ralph-loop-contract.md).
TeamCreate(team_name="council-20260217-auth", description="Council validation of auth module")TeamCreate(team_name="swarm-1739812345-w1", description="Wave 1: parallel implementation")Naming conventions:
- Council:
council-YYYYMMDD-<target> - Swarm:
swarm-<epoch>-w<wave> - Crank: delegates to swarm naming
Leader Contract (Native Teams)
Claude teams are leader-first orchestration:
1. One lead creates the team and assigns all work. 2. Teammates never self-assign from shared tasks. 3. Teammates report to lead via short SendMessage signals. 4. Lead reads result artifacts from disk, validates, and decides retries/escalation.
Recommended signal envelope (single-line JSON, under 100 tokens):
{"type":"completion|blocked|help_request","agent":"worker-3","task":"3","detail":"short status","artifact":".agents/swarm/results/3.json"}completion: task finished, artifact written. blocked: cannot proceed safely. help_request: teammate needs coordination or scope clarification.
Peer Messaging (Allowed, Lead-Controlled)
Native teams support direct teammate-to-teammate messaging. Use this only for coordination handoffs; keep messages thin and always copy the lead in follow-up summaries.
worker-2 -> worker-5: "Need auth schema constant name; please confirm from src/auth/schema.ts"
worker-5 -> lead: "Resolved peer question for worker-2; no scope change."---
Spawn: Create Workers/Judges
After TeamCreate, spawn each agent with Task(team_name=..., name=...). All agents in a wave spawn in parallel (single message, multiple tool calls).
Council Judges (parallel spawn)
Task(
subagent_type="general-purpose",
team_name="council-20260217-auth",
name="judge-1",
prompt="You are judge-1 on team council-20260217-auth.\n\nYour perspective: Correctness & Completeness\n\n<PACKET>\n...\n</PACKET>\n\nWrite your verdict to .agents/council/2026-02-17-auth-judge-1.md\nThen send a SHORT completion signal to the team lead (under 100 tokens).\nDo NOT include your full analysis in the message — the lead reads your file.",
description="Council judge-1"
)
Task(
subagent_type="general-purpose",
team_name="council-20260217-auth",
name="judge-error-paths",
prompt="You are judge-error-paths on team council-20260217-auth.\n\nYour perspective: Error Paths & Edge Cases\n\n<PACKET>\n...\n</PACKET>\n\nWrite your verdict to .agents/council/2026-02-17-auth-judge-error-paths.md\nThen send a SHORT completion signal to the team lead (under 100 tokens).",
description="Council judge-error-paths"
)Both Task calls go in the same message — they spawn in parallel.
Swarm Workers (parallel spawn)
Task(
subagent_type="general-purpose",
team_name="swarm-1739812345-w1",
name="worker-3",
prompt="You are worker-3 on team swarm-1739812345-w1.\n\nYour Assignment: Task #3: Add password hashing\n<description>...</description>\n\nInstructions:\n1. Execute your task — create/edit files as needed\n2. Write result to .agents/swarm/results/3.json\n3. Send a SHORT signal to team lead (under 100 tokens)\n4. Do NOT run git add/commit/push — the lead commits\n\nRESULT FORMAT:\n{\"type\":\"completion\",\"issue_id\":\"3\",\"status\":\"done\",\"detail\":\"one-line summary\",\"artifacts\":[\"path/to/file\"]}",
description="Swarm worker-3"
)
Task(
subagent_type="general-purpose",
team_name="swarm-1739812345-w1",
name="worker-5",
prompt="You are worker-5 on team swarm-1739812345-w1.\n\nYour Assignment: Task #5: Create login endpoint\n...",
description="Swarm worker-5"
)Research Explorers (read-only)
Task(
subagent_type="Explore",
team_name="research-20260217-auth",
name="explorer-1",
prompt="Thoroughly investigate: authentication patterns in this codebase\n\n...",
description="Research explorer"
)Use subagent_type="Explore" for read-only research agents. Use "general-purpose" for agents that need to write files.
---
Wait: Receive Completion Signals
Workers/judges send completion signals via SendMessage. These are automatically delivered to the team lead — no polling needed.
When a teammate finishes, their message appears as a new conversation turn. The lead reads result files from disk, NOT from message content.
# Teammate message arrives automatically:
# "judge-1: Done. Verdict: WARN, confidence: HIGH. File: .agents/council/2026-02-17-auth-judge-1.md"
# Lead reads the file for full details:
Read(".agents/council/2026-02-17-auth-judge-1.md")Timeout handling (default: 120s per round, 90s for debate R2):
If a teammate goes idle without sending a completion signal: 1. Check their result file — they may have written it but failed to message 2. If result file exists → read it and proceed (the message was the only thing missing) 3. If no result file → the agent failed silently. Recovery: proceed with N-1 judges/workers and note the failure in the report. For swarm workers, add the task back to the retry queue. 4. Never wait indefinitely — after the timeout, move on
See skills/council/references/cli-spawning.md for timeout configuration (COUNCIL_TIMEOUT, COUNCIL_R2_TIMEOUT).
Fallback: If native teams fail at runtime despite passing detection (e.g., TeamCreate succeeds but Task spawning fails), fall back to background tasks. See backend-background-tasks.md.
---
Message: Debate R2 / Retry
Send messages to specific teammates using SendMessage. Teammates wake from idle when messaged.
Council Debate R2
SendMessage(
type="message",
recipient="judge-1",
content="DEBATE ROUND 2\n\nOther judges' verdicts:\n- judge-error-paths: FAIL (HIGH confidence) — file: .agents/council/2026-02-17-auth-judge-error-paths.md\n\nRead the other judge's file. Revise your assessment considering their perspective.\nWrite your R2 verdict to .agents/council/2026-02-17-auth-judge-1-r2.md\nThen send a completion signal.",
summary="R2 debate instructions for judge-1"
)R2 timeout (default: 90s): If a judge doesn't respond to R2 within COUNCIL_R2_TIMEOUT, use their R1 verdict for consolidation. See skills/council/references/debate-protocol.md for full timeout handling.
Swarm Worker Retry
SendMessage(
type="message",
recipient="worker-3",
content="Validation failed: pytest tests/test_auth.py returned exit code 1.\nFix the failing tests and rewrite your result to .agents/swarm/results/3.json",
summary="Retry worker-3: test failure"
)---
Cleanup: Shutdown and Delete
After consolidation/validate, shut down all teammates then delete the team.
# Shutdown each teammate
SendMessage(type="shutdown_request", recipient="judge-1", content="Council complete")
SendMessage(type="shutdown_request", recipient="judge-error-paths", content="Council complete")
# After all teammates acknowledge shutdown:
TeamDelete()Reaper pattern: If a teammate doesn't respond to shutdown within 30s, proceed with TeamDelete() anyway.
If `TeamDelete` fails (e.g., stale members): clean up manually with rm -rf ~/.claude/teams/<team-name>/ then retry TeamDelete() to clear in-memory state.
---
Multi-Wave Pattern
For crank/swarm with multiple waves, create a new team per wave:
# Wave 1
TeamCreate(team_name="swarm-1739812345-w1", description="Wave 1")
# ... spawn workers, wait, validate, commit ...
# ... shutdown teammates ...
TeamDelete()
# If TeamDelete fails: rm -rf ~/.claude/teams/swarm-1739812345-w1/ then retry
# Wave 2 (fresh context)
TeamCreate(team_name="swarm-1739812345-w2", description="Wave 2")
# ... spawn workers for newly-unblocked tasks ...
TeamDelete()This ensures each wave's workers start with clean context (no leftover state from prior waves).
If `TeamDelete` fails between waves, the next TeamCreate may conflict. Always verify cleanup succeeded before creating the next wave team.
---
Key Rules
1. `TeamCreate` before `Task` — tasks created before the team are invisible to teammates — Enforcement: `safety.ValidateTeamLifecycle()` (T9) 2. Pre-assign tasks before spawning — workers do NOT race-claim from TaskList — Enforcement: documentation only 3. Lead-only commits — workers write files, lead runs git add + git commit — Enforcement: `hooks/git-worker-guard.sh` (T4) 4. Thin messages — workers send <100 token signals, full results go to disk — Enforcement: `safety.ValidateMessageSize()` (T9) 5. New team per wave — fresh context, Ralph Wiggum preserved — Enforcement: `safety.ValidateTeamLifecycle()` (T9) 6. Always cleanup — TeamDelete() after every wave, even on partial failure — Enforcement: `hooks/stop-team-guard.sh` + `safety.ValidateTeamLifecycle()` (T9)
Backend: Codex Sub-Agents
Concrete tool calls for spawning agents using Codex CLI (codex exec). Used for --mixed mode cross-vendor consensus and as the primary backend when running inside a Codex session with spawn_agent.
---
Variant A: Codex CLI (from any runtime)
Used when codex CLI is available on PATH. Agents run as background shell processes.
When detected: which codex succeeds.
Spawn: Background Shell Processes
# With structured output (preferred for council judges)
Bash(
command='codex exec -s read-only -m gpt-5.3-codex -C "$(pwd)" --output-schema skills/council/schemas/verdict.json -o .agents/council/codex-1.json "JUDGE PROMPT HERE"',
run_in_background=true
)
# Without structured output (fallback)
Bash(
command='codex exec --full-auto -m gpt-5.3-codex -C "$(pwd)" -o .agents/council/codex-1.md "JUDGE PROMPT HERE"',
run_in_background=true
)Flag order: -s/--full-auto → -m → -C → --output-schema → -o → prompt
Valid flags: --full-auto, -s, -m, -C, --output-schema, -o, --add-dir Invalid flags: -q (doesn't exist), --quiet (doesn't exist), -p as a prompt flag (in Codex CLI it means profile)
Wait: Poll Background Shell
TaskOutput(task_id="<shell-id>", block=true, timeout=120000)Then read the output file:
Read(".agents/council/codex-1.json")Limitations
- No messaging — Codex CLI processes are fire-and-forget
- No debate R2 with Codex judges — they produce one verdict only
--output-schemarequiresadditionalProperties: falseat all levels--output-schemarequires ALL properties inrequiredarray-s read-only+-oworks —-ois CLI-level post-processing, not sandbox I/O
---
Variant B: Codex Sub-Agents (inside Codex runtime)
Used when running inside a Codex session where spawn_agent is available.
When detected: spawn_agent tool is in your tool list.
Spawn
spawn_agent(message="You are judge-1.\n\nPerspective: Correctness & Completeness\n\n<PACKET>...</PACKET>\n\nWrite verdict to .agents/council/2026-02-17-auth-judge-1.md")
# Returns: agent_id
spawn_agent(message="You are worker-3.\n\nTask: Add password hashing\n...\n\nWrite result to .agents/swarm/results/3.json")
# Returns: agent_idWait
wait(ids=["agent-id-1", "agent-id-2"])Timeout: wait() blocks until completion. Set a timeout at the orchestration level (default: COUNCIL_TIMEOUT=120s). If an agent doesn't complete within the timeout, close_agent it and proceed with N-1 verdicts/workers.
Message (retry/follow-up)
send_input(id="agent-id-1", message="Validation failed: fix tests and retry")Cleanup
close_agent(id="agent-id-1")---
Mixed Mode (Council)
For --mixed council, spawn runtime-native judges AND Codex CLI judges in parallel:
# Claude native team judges (via TeamCreate — see backend-claude-teams.md)
Task(subagent_type="general-purpose", team_name="council-20260217-auth", name="judge-1", prompt="...", description="Judge 1")
Task(subagent_type="general-purpose", team_name="council-20260217-auth", name="judge-2", prompt="...", description="Judge 2")
# Codex CLI judges (parallel background shells)
Bash(command='codex exec -s read-only -m gpt-5.3-codex -C "$(pwd)" --output-schema skills/council/schemas/verdict.json -o .agents/council/codex-1.json "PACKET"', run_in_background=true)
Bash(command='codex exec -s read-only -m gpt-5.3-codex -C "$(pwd)" --output-schema skills/council/schemas/verdict.json -o .agents/council/codex-2.json "PACKET"', run_in_background=true)All four spawn in the same message — maximum parallelism.
Mixed mode quorum: At least 1 judge from each vendor should respond for cross-vendor consensus. If all judges from one vendor fail, proceed as single-vendor council and note the degradation in the report.
---
Key Rules
1. Pre-flight check: which codex before attempting Codex CLI spawning 2. Model availability: gpt-5.3-codex requires API account — fall back to gpt-4o if unavailable 3. Flag order matters — agents copy examples exactly 4. `codex review` is a different command with different flags — do not conflate with codex exec 5. No debate with Codex judges — they produce one verdict, Codex CLI has no messaging
Backend: Inline (No Spawn Available)
Degraded single-agent mode when no multi-agent primitives are detected. The current agent performs all work sequentially in its own context.
When detected: No spawn_agent, no TeamCreate, no Task tool available — or --quick flag was explicitly set.
---
Council: Single Inline Judge
Instead of spawning parallel judges, the lead evaluates from each perspective sequentially:
1. Build the context packet (same as multi-agent mode)
2. For each perspective:
a. Adopt the perspective mentally
b. Write findings to .agents/council/YYYY-MM-DD-<target>-<perspective>.md
3. Synthesize into final reportOutput format is identical — same file paths, same verdict schema. Downstream consumers (consolidation, report) don't know it was inline.
No debate available — debate requires messaging between agents.
---
Swarm: Sequential Execution
Instead of parallel workers, execute each task sequentially:
1. TaskList() — find unblocked tasks
2. For each unblocked task (in order):
a. Execute the task directly
b. Write result to .agents/swarm/results/<task-id>.json
c. TaskUpdate(taskId="<id>", status="completed")
3. Check for newly-unblocked tasks
4. Repeat until all tasks completeSame result files, same validation — just sequential.
Error handling: If a task fails mid-execution: 1. Write failure result to .agents/swarm/results/<task-id>.json with "status": "blocked" 2. Check if downstream tasks depend on it (blockedBy) 3. Skip blocked downstream tasks, mark as skipped 4. Continue with independent tasks that don't depend on the failed one
---
Research: Inline Exploration
Instead of spawning an Explore agent, perform the tiered search directly:
1. Read docs/code-map/ if present
2. Grep/Glob for relevant files
3. Read key files
4. Write findings to .agents/research/YYYY-MM-DD-<topic>.md---
Key Rules
1. Same output format — inline mode writes the same files as multi-agent mode 2. Same validation — all checks still apply 3. Slower but functional — no parallelism, but all skill capabilities preserved (except debate) 4. Inform the user — log "Running in inline mode (no multi-agent backend detected)"
Claude CLI Verified Commands
Verified in this repo on 2026-02-26 (local environment).
Known-Good Command Shapes
# Discover CLI surface
claude --help
claude agents --help
# List configured agents
claude agents
# Non-interactive mode (prompt flag from help output)
claude -p "Summarize current git status."Common Operational Flags (from verified help output)
claude --model <alias-or-model>
claude --permission-mode <mode>
claude --worktree
claude --dangerously-skip-permissionsKnown Runtime Caveat
Historical note (superseded by ADR-0009 engine teardown): during high-concurrency retired phased-engine runs in this environment, Claude subprocesses were observed exiting with:
claude exited with code -1: signal: killedWhen this appears, prefer:
- retry with reduced concurrency / fewer parallel runtime sessions
- or use
--runtime-cmd codexfor phased runs in this repo
Claude Code Latest Features Contract
This document is the shared source of truth for Claude Code feature usage across AgentOps skills.
Baseline
- Target Claude Code release family:
2.1.x - Last verified against upstream changelog:
2.1.75 - Changelog source:
https://raw.githubusercontent.com/anthropics/claude-code/main/CHANGELOG.md
Current Feature Set We Rely On
1. Core Slash Commands
Skills and docs should assume these commands exist and prefer them over legacy naming:
/agents/hooks/permissions/memory/mcp/output-style/effort— set model effort level (low/medium/high). Opus 4.6 defaults to medium./color— set prompt-bar color per session (useful for distinguishing parallel sessions)
Reference: https://code.claude.com/docs/en/slash-commands
2. Agent Definitions
For custom teammates in .claude/agents/*.md, use modern frontmatter fields where applicable:
modeldescriptiontoolsmemory(scope control)background: truefor long-running teammatesisolation: worktreefor safe parallel write isolation
Reference: https://code.claude.com/docs/en/sub-agents
3. Worktree Isolation
When parallel workers may touch overlapping files, prefer Claude-native isolation features first:
- Session-level isolation:
claude --worktree(-w) - Agent-level isolation:
isolation: worktree - Sparse checkout:
worktree.sparsePathssetting — limit worktree to relevant directories in large monorepos
If unavailable in a given runtime, fall back to manual git worktree orchestration.
Reference: changelog 2.1.49, 2.1.50, and 2.1.75.
4. Hooks and Governance Events
Hooks-based workflows should include modern event coverage:
WorktreeCreateWorktreeRemoveConfigChangeSubagentStopTaskCompletedTeammateIdlePostCompact— fires after session context compaction. Use for auto-recovery (e.g., re-inject context).InstructionsLoaded— fires when CLAUDE.md loads. Use for policy enforcement.
HTTP hooks: Hooks can POST JSON to a URL and receive JSON responses, in addition to shell script execution.
Use these for auditability, policy enforcement, and cleanup.
Reference: https://code.claude.com/docs/en/hooks
5. Settings Hierarchy
Skill guidance must respect settings precedence:
1. Enterprise managed policy 2. Command-line args 3. Local project settings 4. Shared project settings 5. User settings
Reference: https://code.claude.com/docs/en/settings
6. Agent Inventory Command
Use claude agents as the first CLI-level check to confirm configured teammate profiles before multi-agent runs.
Reference: changelog 2.1.50.
7. Session Management
--from-pr <url>— start or resume a session linked to a specific GitHub PR--worktree(-w) — start session in an isolated git worktree
Reference: https://code.claude.com/docs/en/cli-reference
8. Tool Enhancements
- Read tool:
pagesparameter for PDFs — read specific page ranges (e.g.,pages: "1-5"). Large PDFs (>10 pages) require this parameter. - Bash tool: Wildcard permission patterns —
Bash(npm *)orBash(* install)for flexible auto-approval.
9. Effort Levels
The /effort command controls model reasoning depth:
low— fast, shallow reasoning. Good for research/exploration agents.medium— balanced (Opus 4.6 default).high— deep reasoning. Good for implementation and complex debugging.
Skill recommendation: set effort per agent role — low for judges/explorers, high for implementors.
Skill Authoring Rules
1. Do not reference deprecated permission command names (/allowed-tools, /approved-tools). 2. Multi-agent skills (council, swarm, research, crank, codex-team) must explicitly point to this contract. 3. Prefer declarative agent isolation (isolation: worktree) over ad hoc branch/worktree shell choreography where runtime supports it. 4. Keep manual git worktree fallback documented for non-Claude runtimes. 5. For long-running explorers/judges/workers, document background: true as the default custom-agent policy. 6. Use /effort to right-size model reasoning per agent role when spawning multi-agent workflows.
Review Cadence
- Re-verify this contract when:
- Claude Code changelog introduces new
2.1.xor2.2.xentries - any skill adds or changes multi-agent orchestration
- hook event support changes
CLI Command Failures Notes (2026-02-26)
Captured from live RPI batch execution logs in this repo.
Observed Failures
1. ao version (both PATH and local build)
- Output:
Error: unknown flag: --version - Working form:
ao version
2. codex -p "<prompt>" via older ao runtime wiring
- Output:
Error loading configuration: config profile ... not found - Cause:
-pmeans profile for Codex CLI - Working form:
codex exec "<prompt>"
3. Historical/superseded: retired phased-engine runs using Claude runtime in heavy batch
- Output:
phase 2 (implementation) failed: claude exited with code -1: signal: killed - Mitigation: lower concurrency or switch runtime to Codex (
--runtime-cmd codex)
4. Shell script variable collision in zsh
- Output:
read-only variable: status - Cause: using reserved name
statusin zsh loop scripts - Fix: rename variable (for example
result_state)
5. Shell glob failure in zsh with no matches
- Output:
zsh: no matches found: .agents/council/*pre-mortem* - Fix: guard with
2>/dev/null,setopt nonomatch, orls ... 2>/dev/null | head -1
6. Descriptor exhaustion during parallel orchestration
- Output:
Failed to create unified exec process: Too many open files (os error 24) - Mitigation: close stale agents/processes before launching more sub-runs
7. Non-blocking MCP startup failure during Codex runs
- Output:
MCP_DOCKER failed ... handshaking ... connection closed - Note: other MCP servers still started; run continued
Sources
.agents/rpi/batch-ready-20260226T123121.log.agents/rpi/batch-ready-local-20260226T131237.log
Codex CLI Verified Commands
Verified in this repo on 2026-02-26 (local environment).
Known-Good Command Shapes
# Discover CLI surface
codex --help
codex exec --help
# Non-interactive execution (prompt argument)
codex exec "Summarize current git status."
# Pin working directory
codex exec -C "$(pwd)" "List changed files and suggest next step."
# Structured output options
codex exec --json "Return one-line status"
codex exec -o /tmp/codex-last.txt "Return one-line status"Integration with NTM / Operating Loop
# Preferred runtime for a substrate pane running one operating-loop slice
codex exec "<prompt>"Expected spawn shape:
codex "exec" "<prompt>"Known-Bad / Mismatched Patterns
# BAD: -p is profile, not prompt
codex -p "do work"Observed failure mode:
Error loading configuration: config profile ... not foundOther invalid assumptions to avoid:
codex -q(not a valid quiet flag)codex exec --quiet(no such flag)
Tool-Call-Based Compaction Signals
Suggest context compaction at strategic points based on tool call count, not time or token usage.
Problem
Auto-compaction happens at arbitrary points (95% context fill), often mid-task. This destroys working memory at the worst possible time. Time-based compaction doesn't correlate with context complexity.
Solution: Tool-Call Counter with Strategic Signals
Track tool calls per session. Signal compaction at logical breakpoints.
Signal Schedule
| Tool Calls | Signal | Message |
|---|---|---|
| 50 (threshold) | First signal | "50 tool calls reached — consider /compact if transitioning phases" |
| 75 | Recurring | "75 tool calls — good checkpoint for /compact" |
| 100 | Recurring | "100 tool calls — strongly recommend /compact before next major task" |
| Every 25 after threshold | Recurring | Repeating signal |
Why 50?
- Typical exploration phase: 15-25 tool calls (reads, greps, globs)
- Typical implementation phase: 20-40 tool calls (reads, edits, writes, bash)
- Transition point (exploration → implementation): ~40-60 tool calls
- Compacting at the phase boundary preserves implementation context
Implementation (Hook Pattern)
#!/usr/bin/env bash
set -euo pipefail
[[ "${AGENTOPS_HOOKS_DISABLED:-}" == "1" ]] && exit 0
# Session-specific counter
SESSION_ID="${CLAUDE_SESSION_ID:-default}"
COUNTER_FILE="/tmp/agentops-toolcount-${SESSION_ID}"
THRESHOLD="${COMPACT_THRESHOLD:-50}"
# Increment counter
if [[ -f "$COUNTER_FILE" ]]; then
COUNT=$(( $(cat "$COUNTER_FILE") + 1 ))
else
COUNT=1
fi
echo "$COUNT" > "$COUNTER_FILE"
# Signal at threshold, then every 25
if [[ "$COUNT" -eq "$THRESHOLD" ]]; then
echo '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"[StrategicCompact] '"$THRESHOLD"' tool calls reached — consider /compact if transitioning between exploration and implementation phases."}}'
elif [[ "$COUNT" -gt "$THRESHOLD" ]] && [[ $(( (COUNT - THRESHOLD) % 25 )) -eq 0 ]]; then
echo '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"[StrategicCompact] '"$COUNT"' tool calls — good checkpoint for /compact if context is getting stale."}}'
fi
exit 0Why Manual Over Auto-Compact
| Auto-Compact | Strategic Compact |
|---|---|
| Fires at 95% context fill | Fires at phase transitions |
| Loses working memory mid-task | Preserves working memory for current task |
| No user awareness | User chooses when to compact |
| Can't save important context | User can save critical notes before compact |
When to Compact (User Guide)
Good times:
- After finishing exploration, before starting implementation
- After completing a crank wave, before starting the next
- After reading many files, before writing code
- At any natural milestone
Bad times:
- Mid-implementation (lose edit context)
- During council deliberation (lose judge context)
- While debugging (lose error reproduction context)
Integration with RPI Phases
| Phase Transition | Compact? | Reason |
|---|---|---|
| Discovery → Implementation | Yes | Fresh context for coding |
| Between crank waves | Optional | If context feels stale |
| Implementation → Validation | Yes | Fresh context for review |
| Post-mortem → Next RPI | Yes | Clean slate |
Configuration
Environment variables:
COMPACT_THRESHOLD=50— First signal threshold (default: 50)COMPACT_INTERVAL=25— Recurring signal interval (default: 25)
Counter Management
# Reset counter (on /compact or session start)
rm -f "/tmp/agentops-toolcount-${CLAUDE_SESSION_ID:-default}"
# Check current count
cat "/tmp/agentops-toolcount-${CLAUDE_SESSION_ID:-default}" 2>/dev/null || echo "0"The counter file is session-specific and lives in /tmp, so it auto-cleans on reboot.
Content-Hash Caching Pattern
Cache expensive operations by content hash (SHA-256), not file path. Survives renames, auto-invalidates on change.
When to Use
- File processing pipelines (PDF extraction, image analysis, text parsing)
- Expensive LLM calls on file content (summarization, code review)
- Any operation where: same content → same result, regardless of path
Core Pattern
1. Hash Computation (Chunked for Large Files)
import hashlib
from pathlib import Path
_HASH_CHUNK_SIZE = 65536 # 64KB chunks
def compute_file_hash(path: Path) -> str:
sha256 = hashlib.sha256()
with open(path, "rb") as f:
while True:
chunk = f.read(_HASH_CHUNK_SIZE)
if not chunk:
break
sha256.update(chunk)
return sha256.hexdigest()// Go equivalent
func computeFileHash(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", fmt.Errorf("opening %s: %w", path, err)
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return "", fmt.Errorf("hashing %s: %w", path, err)
}
return hex.EncodeToString(h.Sum(nil)), nil
}2. File-Based Storage ({hash}.json — O(1) lookup)
import json
def read_cache(cache_dir: Path, file_hash: str):
cache_file = cache_dir / f"{file_hash}.json"
if not cache_file.is_file():
return None
try:
return json.loads(cache_file.read_text(encoding="utf-8"))
except (json.JSONDecodeError, ValueError, KeyError):
return None # Corruption → cache miss (graceful degradation)
def write_cache(cache_dir: Path, file_hash: str, data: dict):
cache_dir.mkdir(parents=True, exist_ok=True)
cache_file = cache_dir / f"{file_hash}.json"
cache_file.write_text(json.dumps(data, indent=2), encoding="utf-8")3. Service Layer (SRP: Pure Function + Cache Wrapper)
# Pure processing function — no cache knowledge
def extract_text(file_path: Path) -> dict:
"""Extract text from file. Pure function."""
# ... expensive processing ...
return {"content": "...", "metadata": {...}}
# Cache wrapper — adds caching around pure function
def extract_with_cache(
file_path: Path,
*,
cache_enabled: bool = True,
cache_dir: Path = Path(".cache/content"),
) -> dict:
if not cache_enabled:
return extract_text(file_path)
file_hash = compute_file_hash(file_path)
cached = read_cache(cache_dir, file_hash)
if cached is not None:
return cached
result = extract_text(file_path)
write_cache(cache_dir, file_hash, result)
return resultKey Design Decisions
| Decision | Rationale |
|---|---|
| SHA-256 hash, not path | Survives renames, auto-invalidates on content change |
{hash}.json file naming | O(1) lookup, no index file needed, easy to inspect |
| Corruption → cache miss | Graceful degradation, re-processes on next run |
| Service layer separation | Keeps processing function pure and testable |
| Lazy directory creation | mkdir -p on first write, no setup needed |
| Chunked hashing | Handles large files without loading into memory |
Anti-Patterns
| Anti-Pattern | Why It Fails | Fix |
|---|---|---|
| Path-based cache key | Breaks on file move/rename | Use content hash |
| Cache logic inside processor | SRP violation, can't test independently | Wrap as separate layer |
dataclasses.asdict() with nested frozen | Breaks serialization | Manual serialization |
| No corruption handling | Corrupted cache blocks processing | Return None, re-process |
| Shared index file | Concurrency issues, bottleneck | Per-hash files |
Integration Points
- Research skill: Cache firecrawl/exa results by URL content hash
- Reverse-engineer-rpi: Cache upstream repo analysis by commit SHA
- Doc skill: Cache documentation generation by source file hash
- Any file-processing pipeline: Add
--cache/--no-cacheflag
Cache Cleanup
# Remove entries older than 30 days
find .cache/content/ -name "*.json" -mtime +30 -delete
# Remove all cache
rm -rf .cache/content/Add to .gitignore:
.cache/content/Cross-Harness Skill Parity
A skill must carry the same intent, knowledge, BDD/Gherkin acceptance, and observable behavior across Claude Code and Codex. Only the implementation adapts to each harness (tools, APIs, self-perpetuation primitive). A "trimmed variant" in skills-codex/ is a parity defect, not a legitimate baseline.
scripts/audit-codex-parity.sh passing is necessary, not sufficient. It checks structural drift against the established baseline — but if that baseline itself was a trimmed copy (9 of 19 reference files, missing whole mechanisms), the audit silently approves the gap.
When This Fires
| Signal | Action |
|---|---|
Editing skills/<n>/SKILL.md and there's a Codex twin at skills-codex/<n>/ | Verify knowledge parity by diffing references/ — not just the audit script |
Adding a new references/*.md to the Claude-side skill | Decide: port (harness-adapted) or omit-with-justification |
audit-codex-parity.sh green after a substantial Claude-side edit | Audit is satisfied; knowledge parity is a separate question |
Need to add metadata (e.g. practices:, metadata:) to skills-codex/<n>/SKILL.md | Stop — codex frontmatter is strict |
The Strict-Frontmatter Rule (Codex Twins)
skills-codex/*/SKILL.md files are enforced to have ONLY two frontmatter keys: `name` and `description`. Adding any other key (practices:, metadata:, hexagonal_role:, etc.) fails scripts/validate-codex-generated-artifacts.sh with:
<skill> has non-Codex frontmatter fields: <key>The check is regex-driven on the frontmatter block, not config-driven — you can't override it per-skill.
How to extend Claude-side metadata without breaking Codex
1. Edit skills/<n>/SKILL.md only (Claude side). 2. Run scripts/regen-codex-hashes.sh. This updates skills-codex/<n>/.agentops-generated.json markers (specifically source_hash) WITHOUT touching skills-codex/<n>/SKILL.md content. 3. The marker's source_hash records that Claude-side content drifted; codex content stays stable.
The pre-push gate agentops-core.distribution-install-update canary will fail with N errors (one per affected twin) if you violate this.
Evidence (anchored)
"`audit-codex-parity.sh` passing is NOT knowledge parity. It checks
structural drift against the established baseline — and the codex
evolve skill's baseline was a trimmed 9-of-19-reference-files variant.
A green parity audit silently accepted a skill that omitted whole
mechanisms (convergence, healing-first classifier, hypothesis
tracking)."
— .agents/learnings/2026-05-16-cross-harness-skill-parity.md (soc-y5vh.8 retro)
"a skill must carry the same intent, knowledge, BDD/Gherkin, and
behavior across Claude Code and Codex. Only the implementation
adapts to each harness (tools, APIs, self-perpetuation primitive —
e.g. ClaudeScheduleWakeupnon-re-arm vs Codex Step 7while-loop
break). A 'trimmed variant' is a parity defect, not a legitimate
baseline."
— .agents/learnings/2026-05-16-cross-harness-skill-parity.md
"Initially mirrored practices: into all 13 codex twins; pre-pushgate's agentops-core.distribution-install-update canary failed with13 non-Codex frontmatter fields: practices errors. Reverted codextwins; re-ran regen; gate passed."
— .agents/learnings/2026-05-10-codex-frontmatter-is-strict-name-description.md (soc-hdot pass-1)
How To Apply
When editing a Claude-side skill
1. Make the edit on the Claude side first. skills/<n>/SKILL.md and skills/<n>/references/. 2. Diff the references/ directory against the Codex sibling:
diff <(ls skills/<n>/references/) <(ls skills-codex/<n>/references/ 2>/dev/null)3. For each file in the Claude-side but not the Codex-side: decide to port (with harness adaptation) or omit (with a comment in skills-codex-overrides/<n>/ explaining why). 4. Regenerate codex hashes: scripts/regen-codex-hashes.sh. 5. Verify both gates:
bash scripts/audit-codex-parity.sh --skill <n>
bash tests/skills/lint-skills.shWhen adding a NEW reference file Claude-side
Default: port to the Codex side as a harness-adapted copy. Justify omission only when the reference is fundamentally Claude-specific (e.g., ScheduleWakeup mechanics, Skill() tool semantics). Document the justification in skills-codex-overrides/<n>/.parity-omissions.md if one exists, or as a comment in the relevant skills-codex/<n>/... file.
When tempted to add frontmatter to a Codex twin
Don't. Edit Claude-side only; let regen-codex-hashes.sh track drift. If you genuinely need Codex-specific metadata, put it in skills-codex-overrides/<n>/ as a separate file, not in the SKILL.md frontmatter.
Implementation Adaptations That Are Legitimate
These are NOT parity defects — they're the implementation layer adapting to each harness:
| Concept | Claude Code | Codex |
|---|---|---|
| Self-perpetuation | ScheduleWakeup (non-re-arm) | Step 7 while-loop break |
| Tool invocation | Skill(skill="x"), Agent(...) | inline shell + filesystem |
| Background work | Bash(run_in_background=true) + Monitor | nohup / disown |
| File state | Read/Edit/Write tools | direct filesystem |
| Memory loop | ~/.claude/projects/.../memory/ | session JSONL parse |
What MUST be the same: intent (what the skill is for), knowledge (the references that explain the mechanism), Gherkin acceptance (the BDD scenarios), and observable behavior (the final artifact a session produces).
Why The Audit Alone Isn't Enough
audit-codex-parity.sh enforces:
skills-codex/<n>/SKILL.mdexists ifskills/<n>/SKILL.mdexists- The
namefield matches - The
source_hashin.agentops-generated.jsonmatches the current
Claude-side hash (when codex was last regenerated)
It does NOT enforce:
- That the
descriptioncaptures the same intent - That the
references/directory has structurally-equivalent files - That the Codex twin actually implements the same workflow
The trimmed-baseline failure mode is invisible to the audit. The fix is a knowledge-parity check — read both sides, confirm same intent, same references (or justified omissions), same Gherkin. Broader reconciliation tracked in [[soc-an3v]] (per the retro).
See Also
scripts/audit-codex-parity.sh— the structural auditscripts/regen-codex-hashes.sh— drift marker regeneratorscripts/validate-codex-generated-artifacts.sh— the strict-frontmatter
enforcer
skills-codex-overrides/<n>/— where genuine Codex-side deviations livedocs/contracts/claude-bot-delegation.md— the bot-permissions parity
layer (orthogonal to skill parity)
Orchestration-as-Prompt Pattern
What
Orchestration logic embedded in SKILL.md prompts rather than in Go/Python code. The LLM reads the orchestration rules and executes them as part of its reasoning. The prompt IS the program.
Why
- Runtime adaptability. The LLM adapts to runtime context (different backends, different capabilities) without conditional compilation or feature flags.
- Judgment calls. Prompt-based rules handle decisions that code cannot anticipate — "is this research sufficient?", "should this wave retry or escalate?"
- Iteration speed. Changing a SKILL.md is a single file edit. No build, no deploy, no version matrix.
- Cross-runtime portability. The same orchestration works across Claude Code, Codex, and Cursor without platform-specific code paths.
When to Use Code vs Prompt
| Use Code For | Use Prompt For |
|---|---|
Hard constraints (MAX_EPIC_WAVES = 50) | Judgment calls ("is this research sufficient?") |
| File I/O, git operations, CLI wrappers | Workflow sequencing and phase transitions |
| Schema validation, JSON parsing | Quality assessment and retry decisions |
| Timeout enforcement, kill switches | Scope decisions and prioritization |
| Binary pass/fail gates (test suites) | Nuanced severity classification |
| Secrets management, credential handling | Work selection ladders and fallback cascades |
Examples from This Codebase
Completion Markers (crank)
The Sisyphus Rule in skills/crank/SKILL.md uses prompt-embedded markers to enforce completion semantics. After each wave, the LLM must emit one of <promise>DONE</promise>, <promise>BLOCKED</promise>, or <promise>PARTIAL</promise>. The retry logic (max 3 attempts, escalation on repeated BLOCKED) lives entirely in the prompt. Code only enforces the hard wave cap (MAX_EPIC_WAVES = 50).
Wave Orchestration (crank + swarm)
skills/crank/SKILL.md defines the full wave loop — identify ready work, bridge tracker state into the current runtime's execution queue, invoke /swarm, verify results, and loop until the epic closes. The LLM decides wave composition, conflict resolution strategy (serialize vs isolate), and when to stop. skills/swarm/SKILL.md defines runtime-native spawn selection where the LLM chooses the available multi-agent backend, or inline fallback, from capability detection rather than hardcoded tool names.
Work Selection Ladder (evolve)
skills/evolve/SKILL.md defines a 7-layer priority cascade: pinned queue, harvested work, open beads, failing goals, testing improvements, validation tightening, drift mining, feature suggestions. The LLM walks the ladder each cycle, making judgment calls at every layer. Code handles the kill switch check and cycle logging. The dormancy decision ("are all generator layers truly empty?") is a prompt-level judgment, not a boolean.
Phase Routing (rpi)
skills/rpi/SKILL.md classifies work complexity (fast/standard/full) using keyword matching and goal length — logic that could be code but benefits from LLM flexibility when edge cases arise. The three-phase rule (discovery, implementation, validation) and the validation-to-crank retry loop are prompt-orchestrated. The LLM decides whether to re-enter crank with findings context or escalate to manual intervention.
Backend Selection (swarm)
skills/swarm/SKILL.md instructs the LLM to detect multi-agent capabilities at runtime and select the native backend. Rather than a code-level if/else on runtime type, the prompt says "use runtime capability detection, not hardcoded tool names" and the LLM adapts to whatever tools are available in the current session.
Anti-Patterns
- Timing/timeout logic in prompts. LLMs cannot reliably track wall-clock time. Use code for timeouts, kill switches, and stall detection.
- Binary validation in prompts. If the answer is strictly pass/fail (test suite, schema check, lint), run it in code. Prompts add ambiguity where none is needed.
- Secrets or credentials in prompt-based orchestration. Prompts are logged, cached, and visible in transcripts. Keep credentials in environment variables and code-level injection.
- Unbounded loops without code-level caps. Always pair prompt-level "loop until done" with a hard code-level limit (e.g.,
MAX_EPIC_WAVES = 50). The LLM may misjudge completion. - Complex arithmetic or counting. LLMs make arithmetic errors. Use code for counters, SHA comparisons, and numeric thresholds.
Origin
Pattern validated by Claude Code's internal coordinatorMode.ts (discovered via npm source map leak, March 2026). The coordinator uses prompt-embedded orchestration rules for sub-agent dispatch, phase transitions, and tool routing — the same approach codified in AgentOps skills.
Ralph Loop Contract (Reverse-Engineered)
This contract captures the operational Ralph mechanics reverse-engineered from:
https://github.com/ghuntley/how-to-ralph-wiggum.tmp/how-to-ralph-wiggum/README.md.tmp/how-to-ralph-wiggum/files/loop.sh.tmp/how-to-ralph-wiggum/files/PROMPT_plan.md.tmp/how-to-ralph-wiggum/files/PROMPT_build.md
Use this as the source-of-truth for Ralph alignment in AgentOps orchestration skills.
Core Contract
1. Fresh context every iteration/wave.
- Each execution unit starts clean; no carryover worker memory.
2. Scheduler-heavy, worker-light.
- The lead/orchestrator schedules and reconciles.
- Workers perform one scoped unit of work.
3. Disk-backed shared state.
- Loop continuity comes from filesystem state, not accumulated chat context.
- In classic Ralph:
IMPLEMENTATION_PLAN.mdandAGENTS.md.
4. One-task atomicity.
- Select one important task, execute, validate, persist state, then restart fresh.
5. Backpressure before completion.
- Build/tests/lint/gates must reject bad output before task completion/commit.
6. Observe and tune outside the loop.
- Humans (or lead agents) monitor outcomes and adjust prompts/constraints/contracts.
AgentOps Mapping
| Ralph concept | AgentOps implementation |
|---|---|
| Fresh context per loop | New workers/teams per wave in /swarm; fresh operating-loop context per worker or NTM pane |
| Main context as scheduler | Mayor/lead orchestration in /swarm and /crank |
| Plan file as state | bd issue graph, TaskList state, plan artifacts in .agents/plans/ |
| One task per pass | One issue per worker assignment in swarm/crank waves |
| Backpressure | /validate, task validation hooks, tests/lint gates, push/pre-mortem gates |
| Outer loop restart | Wave loop in /crank; NTM/Agent Mail substrate for out-of-session loop restarts |
Implementation Notes
- Keep worker prompts concise and operational.
- Keep state in files/issue trackers, not long conversational memory.
- Prefer deterministic checks over subjective completion.
Planning Rule: Re-Validate Inherited Scope Estimates
Applies to: /plan, /pre-mortem, /discovery, any skill that consumes a bead description, prior plan, handoff doc, or scope estimate produced by an earlier session.
Status: Active rule. Violations should be called out in pre-mortem gates.
The rule
Before acting on a deferred bead, handoff doc, or an inherited scope estimate, verify that the cited infrastructure (functions, files, LOC counts, call-site counts, "already exists" vs "needs to be built") still matches HEAD.
Run /council --evidence against the description before starting implementation when any of the following are true:
1. The description is older than 7 days at the time you act on it. 2. The description was filed by a prior session under time pressure (look for phrases like "hastily filed", "deferred from X wave", "quick note"). 3. The description cites specific LOC counts, "N callers", "need to build X", or "architectural change required". 4. The description references a function, file, or symbol by name. 5. The scope is classified as full complexity and the estimate was produced by a different session/agent.
Why
Scope estimates inflate in deferral handoffs. A description written hours before being deferred often embeds the first-reader's mental model of difficulty — that model gets anchored to "looked hard", and subsequent sessions inherit the estimate without re-validating. Three failure modes this rule prevents:
1. Ghost work. A session begins re-implementing infrastructure that a later commit added. Example: rebuilding staging primitives that already exist in checkpoint.go. 2. Deferral loop. Each session inherits the "too complex for this session" verdict and defers again, permanently locking in the inferior design. 3. Symbol drift. The description cites package.Func that has been renamed, moved, or deleted. Acting on the stale citation produces bugs or scope creep.
How to apply
1. Extract citations from the description. File paths (path/to/file.go:123), function names (func foo(), backticked symbols, LOC counts. 2. Run `ao beads verify <id>` if the input is a bead ID — it mechanically checks each citation against HEAD and reports stale references. (If this command doesn't exist in your session, grep manually.) 3. Run `/council --evidence validate "verify this scope estimate: <description>"` on the description. Each judge must return concrete test_assertions that either confirm or refute the cited infrastructure. 4. Re-state the scope based on HEAD, not the description. Include the delta: "Description said 395 LOC; HEAD says ~300 LOC because [X] already exists at [file:line]." 5. Proceed only if scope is unchanged or smaller. If the HEAD-validated scope is materially larger than the description, defer to a new pre-mortem — the original framing was under-estimating and the new framing may need different resources.
Concrete case (2026-04-11, na-h61)
The na-h61 bead claimed cli/cmd/ao/fitness.go::collectLearnings with "8 existing callers" as the refactor target for a "395+ LOC architectural change to collect fitness snapshots against a staging tree".
Running /council --evidence --tdd against the description before touching code revealed:
cli/cmd/ao/fitness.godoes not exist.collectLearningsdoes exist atcli/cmd/ao/inject_learnings.go:50but it is an INJECT-side artifact loader, not a fitness-MEASURE function. It has nothing to do with M8.- The staging tree infrastructure (
cp.StagingDir, deep-copy viaNewCheckpoint, atomic swap viaCommit,Rollbacksafe at any state) already existed incli/internal/overnight/checkpoint.go:194-345. - The actual fix was a sequencing bug:
RunMeasurewas called atloop.go:381(aftercp.Commit()atloop.go:335). Moving MEASURE before COMMIT and wiringcp.Rollback()into the halt branches was ~300 LOC, not 395.
Without the pre-flight validation, the session would have either (a) re-implemented existing staging primitives, or (b) deferred again. With the validation, M8 landed in one session.
Anti-pattern: "I'll fix it when I see the code"
A common trap: believing the description and starting to read code, trusting that discrepancies will surface during implementation. They do — but by then you've already committed to the wrong mental model and sunk time. The --evidence council validation is ~3 minutes of parallel judge work. It returns concrete assertions you can grep against HEAD in another 30 seconds. Total cost: under 5 minutes. Total savings: hours of ghost work.
See also
skills/council/SKILL.md—--evidenceflag and the falsifiable-assertion schemaskills/plan/SKILL.md— loads this rule during scope decompositionskills/pre-mortem/SKILL.md— loads this rule when the input is a handoff or deferred bead
Strict Delegation Contract (shared)
Applies to all top-level orchestrator skills:/rpi,/discovery,/validate.
Strict sub-skill delegation is the default, not opt-in.
The Contract
Top-level orchestrator skills delegate to their declared sub-skills via Skill(skill="<name>", ...) — as separate tool invocations, one per phase/step. Each sub-skill owns its artifact, its gate, and its retry policy. Inlining the work breaks that ownership chain.
There is no --full flag because strict delegation is always on.
Phase-Isolated Transport
Strict delegation names the contract. Transport isolation names where that contract runs.
For high-cost lifecycle phases, the desired runtime shape is:
1. The visible orchestrator keeps the lifecycle objective, phase order, and retry policy. 2. A phase runner receives only the phase skill name, the bounded handoff artifact, and the minimum objective context. 3. The runner executes the declared skill contract (/discovery, /crank, or /validate) in an isolated phase context. 4. The orchestrator receives only artifact path, verdict, and next action.
This is not a compression escape. It is strict delegation over an isolated transport. The forbidden move is replacing the skill contract with direct agent work.
Anti-Pattern: Compression
Do not inline phase work, compress multiple phases into one pass, substitute direct Agent() work for a skill contract, or skip mandatory phases. Typical rationalizations to reject:
- "I'll compress the three phases into one pass."
- "Let me do discovery inline — I already know what to do."
- "Nested `Skill()` calls waste context; I'll spawn an `Agent()` instead."
- "The implementation is validated by tests passing; skipping `/validate`."
- "The plan looks good, skipping pre-mortem to save time."
- "I'll just spawn 3 judges directly — it's what `/validate` does anyway."
- "Post-mortem is just writing a summary, I'll do it inline."
Pre-Mortem Anti-Rationalization Clause
The following do NOT count as a pre-mortem and MUST NOT be used to skip the delegated /pre-mortem pass:
1. An inline risk or "honest risk" section the author wrote. The author's own risk assessment is autocorrelated with the plan — the same blind spots that shaped the plan shape the risk section. It is not an independent check. 2. An earlier adversarial pass on an INPUT or premise, not THIS plan. A prior council/siege/refutation that challenged a premise (e.g. "is this the right goal?") does not validate the implementation plan derived from that premise. Different artifact, different failure modes. 3. "A related council already ran." A council on a sibling plan, a prior version of the plan, or a different artifact in the same epic does not transfer. Pre-mortem is plan-specific.
Pre-mortem = DELEGATED + INDEPENDENT (author ≠ reviewer) + fresh-context on THIS plan. All three conditions must hold. An inline section satisfies none; a prior-premise adversarial pass satisfies at most one (independent) but not the other two (not this plan, not delegated).
All of these are contract violations. A live compression was observed 2026-04-19 (see `docs/learnings/orchestrator-compression-anti-pattern.md`). The compression "worked" mechanically (strict build passed, 2-judge inline vibe PASSed) but the knowledge flywheel never turned — no forged learnings, no post-mortem artifact, no structured council verdict. Contract strength depends on actual Skill() invocations, not self-certification.
Agent() vs Skill()
These are not interchangeable:
| Call | When to use |
|---|---|
Skill(skill="<name>", ...) | Invoking a declared skill with its full contract. Required for phase delegation. |
Agent(subagent_type="...", ...) | Spawning a sub-agent for parallel independent work within a skill's step (e.g., /research dispatching parallel Explore agents is fine). |
| Phase runner | Runtime transport that executes one declared skill contract in an isolated context and returns only the bounded phase artifact. |
If you're tempted to call Agent() in place of a Skill() invocation, you're compressing. Stop.
If a runtime lacks a native Skill()-fork boundary, a phase runner may use a subagent, daemon job, or process wrapper as transport. That wrapper must be thin: load the declared skill, execute the skill workflow, write the expected artifact, and return a compact result. It must not perform the phase directly.
Supported Compression Escapes
These flags scale gate depth or scope, never skip phases. They are the only supported shortcuts:
/rpi
--quick/--fast-path— force fast complexity (inline--quickgates inside sub-skills; still runs all three phases)--from=<phase>— resume from a specific phase when earlier artifacts already exist--skip-pre-mortem/--no-retro/--no-forge— skip specific sub-skills inside a phase--no-budget— disable phase time budgets
/discovery
--quick— passed through to/pre-mortemfor fast inline gate--skip-brainstorm— skip STEP 1 when the goal is specific (>50 chars, no vague keywords)--interactive/--auto— control human-gate behavior in research and plan--no-scaffold— skip STEP 4.5 scaffold auto-invocation (canonical name;--no-lifecycleis a deprecated alias through v2.40.0)
/validate
--quick— fast inline gates inside sub-skills (vibe, post-mortem)--no-retro/--no-forge— skip specific sub-skills--no-lifecycle— skip STEP 1.7 lifecycle checks (test, deps, review, perf)--no-behavioral— skip STEP 1.8 holdout scenarios--allow-critical-deps— allow shipping despite CVSS ≥ 9.0 findings
If tempted to shortcut outside this list: stop and delegate.
Positive Pattern: What Correct Delegation Looks Like
A correct /rpi invocation shows three distinct Skill() tool calls at phase boundaries:
Skill(skill="discovery", args="<goal> --auto") # Phase 1
→ <promise>DONE</promise>
→ reads .agents/rpi/execution-packet.json
Skill(skill="crank", args="<packet-path> [--test-first]") # Phase 2
→ <promise>DONE</promise>
→ reads .agents/rpi/phase-2-summary-*.md
Skill(skill="validate", args="--complexity=<level> [--strict-surfaces]") # Phase 3
→ <promise>DONE</promise>
→ writes .agents/rpi/phase-3-summary-*.mdAnything less is compressed.
When phase-isolated transport is available, the transcript may show a phase runner instead of raw inline skill execution. The acceptance rule is still the same: the delegated phase contract must run, emit its completion marker, and write the expected phase summary file.
Detection for Reviewers
When auditing a session that claims to have run /rpi, check the transcript for:
1. Three delegated phase contracts at phase boundaries (Skill() directly, or a phase runner whose sole job is to execute the named skill contract). 2. Three `<promise>DONE</promise>` markers, each from the delegated sub-skill. 3. Three phase summary files in .agents/rpi/phase-{1,2,3}-summary-*.md.
Missing any of the three = compression.
Enforcement Layers (defense in depth)
1. This contract document — read before / during orchestrator invocation. 2. Loud text in each orchestrator's SKILL.md — anti-pattern section with explicit examples. 3. Durable learning at docs/learnings/orchestrator-compression-anti-pattern.md — surfaced through the orchestrator skill contracts. 4. Optional future: runtime hook that inspects the skill invocation trace and blocks downstream work when phases were skipped. Not implemented; deferred to a follow-up initiative.
Contract strength alone is not enforcement. Layer 1 (this doc) + Layer 2 (SKILL.md sections) + Layer 3 (flywheel injection) together give durable coverage.
Substring Rename Overreach
When a bulk rename uses substring matching (sed -i 's/Old/New/g' over many files), it catches identifiers from concepts that share a prefix with the target but are semantically different. The build passes (symbols are just identifiers — the compiler doesn't care what concept they encode) and tests pass, but the post-rename code has semantic mismatches that surface later as confused APIs and misnamed errors.
Mirror of docs/learnings/2026-05-13-substring-sed-rename-overreach.md (authored from /evolve cycle 126). Copied here per CI's no-symlinks rule so any skill (/evolve, /crank, /refactor, /standards) can reference the rule.
Worked Example (Cycle 126)
daemon.QueueClaim → daemon.QueueLease shipped via find ... -name '*.go' | xargs sed -i 's/QueueClaim/QueueLease/g'.
The sed pattern caught identifiers from two different concepts:
1. Intended: daemon.QueueClaim (struct, lease semantics — has fields ClaimToken, LeaseEpoch, LeaseExpiresAt). Correctly renamed to QueueLease. 2. Over-reach: rpi.ErrQueueClaimConflict, rpi.RequireQueueClaimOwner, and the cli/cmd/ao wrappers errQueueClaimConflict / requireQueueClaimOwner. These are about work-item claim coordination in `.agents/rpi/next-work.jsonl` — when two workers race to claim the same harvested work item. That IS a Claim concept (per the BC2 contract: Claim = public assertion of a work slot), not a Lease.
Caught by the PreToolUse:Bash post-commit diff hook:
func EnsureQueueItemClaimable(...) error {
...
return ErrQueueLeaseConflict // ← Claim API, Lease error name
}EnsureQueueItemClaimable kept Claim-language (sed only matched QueueClaim, not Claimable), but the error it returned was renamed to ErrQueueLeaseConflict. The semantic mismatch was visible at a glance.
The Rule (Pre-Rename Checklist)
Before any bulk sed rename across packages:
1. Find the type definition — grep -rn 'type <OldName>\b'. 2. Enumerate every identifier that contains the substring — not just the type itself. Use:
grep -roE '\w*<OldName>\w*' cli/ scripts/ docs/ --exclude-dir=testdata \
| sort -u3. Classify each identifier by concept: the type-def concept vs. sibling concepts that share the prefix incidentally. 4. Sed only on identifiers matching the target concept. Use file restrictions, line-number restrictions, or per-concept regexes. 5. After commit, re-read the diff. Look for semantic inconsistencies — APIs that kept old language returning errors that took new language, or vice versa.
Worked Pattern
# Step 1: type def
grep -rn 'type QueueClaim\b' cli/
# Step 2: enumerate ALL identifiers containing "QueueClaim"
grep -roE '\w*QueueClaim\w*' cli/ scripts/ docs/ \
--exclude-dir=testdata | sort -u
# What you SHOULD see (with classification):
# QueueClaim (the struct — daemon, rename)
# ErrQueueClaimConflict (rpi, WORK-ITEM claim — KEEP)
# RequireQueueClaimOwner (rpi, WORK-ITEM claim — KEEP)
# errQueueClaimConflict (ao wrapper — KEEP)
# requireQueueClaimOwner (ao wrapper — KEEP)
# Step 3: classify (above)
# Step 4: sed only on the struct + its method-receiver params
# Step 5: post-commit diff re-readAnti-Pattern Signal
If a single sed across N files moves a counter from K → 0 and N is large enough you can't diff-review the changes by eye, the rename almost certainly over-reached. Lower N by restricting the file set, or use gopls rename / IDE refactoring tooling that knows about identifier scope.
When This Matters Most
Renames where the substring is a noun that has BOTH a domain concept (BC1/2/etc.) AND an incidental code identifier:
- Gate vs Validator:
cli/internal/flywheel.Validatorhas nothing to do
with scripts/check-*.sh validators. Mass Validator → Gate sed would break it.
- Run vs Cycle:
CIRun(BC2 port),RPIRun(rpi package),
ContextVariantRun (eval) — all legitimate "Run" identifiers. Narrow renames only.
- Session: already-prefixed Sessions (
AgentSession,GCSession,
GasCitySession, CLIFallbackSession) are unaffected. Only the bare type Session struct declarations need the rename.
See Also
docs/learnings/2026-05-13-substring-sed-rename-overreach.md— the
promoted canonical version (this is a skill-side mirror).
docs/contracts/ubiquitous-language.md— the source-of-truth for which
identifiers map to which concept.
skills/standards/references/go.md— Go-specific rename conventions.
#!/usr/bin/env bash
set -euo pipefail
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
PASS=0; FAIL=0
check() { if bash -c "$2"; then echo "PASS: $1"; PASS=$((PASS + 1)); else echo "FAIL: $1"; FAIL=$((FAIL + 1)); fi; }
check "SKILL.md exists" "[ -f '$SKILL_DIR/SKILL.md' ]"
check "SKILL.md has YAML frontmatter" "head -1 '$SKILL_DIR/SKILL.md' | grep -q '^---$'"
check "name is shared" "grep -q '^name: shared' '$SKILL_DIR/SKILL.md'"
check "marked as internal" "grep -q 'internal: true' '$SKILL_DIR/SKILL.md'"
echo ""; echo "Results: $PASS passed, $FAIL failed"
[ $FAIL -eq 0 ] && exit 0 || exit 1
Validation Contract
The Trust Problem: Agent completion claims cannot be trusted. Verify then trust.
Overview
This document specifies how validation requirements are defined, executed, and enforced in the swarm/crank architecture.
BROKEN (old):
<task-notification> --> TaskUpdate(completed) --> bd close
^ TRUST (no verification)
CORRECT (new):
<task-notification> --> RUN VALIDATION --> IF PASS --> complete
--> IF FAIL --> retry/escalate
^ VERIFY then trust---
Completion-Claim Kernel
Apply this kernel whenever an artifact says a bead, task, epic, gate, or phase is DONE, closed, complete, green, or ready to ship:
1. Treat status fields and agent summaries as claims until fresh evidence proves the contract is satisfied. 2. Rerun the narrowest checks that prove the acceptance criteria now, and keep command, exit code, and relevant output in the verdict or linked evidence. 3. Separate test existence, command success, and non-trivial assertions against production paths. Flag skipped tests, assert true, hardcoded success paths, disabled code, and mocks where the spec required real integration. 4. Map each claimed acceptance criterion to file:line evidence, named tests, raw logs, or explicit no-file evidence. 5. Check parent/child reconciliation, dependency graph health, orphaned acceptance criteria, and cross-bead contract drift. 6. Label deterministic suspicion as flagged-for-review until rerun evidence proves a true failure.
The evidence minimum for a completion claim is: claimed scope, acceptance criterion, proof artifact, rerun command when applicable, and parent/dependency reconciliation outcome.
---
Specifying Validation Requirements
TaskCreate Metadata
Validation requirements are specified with metadata.issue_type plus the metadata.validation field when creating tasks:
TaskCreate(
subject="Implement feature X",
description="...",
metadata={
"issue_type": "feature",
"validation": {
"files_exist": ["path/to/file1", "path/to/file2"],
"command": "npm test",
"content_check": {"file": "path/to/file", "pattern": "expected_pattern"},
"tests": "pytest tests/test_feature.py",
"lint": "eslint src/"
}
}
)Required Validation by Issue Type
| Issue Type | Requirement |
|---|---|
feature, bug, task | metadata.validation.tests is required, plus at least one structural check: files_exist and/or content_check |
docs, chore, ci | Explicit exemption from required tests; use structural and/or command/lint checks as applicable |
If a feature/bug/task TaskCreate is missing required test or structural checks, do not dispatch the task. If a TaskCreate is missing metadata.issue_type, do not dispatch it once active constraints are in play; task validation cannot apply issue-scoped prevention safely without it. Treat this as part of the closed flywheel, not extra metadata ceremony: a finding only shifts left into deterministic validation when applicability can be resolved without guessing.
Active Compiled Constraint Runtime
Active compiled constraints execute through hooks/task-validation-gate.sh. The hook reads .agents/constraints/index.json as the only executable surface; .agents/constraints/<id>.sh files are human-reviewable companion artifacts and must not be executed directly.
Plans that add or depend on active constraints must name these detector kinds explicitly:
content_pattern- literal must-contain or must-not-contain checks over normalized target files.paired_files- companion-file checks derived from normalized changed files.restricted_command- bare-name commands that passvalidate_restricted_cmdbefore execution.
Applicability is resolved from concrete task and repository inputs:
metadata.issue_type, matched againstapplies_to.issue_types.metadata.files.metadata.validation.files_exist.metadata.validation.content_check[].file.- staged, unstaged, and untracked git changed files.
applies_to.path_globsandapplies_to.languages, applied to the normalized target-file set.
If any active constraint declares issue-type applicability, the task payload must include metadata.issue_type; do not infer it from prose.
Validation Types
| Type | Schema | Description |
|---|---|---|
files_exist | string[] | List of file paths that must exist after task completion |
command | string | Shell command that must exit with code 0 |
content_check | {file: string, pattern: string} | File must contain pattern (regex supported) |
tests | string | Test command that must pass |
lint | string | Lint command that must pass |
custom | {name: string, command: string} | Named custom check |
cross_cutting | {name, type, ...}[] | Epic-level constraints from "Always" boundaries, applied to every task |
Multiple Checks
All specified checks must pass. Order of execution:
1. files_exist - fastest, fail-fast 2. content_check - fast pattern matching 3. lint - catch style issues before tests 4. tests - comprehensive verification 5. command - custom commands last 6. custom - any additional custom checks 7. cross_cutting - epic-level constraints last
---
Validation Check Details
files_exist
Verifies that specified files exist after task completion.
Schema:
{
"files_exist": ["src/auth.py", "tests/test_auth.py"]
}Execution:
for file in files_exist:
if not os.path.exists(file):
FAIL("File not found: " + file)
PASSUse when: Task creates new files.
command
Runs arbitrary shell command, checks exit code.
Schema:
{
"command": "make build"
}Execution:
result = subprocess.run(command, shell=True)
if result.returncode != 0:
FAIL("Command failed with exit code: " + result.returncode)
PASSUse when: Need custom build/validate step.
content_check
Verifies file contains expected content.
Schema:
{
"content_check": {
"file": "src/config.py",
"pattern": "API_VERSION = \"2.0\""
}
}Multiple patterns:
{
"content_check": [
{"file": "src/auth.py", "pattern": "def authenticate"},
{"file": "src/auth.py", "pattern": "def authorize"}
]
}Execution:
content = read_file(file)
if not regex.search(pattern, content):
FAIL("Pattern not found in " + file + ": " + pattern)
PASSUse when: Task must implement specific functions/patterns.
tests
Runs test suite, checks for passing tests.
Schema:
{
"tests": "pytest tests/test_feature.py -v"
}Execution:
result = subprocess.run(tests, shell=True)
if result.returncode != 0:
FAIL("Tests failed")
PASSUse when: Task has associated tests.
lint
Runs linter, checks for clean output.
Schema:
{
"lint": "ruff check src/"
}Execution:
result = subprocess.run(lint, shell=True)
if result.returncode != 0:
FAIL("Lint errors found")
PASSUse when: Code quality must be maintained.
custom
Named custom check for documentation.
Schema:
{
"custom": {
"name": "Database migration",
"command": "python manage.py migrate --check"
}
}Use when: Domain-specific validation needed.
cross_cutting
Epic-level constraints applied to EVERY task. Derived from "Always" boundaries in the plan.
Schema:
{
"cross_cutting": [
{"name": "auth-required", "type": "content_check", "file": "src/middleware.go", "pattern": "AuthMiddleware"},
{"name": "tests-pass", "type": "tests", "tests": "go test ./..."},
{"name": "builds-clean", "type": "command", "command": "go build ./..."}
]
}Each entry is a flat object with:
name(string): Human-readable label for the constrainttype(string): One offiles_exist,content_check,command,tests,lint- Remaining fields: Same as the corresponding validation type above
Execution:
# Run AFTER all per-task checks pass
for check in cross_cutting:
run_check(check.type, check) # Same execution logic as per-task checks
if FAIL:
FAIL(f"Cross-cutting constraint '{check.name}' failed")
PASSUse when: Plan defines "Always" boundaries that apply to every issue in the epic. /crank reads these from the epic description and injects into every worker task.
Source: Cross-cutting constraints flow from plan boundaries:
Plan "Always" boundaries → Epic description → /crank extracts → TaskCreate metadata---
Failure Handling
Retry Strategy
| Failure Type | Retry Action | Max Retries |
|---|---|---|
files_exist | Re-spawn agent with explicit file list | 2 |
command | Re-spawn with command output as context | 3 |
content_check | Re-spawn with exact pattern required | 2 |
tests | Re-spawn with test failure details | 3 |
lint | Re-spawn with lint errors | 2 |
Retry Context
When retrying, include failure context in the agent's prompt:
RETRY task #<id>: Previous attempt failed validation.
## Original Task
<original description>
## Validation Failure
Type: <check_type>
Details: <failure_output>
## Required Fix
<specific guidance based on failure>
Complete the task and ensure validation passes."
)Escalation
After MAX_RETRIES failures:
1. Mark task as blocked:
TaskUpdate(taskId="<id>", status="blocked")2. Record failure history:
TaskUpdate(taskId="<id>", description="<original>
## ESCALATED - Validation Failures
Attempt 1: <failure>
Attempt 2: <failure>
Attempt 3: <failure>
Requires human review.")3. Continue with other tasks (don't block entire swarm)
---
Default Validation
When no explicit validation is specified (docs/chore/ci exemption path or legacy tasks), apply minimal checks:
def default_validation(task_id, worker_artifacts):
# Check agent didn't end with errors
# (parse task notification / SendMessage envelope for failure indicators)
# Check worker reported artifacts exist
# Workers do NOT commit — they write files and report via SendMessage.
# The team lead validates artifacts exist before committing.
for artifact in worker_artifacts:
if not os.path.exists(artifact):
return FAIL(f"Reported artifact not found: {artifact}")
# Check for modified files (workers write in main tree or isolated worktrees)
result = subprocess.run("git status --porcelain", shell=True, capture_output=True)
if not result.stdout.strip():
return WARN("No file changes detected — worker may not have written anything")
return PASSNote: Workers MUST NOT commit. The team lead is the sole committer.
Validation checks for file existence and content, not commit history.
The lead commits all validated changes after the wave completes.
See skills/swarm/SKILL.md "Git Commit Policy" for details.---
Integration with Crank
When crank invokes swarm, it can specify validation at the epic level:
# Crank creates tasks from beads issues
for issue in ready_issues:
TaskCreate(
subject=f"{issue.id}: {issue.title}",
description=issue.description,
metadata={
"beads_id": issue.id,
"validation": build_validation_from_issue(issue)
}
)Building Validation from Issue
def build_validation_from_issue(issue):
issue_type = (issue.type or "").lower()
validation = {}
files_mentioned = extract_file_paths(issue.description)
patterns = extract_code_patterns(issue.description)
if issue_type in {"feature", "bug", "task"}:
test_cmd = detect_test_command(issue)
if not test_cmd:
raise ValueError("feature|bug|task require metadata.validation.tests")
validation["tests"] = test_cmd
if files_mentioned:
validation["files_exist"] = files_mentioned
if patterns:
validation["content_check"] = patterns
if "files_exist" not in validation and "content_check" not in validation:
raise ValueError("feature|bug|task require files_exist or content_check")
return validation
if issue_type in {"docs", "chore", "ci"}:
# Explicit test exemption path for non-implementation work.
if files_mentioned:
validation["files_exist"] = files_mentioned
if patterns:
validation["content_check"] = patterns
return validation
# Unknown type: best-effort inference with structural checks only.
if files_mentioned:
validation["files_exist"] = files_mentioned
if patterns:
validation["content_check"] = patterns
return validation---
Examples
Example 1: New Feature with Tests
TaskCreate(
subject="Add user authentication",
description="Implement JWT-based authentication...",
metadata={
"validation": {
"files_exist": [
"src/auth/jwt.py",
"src/auth/__init__.py",
"tests/test_auth.py"
],
"content_check": [
{"file": "src/auth/jwt.py", "pattern": "def create_token"},
{"file": "src/auth/jwt.py", "pattern": "def verify_token"}
],
"tests": "pytest tests/test_auth.py -v",
"lint": "ruff check src/auth/"
}
}
)Example 2: Bug Fix
TaskCreate(
subject="Fix null pointer in user lookup",
description="Handle case where user not found...",
metadata={
"validation": {
"content_check": {
"file": "src/users/lookup.py",
"pattern": "if user is None"
},
"tests": "pytest tests/test_users.py::test_user_not_found -v"
}
}
)Example 3: Documentation Update
TaskCreate(
subject="Update API docs for v2",
description="Update README with new endpoints...",
metadata={
"validation": {
"files_exist": ["docs/api/v2.md"],
"content_check": {
"file": "docs/api/v2.md",
"pattern": "## Authentication"
}
}
}
)Example 4: Infrastructure Change
TaskCreate(
subject="Add Redis caching layer",
description="Configure Redis for session caching...",
metadata={
"validation": {
"files_exist": ["docker-compose.yml", "src/cache/redis.py"],
"command": "docker-compose config --quiet",
"content_check": {
"file": "docker-compose.yml",
"pattern": "redis:"
}
}
}
)---
See Also
skills/swarm/SKILL.md- Main swarm skill with validation integrationskills/crank/SKILL.md- Crank orchestration with validation loopskills/crank/failure-taxonomy.md- Comprehensive failure handlingskills/validate/SKILL.md- Comprehensive validation skill
Related skills
How it compares
Prefer Claude TeamCreate or Codex spawn_agent backends documented in shared; use background Task fallback only when those native options are unavailable.
FAQ
Can users invoke shared directly?
No; these are not directly invocable skills loaded by other skills when needed.
What does validation-contract cover?
Verification requirements for accepting spawned work from agent backends.
Which skills consume shared?
council, crank, swarm, research, and implement load these references JIT.
Is Shared safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.