Skip to content

Latest commit

 

History

History
1252 lines (1026 loc) · 76.2 KB

config-reference.md

File metadata and controls

1252 lines (1026 loc) · 76.2 KB

Configuration Reference

Complete reference for ~/.ouroboros/config.yaml and all related environment variables.

Source of truth: src/ouroboros/config/models.py and src/ouroboros/config/loader.py

Run ouroboros config init to generate defaults. Edit ~/.ouroboros/config.yaml directly to apply changes.


File Layout

~/.ouroboros/
├── config.yaml          # Main configuration (this document)
├── credentials.yaml     # API keys (chmod 600, do not put secrets in config.yaml)
├── ouroboros.db         # Legacy SQLite event store (preserved when present)
├── seeds/               # Generated seed YAML files
├── data/
│   └── ouroboros.db     # Default configured SQLite event store for new installs
├── logs/
│   └── ouroboros.log    # Log output
└── .env                 # Optional; loaded automatically by the CLI

Codex CLI Users

For Codex-backed Ouroboros workflows:

  • Put persistent Ouroboros role overrides in ~/.ouroboros/config.yaml.
  • Use ouroboros config --web (or ouroboros config in a terminal) to select Use Codex default model for Codex's current default model, or choose Enter another model ID… to pin a model for each pipeline stage, including Execute. ~/.codex/config.toml is for the Codex MCP registration and any user-managed native profiles.
  • The loader does not hardcode a model for Codex. Every model key defaults to auto, which resolves Codex-backed lookups to Codex's default sentinel. A model string you set takes effect only with models.pin: true.
  • Use llm_profiles and llm_role_profiles when you want portable task profiles that can map to Codex CLI profiles or to ordinary model settings for other providers.

Codex Role Override Map

Role config.yaml key
Clarification / interview clarification.default_model
Agent execution execution.default_model
QA verdict llm.qa_model
Semantic evaluation evaluation.semantic_model
Consensus simple voting consensus.models
Consensus deliberative roles consensus.advocate_model, consensus.devil_model, consensus.judge_model

execution.default_model covers the EXECUTE-stage planning roles (atomicity, decomposition, agent_runtime_implementation) alongside Execute-stage runtime calls. With models.pin: true, an explicit model id (or OUROBOROS_EXECUTION_MODEL) pins those roles. When it is unset or set to default/current, CLI backends choose their own default; LiteLLM retains its provider-qualified semantic model because it has no model-free default.

Recommended baseline: use Use Codex default model. Setup assigns each Ouroboros role a per-invocation reasoning effort (fast: low, standard: medium, deep: high, frontier: xhigh) without pinning a Codex model, so Codex's current default remains in control.

Portable Task Profiles

llm_profiles are top-level, provider-neutral task profiles. llm_role_profiles maps logical Ouroboros roles to those profiles. For Codex, the built-in mappings use reasoning_effort, which is passed to codex exec for that call and does not change the user's global model. A user-managed provider profile remains supported and is passed as codex exec --profile <name>. Role mappings preserve each call site's tuned sampling and token settings.

llm_profiles:
  fast:
    max_turns: 1
    temperature: 0.2
    providers:
      codex:
        reasoning_effort: low
      litellm:
        model: openrouter/openai/gpt-5.3-codex-spark

  standard:
    max_turns: 3
    temperature: 0.3
    providers:
      codex:
        reasoning_effort: medium

  deep:
    max_turns: 5
    temperature: 0.4
    providers:
      codex:
        reasoning_effort: high
      claude_code:
        model: claude-opus-5
      gemini:
        model: gemini-2.5-pro
      opencode:
        model: openai/gpt-5.4
      litellm:
        model: openrouter/anthropic/claude-opus-5

  frontier:
    max_turns: 8
    temperature: 0.4
    providers:
      codex:
        reasoning_effort: xhigh

llm_role_profiles:
  ambiguity: deep
  assertion_extraction: fast
  brownfield: fast
  context_compression: deep
  mechanical_detection: fast
  question_classification: deep
  qa: frontier
  brownfield_explore: frontier
  clarification: frontier
  dependency_analysis: standard
  pm_interview: deep
  seed_generation: deep
  consensus_advocate: deep
  consensus_perspective: deep
  consensus_vote: deep
  ontology_analysis: deep
  pm_document: deep
  reflect: deep
  semantic_evaluation: deep
  wonder: frontier
  consensus_judge: frontier
  agent_runtime: standard
  agent_runtime_implementation: standard
  agent_runtime_interview: deep
  agent_runtime_coordinator: standard
  agent_runtime_evaluation: deep

Resolution order is: explicit request-level model pins, role mapping, profile provider mapping for the active backend, existing *_model field, then backend default behavior.

Codex setup applies those effort values with a per-invocation model_reasoning_effort override. It does not create or select an Ouroboros-owned Codex profile, so an App or CLI model change is automatically inherited. Existing user-created Codex profiles remain available when explicitly selected.

If ~/.codex/config.toml already contains a URL-based Ouroboros MCP server, setup preserves it instead of replacing it with a stdio command block:

[mcp_servers.ouroboros]
url = "http://127.0.0.1:12000/mcp"

Top-Level Sections

Section Class Purpose
orchestrator OrchestratorConfig Runtime backend selection and agent permissions
llm LLMConfig LLM-only flow defaults (model selection, permission mode)
models ModelsConfig Automatic model selection and the explicit pin switch
economics EconomicsConfig PAL Router tier definitions and escalation thresholds
clarification ClarificationConfig Phase 0 — Interview / Big Bang settings
execution ExecutionConfig Phase 2 — Double Diamond execution settings
resilience ResilienceConfig Phase 3 — Stagnation detection and lateral thinking
evaluation EvaluationConfig Phase 4 — 3-stage evaluation pipeline settings
consensus ConsensusConfig Phase 5 — Multi-model consensus settings
llm_profiles dict[str, LLMTaskProfileConfig] Provider-neutral LLM task profiles
llm_role_profiles dict[str, str] Logical role to LLM task profile mapping
persistence PersistenceConfig SQLite event store settings
drift DriftConfig Drift monitoring thresholds
runtime_controls RuntimeControlsConfig Long-running workflow liveness and progress controls
logging LoggingConfig Log level, path, and verbosity
boundary BoundaryConfig Check package boundary of ooo run (on by default)

orchestrator

Controls how Ouroboros launches and communicates with the agent runtime backend.

orchestrator:
  runtime_backend: claude       # "claude" | "claude_mcp" | "codex" | "opencode" | "hermes" | "gemini" | "kiro" | "copilot" | "pi" | "omp" | "gjc" | "antigravity" | "grok" | "zcode"
  permission_mode: acceptEdits  # "default" | "acceptEdits" | "bypassPermissions"
  opencode_permission_mode: bypassPermissions
  max_parallel_workers: 3       # Maximum concurrent AC workers
  cli_path: null                # Path to Claude CLI binary; null = resolve from PATH/runtime default
  codex_cli_path: null          # Path to Codex CLI binary; null = resolve from PATH
  opencode_cli_path: null       # Path to OpenCode CLI binary; null = resolve from PATH
  copilot_cli_path: null        # Path to Copilot CLI binary; null = resolve from PATH
  pi_cli_path: null             # Path to Pi CLI binary; null = resolve from PATH
  omp_cli_path: null            # Path to OMP (Oh My Pi) CLI binary; null = resolve from PATH
  zcode_cli_path: null          # Path to zcode.cjs or a zcode executable
  default_max_turns: 10
Option Type Default Description
runtime_backend "claude_mcp" | "claude" | "codex" | "opencode" | "hermes" | "gemini" | "kiro" | "copilot" | "pi" | "omp" | "gjc" | "antigravity" | "grok" | "zcode" "claude" The agent runtime backend used for workflow execution. claude is the default Agent SDK runtime in an MCP 1.x environment. claude_mcp is the explicit out-of-process CLI worker used by the isolated MCP 2 server. Overridable via OUROBOROS_AGENT_RUNTIME. See runtime capability matrix.
permission_mode "default" | "acceptEdits" | "bypassPermissions" "acceptEdits" Stored permission preference. Runner-driven seed execution forces the native bypassPermissions equivalent for both fresh and resumed dispatches wherever the backend exposes an approval surface; persisted handles cannot downgrade it. Pi and GJC expose no separate approval flag and run headlessly without an approval dialogue.
opencode_permission_mode "default" | "acceptEdits" | "bypassPermissions" "bypassPermissions" Permission mode when using the OpenCode runtime. Overridable via OUROBOROS_OPENCODE_PERMISSION_MODE.
max_parallel_workers int >= 1 3 Maximum Acceptance Criteria workers the adaptive dispatch window may reach. Overridable via OUROBOROS_MAX_PARALLEL_WORKERS. Invalid explicit values fail instead of falling back to the default. The native Claude backend starts at this value and is paced by its RPM/TPM bucket. CLI runtimes whose underlying LLM limits are unknown (hermes, codex, gemini, opencode, ...) start at 1, halve the window on 429 pressure, honor Retry-After, and add one worker after sustained success until this ceiling.
cli_path string | null null Absolute path to the Claude CLI binary (~ is expanded). When null, the active Claude runtime resolves its normal CLI default. Overridable via OUROBOROS_CLI_PATH.
codex_cli_path string | null null Absolute path to the Codex CLI binary (~ is expanded). When null, resolved from PATH at runtime. Overridable via OUROBOROS_CODEX_CLI_PATH.
opencode_cli_path string | null null Absolute path to the OpenCode CLI binary (~ is expanded). When null, resolved from PATH at runtime. Overridable via OUROBOROS_OPENCODE_CLI_PATH.
copilot_cli_path string | null null Absolute path to the GitHub Copilot CLI binary (~ is expanded). When null, resolved from PATH at runtime. Overridable via OUROBOROS_COPILOT_CLI_PATH.
pi_cli_path string | null null Absolute path to the Pi CLI binary (~ is expanded). When null, resolved from PATH at runtime. Overridable via OUROBOROS_PI_CLI_PATH.
omp_cli_path string | null null Absolute path to the OMP (Oh My Pi) CLI binary (~ is expanded). When null, resolved from PATH at runtime. Overridable via OUROBOROS_OMP_CLI_PATH.
zcode_cli_path string | null null Path to the Zcode app-bundle zcode.cjs script, a standalone script, or a directly executable zcode wrapper. Official app bundles use their bundled Electron/Node runtime. Resolution falls back to the macOS app bundle, then PATH. Overridable via OUROBOROS_ZCODE_CLI_PATH.
ourocode_cli_path string | null null Absolute path to the ourocode CLI binary (~ is expanded). Used by the LLM-only ourocode backend; when null, resolved from PATH at runtime. Overridable via OUROBOROS_OUROCODE_CLI_PATH.
dsh_cli_path string | null null Absolute path to the DeepSeek Harness ACP server binary dsh-acp-demo (~ is expanded). Used by the LLM-only dsh backend; when null, resolved from PATH at runtime. Overridable via OUROBOROS_DSH_CLI_PATH.
dsh_config_path string | null null Absolute path to the trusted Cordis composition the dsh backend loads. Required for that backend — it fails closed rather than guessing, and a relative path is rejected because it would resolve against the untrusted project cwd. Overridable via OUROBOROS_DSH_CONFIG_PATH. See the DeepSeek Harness guide.
default_max_turns int >= 1 10 Default maximum number of turns per agent execution task.

llm

Defaults for LLM-only flows (interview, seed generation, QA, analysis). The orchestrator section governs agent runtime execution; the llm section governs model-level LLM calls within the orchestration pipeline.

llm:
  backend: claude_code
  permission_mode: default
  opencode_permission_mode: acceptEdits
  qa_model: auto
  dependency_analysis_model: auto
  ontology_analysis_model: auto
  context_compression_model: auto
Option Type Default Description
backend "claude" | "claude_code" | "litellm" | "codex" | "opencode" | "hermes" | "gemini" | "kiro" | "copilot" | "goose" | "pi" | "omp" | "ourocode" | "gjc" | "zcode" | "dsh" "claude_code" Default backend for LLM-only flows. Overridable via OUROBOROS_LLM_BACKEND. ourocode and dsh are LLM-only and are not valid for orchestrator.runtime_backend. dsh additionally requires orchestrator.dsh_config_path; see the DeepSeek Harness guide. The runtime alias deepseek_harness is not accepted here — this field is validated against the literals above, so persist dsh.
permission_mode "default" | "acceptEdits" | "bypassPermissions" "default" Permission mode for non-OpenCode LLM flows. Overridable via OUROBOROS_LLM_PERMISSION_MODE.
opencode_permission_mode "default" | "acceptEdits" | "bypassPermissions" "acceptEdits" Permission mode for OpenCode-backed LLM flows. Overridable via OUROBOROS_OPENCODE_PERMISSION_MODE.
qa_model string "auto" Model used for post-execution QA verdict generation. Overridable via OUROBOROS_QA_MODEL.
dependency_analysis_model string "auto" Model used for AC dependency analysis. Overridable via OUROBOROS_DEPENDENCY_ANALYSIS_MODEL.
ontology_analysis_model string "auto" Model used for ontological analysis. Overridable via OUROBOROS_ONTOLOGY_ANALYSIS_MODEL.
context_compression_model string "auto" Model used for workflow context compression. Overridable via OUROBOROS_CONTEXT_COMPRESSION_MODEL.

Every model field in llm, clarification, resilience, evaluation, and consensus defaults to auto and follows the rules in models.

When llm.backend is ourocode, model fields are OUROCODE_MODEL selectors, not raw Anthropic model IDs. The supported selectors are claude, claude_api, codex, and gemini; default and shipped Claude default pins resolve to claude so journal metadata matches the ACP child process.


Choosing models

Most users need no model setting at all, and one switch when they do.

  • Default: auto. Every role runs the latest model of its tier (frugal, standard, frontier) on the backend you use. A new model release needs no config change.
  • One switch for every role. ouroboros config set models.default frontier (or frugal, standard, a model id, or back to auto). For a single command, pass --model to ouroboros run, ouroboros interview, or ouroboros pm, for example ouroboros run seed.yaml --model frontier. Tier names work on every backend; Claude aliases such as opus mean something only on Claude backends.
  • See what runs. ouroboros config show prints a models table (role, tier, backend, resolved model, source) under a models: auto (pin off), models: pinned, or models: <value> for every role (<source>) header, and lists saved per-role ids that are not applied under ignored (pin off) with the hint set models.pin: true to use. ouroboros config show --json carries the same data under models.
  • Pin for research. ouroboros config set models.pin true (or --pin-models for one command) runs the per-role ids saved in the role fields below. litellm and copilot take provider-owned ids, so they use saved ids whether or not pin is on.

A fresh config from ouroboros setup or ouroboros config init writes models.default: auto and no per-role model ids. Copilot setup also writes the model you pick from Copilot's catalog, because Copilot needs an id from that catalog.


models

How every role's model is chosen. The default, auto, runs each role on the latest model of its tier for the active backend, so a new model release needs no config change.

models:
  default: auto   # auto | frugal | standard | frontier | <model id>
  pin: false      # true = run the model ids persisted in role fields
Option Type Default Description
default string "auto" auto picks each role's own tier. A tier name (frugal, standard, frontier) applies that tier to every role, and a model id applies that model to every role. Overridable via OUROBOROS_MODEL.
pin bool false Run the concrete model ids persisted in role fields (llm.qa_model, clarification.default_model, and the rest) and in the OUROBOROS_*_MODEL variables. While off, those ids are ignored with one warning per process, except on litellm and copilot (see below). Turn on for research or reproducibility. Overridable via OUROBOROS_PIN_MODELS (1/0).

Each role belongs to one tier: interview, seed, PM, brownfield explore, semantic evaluation, Wonder, Reflect, lateral thinking, and the consensus roles are frontier; Execute, validation, QA, assertion extraction, mechanical detection, dependency analysis, ontology analysis, and context compression are standard; the brownfield repository scan is frugal. How a tier becomes a model depends on the backend:

Backend auto resolves to
claude, claude_mcp The Claude CLI alias for the tier (haiku, sonnet, opus), which the CLI maps to its newest model.
litellm (including OpenRouter) The configured model string, used as-is whether or not pin is on. With nothing configured, the shipped Anthropic id for the role. auto is never sent to LiteLLM.
copilot The configured model string (from Copilot's own model catalog), used as-is. With nothing configured, Copilot's default model.
Every other backend (Codex, OpenCode, Gemini, Goose, Kiro, Hermes, Pi, OMP, gjc, Antigravity, Grok, Zcode, ourocode, dsh, host) default: the CLI's own configured model.

A model passed for one invocation (for example an explicit MCP model argument, or --model on ouroboros run, interview, and pm) always applies.


llm_profiles and llm_role_profiles

llm_profiles define reusable task profiles outside any single provider. llm_role_profiles chooses which profile a logical Ouroboros task role should use.

Common role keys include clarification, seed_generation, assertion_extraction, qa, semantic_evaluation, wonder, reflect, consensus_vote, consensus_advocate, consensus_judge, dependency_analysis, context_compression, ontology_analysis, and mechanical_detection.

Profile fields:

Field Type Description
model string | null Portable model override used when no provider-specific model is set.
temperature float | null Portable temperature override.
max_tokens int | null Portable token limit override.
top_p float | null Portable nucleus sampling override.
max_turns int | null Portable CLI-agent turn budget where supported.
reasoning_effort "low" | "medium" | "high" | null Portable effort dial for providers with native reasoning-effort support. A provider-specific mapping may additionally use Codex xhigh.
providers dict Backend-specific overrides keyed by codex, claude_code, gemini, opencode, litellm, or provider aliases such as openrouter.

Provider-specific fields use the same keys plus profile. profile is currently backend-native metadata; Codex maps it to codex exec --profile <name>, while non-Codex adapters ignore it unless they add native profile support later. Role-based resolution uses these profile fields for model/native-profile routing, max_turns, and reasoning_effort; it intentionally preserves request-level temperature, max_tokens, and top_p so existing task-specific tuning does not change just because a role was annotated. Explicit CompletionConfig.profile requests use the profile's full sampling/token envelope. ouroboros setup --runtime codex installs missing default role mappings and preserves existing profile definitions, existing Codex provider model pins, and skips role mappings where explicit legacy model overrides are already configured.

Codex agent-runtime tasks also use these mappings. Runtime handles with session_role: implementation, coordinator, interview, or evaluation resolve through agent_runtime_<session_role>; tasks without a role fall back to agent_runtime. Explicit runtime models still win and are passed with --model instead of --profile.


economics

Configures the PAL Router (Progressive Adaptive LLM): cost tiers, escalation on failure, and downgrade on success.

economics:
  default_tier: frugal          # "frugal" | "standard" | "frontier"
  escalation_threshold: 2       # Retry attempt escalation begins; then +1 tier per retry
  downgrade_success_streak: 5   # Consecutive successes before downgrading tier
  tiers:
    frugal:
      cost_factor: 1
      intelligence_range: [9, 11]
      models:
        - provider: openai
          model: gpt-5.1-codex-mini
        - provider: google
          model: gemini-2.0-flash
        - provider: anthropic
          model: claude-haiku-4-5
      use_cases:
        - routine_coding
        - log_analysis
        - stage1_fix
    standard:
      cost_factor: 10
      intelligence_range: [14, 16]
      models:
        - provider: openai
          model: gpt-5-codex
        - provider: anthropic
          model: claude-sonnet-4-6
        - provider: google
          model: gemini-2.5-pro
      use_cases:
        - logic_design
        - stage2_evaluation
        - refactoring
    frontier:
      cost_factor: 30
      intelligence_range: [18, 20]
      models:
        - provider: openai
          model: gpt-5.2
        - provider: anthropic
          model: claude-opus-5
      use_cases:
        - consensus
        - lateral_thinking
        - big_bang
Option Type Default Description
default_tier "frugal" | "standard" | "frontier" "frugal" The starting tier used when no task-specific override applies.
escalation_threshold int >= 1 2 The retry attempt at which tier escalation begins. From this attempt onward the tier climbs one notch per retry (progressive), capped at the frontier tier. Top-level and untrusted decomposed work start at the base tier. Only a decomposed child with explicit trust authorization starts one tier lower, so that trusted-child ladder may require one additional retry to reach the same frontier ceiling. Current live decomposition supplies no such trust authorization.
downgrade_success_streak int >= 1 5 Number of consecutive successes at the current tier before downgrading to the previous tier.
tiers dict[str, TierConfig] (see above) Tier definitions keyed by name. Model-tier routing keeps each tier's shape and cost, but the model ids listed here apply only with models.pin: true or on backends that keep configured model ids (litellm, copilot); otherwise each tier runs the latest model of that tier (Claude: haiku/sonnet/opus; runtimes that choose their own model: default). A resumed run replays the models it started on.

TierConfig fields:

Field Type Description
cost_factor int >= 1 Relative cost multiplier (1 = frugal, 10 = standard, 30 = frontier).
intelligence_range [int, int] Min/max intelligence score for this tier (min must be ≤ max).
models list[ModelConfig] Models available in this tier.
use_cases list[str] Descriptive tags for which task types this tier is suited for.

ModelConfig fields:

Field Type Description
provider string Provider name (openai, anthropic, google, openrouter).
model string Model identifier. Provider formats differ: Anthropic uses claude-opus-5, while OpenRouter uses openrouter/anthropic/claude-opus-5.

clarification

Controls Phase 0 — the Socratic Interview and seed generation.

clarification:
  ambiguity_threshold: 0.2    # Interview completes when ambiguity score <= this value
  max_interview_rounds: 10    # Hard ceiling on clarification rounds
  model_tier: standard        # "frugal" | "standard" | "frontier"
  default_model: auto
Option Type Default Description
ambiguity_threshold float [0.0, 1.0] 0.2 Maximum ambiguity score to allow seed generation to proceed. Interview loops until the score falls at or below this value.
max_interview_rounds int >= 1 10 Maximum number of question-answer rounds regardless of ambiguity score.
model_tier "frugal" | "standard" | "frontier" "standard" PAL tier used for the clarification phase.
default_model string "auto" Default model for interview and seed generation (see models). Overridable via OUROBOROS_CLARIFICATION_MODEL.

execution

Controls Phase 2 — the Double Diamond execution loop.

execution:
  max_iterations_per_ac: 10   # Maximum execution iterations per acceptance criterion
  retrospective_interval: 3   # Iterations between automatic retrospectives
  auto_evaluate: true          # Evaluate completed runs, including failed runs with artifacts
  auto_evolve: true            # Continue rejected evaluations through bounded Ralph
  auto_evolve_max_generations: 3  # Automatic Ralph budget, clamped to 1..10
  default_model: null         # null/default/current = let the selected runtime choose
  project_guidance:            # Explicit project-local execution guidance allowlist
    - team
Option Type Default Description
max_iterations_per_ac int >= 1 10 Maximum number of execution iterations for a single acceptance criterion before the system escalates or declares failure.
retrospective_interval int >= 1 3 Number of iterations between automatic retrospective evaluations.
auto_evaluate bool true Enqueue formal 3-stage evaluation after a completed background run has a session and artifact. This includes unsuccessful AC execution; handler-level failures without an evaluable run are excluded. Applies to MCP ooo run and to the terminal ouroboros run, which waits for the evaluation; ouroboros run --auto-evaluate/--no-auto-evaluate overrides it for one run.
auto_evolve bool true When formal evaluation returns an explicit rejection, seed a generation-1 lineage snapshot and enqueue a bounded Ralph continuation. Per-call auto_evolve overrides this setting. The terminal ouroboros run follows the same rule and waits for the Ralph job; ouroboros run --auto-evolve/--no-auto-evolve overrides it for one run.
auto_evolve_max_generations int 3 Maximum generations for automatically chained Ralph work. Values are clamped to Ralph's supported 1..10 range.
run_verify_commands bool true Check an AC's success contract (expected artifacts and verify_command) before accepting it. While on, the evidence verifier may also replay allowlisted commands the worker ran, in a copy of the workspace under the execution sandbox (writes only inside the copy, no network; nothing is replayed where the sandbox is unavailable), to corroborate tests_passed and commands_run claims the transcript alone cannot prove (see Verifier Evidence Policy). Off: nothing is executed by the harness.
verify_command_timeout_seconds int >= 1 600 Timeout for each verify_command run and each replayed command.
exec_sandbox bool true Confine the commands the controller runs on its own authority (verifier replay) with the execution sandbox: writes (content, names and metadata) only beneath the workspace copy and a per-run temp directory, no non-loopback network, an allowlisted environment (sandbox-exec on macOS; Landlock plus a seccomp metadata filter on Linux, where metadata inside the copy is read-only too; a per-run AppContainer on Windows, where loopback reaches only the command's own processes, the NUL device is unavailable, batch files (.cmd, .bat) cannot run and are left indeterminate, and reads are limited to what is granted). On Windows the controller's interpreter and the live dependency trees a workspace copy links to are granted read and execute to one Ouroboros capability SID; that grant is persistent, is recorded in ~/.ouroboros/exec-sandbox/read-grants.jsonl, and is removed with python -c "from ouroboros.runtime.exec_sandbox import remove_persistent_read_grants as r; print(r())" (run it while no Ouroboros command is running). Where no backend works, nothing is run and the outcome is indeterminate (sandbox_unavailable). Unsafe when false: those commands run unconfined, able to write anywhere the user can and to use the network. OUROBOROS_EXEC_SANDBOX overrides it; a project .env cannot. The effective value is sealed when a run starts, and resuming it with a different value is refused.
default_model string | null null Optional Execute-stage model pin, applied with models.pin: true (and always on litellm/copilot). null, an empty value, "auto", "default", or "current" means automatic selection: Claude runs the standard tier alias (sonnet) and other runtimes keep their own current/default model. OUROBOROS_EXECUTION_MODEL has highest precedence, and a present empty env var explicitly clears the saved pin for that process.
project_guidance list[string] [] Guidance IDs loaded from <project-root>/.ouroboros/guidance/<id>/GUIDANCE.md and appended to execution system prompts. This option is config-only and has no environment-variable override.
default_policy "ask" | "efficient" | "quality_first" "ask" Persistent default execution policy for fresh runs. ask preserves the host's interactive efficiency prompt exactly. efficient resolves omitted arguments to adaptive/observe and quality_first to quality_first/off without asking. Explicit invocation arguments always win, resumed sessions keep their persisted immutable contract, and strict frugality assurance never derives from this setting.

Project Execution Guidance

project_guidance is an explicit allowlist. Ouroboros does not scan the repository, user home directory, Codex skills, Claude configuration, or other provider-local instruction stores. For the example above, the only loaded file is:

<project-root>/.ouroboros/guidance/team/GUIDANCE.md

IDs must be safe single path segments containing letters, numbers, ., _, or -. Files must be non-empty UTF-8 text, are resolved in deterministic ID order, and are limited to 16 KiB per file and 32 KiB in total. Missing files, path or symlink escapes, invalid encoding, and size violations fail before execution.

The Seed and its Acceptance Criteria remain authoritative. Guidance cannot grant tools, change sandbox or approval policy, alter evaluation requirements, bypass evaluation, or redefine acceptance criteria. A run persists guidance paths, hashes, and sizes without storing the raw body; resume reloads those persisted IDs and fails closed if the files changed. A runtime that ignores system prompts also rejects runs with enabled project guidance.


boundary

The check package boundary of ooo run (CLI and ouroboros_execute_seed), on by default for every run it applies to (a fresh run; a resumed session keeps the boundary it started with): before the worker starts, a read-only model call writes executable checks for the acceptance criteria, and each check is admitted only if it behaves as declared on the current tree. After the worker stops, the package decides the criteria it covers, before the session's terminal status is recorded; the existing verifier's verdict is kept as advisory for those criteria and decides the others. Edits the worker makes to test configuration inside the workspace (for example pytest.ini, conftest.py, or Django's tests/test_sqlite.py) can make the existing verifier accept; the check package's oracle checks are unaffected, because they call the implementation through the product harness and do not read workspace test configuration (a model-written script check that runs the project's test runner would read it).

MCP results: ouroboros_start_execute_seed reports the closed-value summary (check_package, check_package_status, package_verdict, legacy_verdict, reconciliation, verification_coverage) in its job result, with or without auto_evaluate. The receipts of ouroboros_start_execute_seed and of a background ouroboros_execute_seed call carry check_package and check_package_status: pending (off and not_run when the check package is off; the decision is then the session's terminal status, which the package reconciled before it was recorded). A resumed session's receipt reports the mode the run started with, read from its journal before the receipt is returned; if the journal cannot be read, the resume is refused. An execution dispatched to the OpenCode plugin (a child session outside this process) is not governed: its result carries check_package: not_applied with check_package_reason: plugin_dispatch.

Before the package is frozen, each oracle's stated expected values are compared with a reference implementation the constructor writes in the same reply, run in the same isolated target processes: a held-out case that disagrees is dropped (oracle_inconsistent; an oracle left with no held-out case is dropped too, since only a held-out case can verify a pass), and a criterion whose reference does not reproduce a case the constructor declared as stated in the Seed, or has no reference that runs, is reported as unverified (reference_contradicts_stated_case, reference_unavailable). This catches a slip in a stated value; it does not catch a misreading of the criterion that the cases and the reference share.

Library entry points. An oracle's target is any importable callable of the project, including a classmethod. A case input that is an object rather than a JSON value is written as {"$symbol": "package.module.Name"} (at any depth in the arguments or a method's init); the harness imports it and passes the object. An oracle may declare setup, calls of callables defined in the project with JSON arguments (for Django, django.conf.settings.configure then django.setup), which the harness makes before it resolves the target. A setup call that fails, or an input that names nothing, leaves the base run indeterminate, never a reproduction failure. A returned set compares as the list of its items sorted by their JSON text. The reference check makes no setup call and gives the reference each object input as its dotted path. Command oracles take neither.

What an oracle observation proves. The candidate's code runs inside each target process, so it can write the process's report itself (the frame nonce is a parsing convention, not an authority boundary). A report is therefore evidence of what the candidate's code computed for the inputs it was sent, never proof that the bound function ran: the identity of the bound callable is not proven, and a candidate that computes the right outputs elsewhere in its code passes. That is why every oracle must carry at least one held-out case (a reply without one is refused, oracle_without_held_out_case) and why only a passing held-out case of a reproduction oracle verifies a criterion: its inputs reach the candidate only in the final verification, so matching its expected value means computing the rule's output for an input the candidate had never seen. A report can only make a case pass, never fail one the candidate's code passes, and admission runs the base checkout's code, so neither admission nor a failure depends on it.

Admission is per check: a reproduction check that already passes on the current tree, or a preservation check that already fails on it, is excluded on its own (repro_passes_on_base, preservation_fails_on_base) and the rest of the package is admitted. A criterion keeps the package's authority only while an admitted check covers it (for a bug-fix criterion, an admitted reproduction check). For the criteria left without an admitted check, one more constructor call, before the worker starts, asks for replacement checks and says why the earlier ones were excluded; the replacements go through the same admission. The constructor is asked for a check for every criterion. Whether an admitted check covers a criterion is the only thing that decides who judges it; nothing classifies criteria by kind, and the reason a criterion was left uncovered is descriptive only.

Every criterion the package cannot verify (no admitted check, or no binding) is decided by the existing verifier: its rejection fails the run (exit 1) and is shown as legacy-decided. Only a criterion neither verifier had evidence for (for example the existing verifier's transcript was unavailable) is accepted as unverified; the run then prints an insufficient-verification warning, as it does when half or more of the criteria were not decided by the package.

boundary:
  check_package: off              # on | off; unset = on
  constructor_timeout_seconds: 600
  check_timeout_seconds: 120
  max_construction_attempts: 2
Option Type Default Description
check_package "on" | "off" | unset unset (on) off turns the check package off for every run; unset or on keeps it on. --check-package/--no-check-package and OUROBOROS_CHECK_PACKAGE take precedence, in that order. A bare YAML on/off is accepted. If config.yaml cannot be read, the check package is off for runs that set neither the flag nor the variable, since the unreadable file may hold an off; with no config.yaml at all the default (on) applies. The setting does not depend on telemetry.
constructor_timeout_seconds int (30..3600) 600 Wall-clock budget of one constructor call.
check_timeout_seconds int (5..1800) 120 Per-check timeout during admission and verification.
max_construction_attempts int (1..5) 2 Package versions tried before the worker starts; a version that is not admitted is superseded by the next. The one replacement call for criteria left without an admitted check adds a version outside this budget.

Checks are model-written Python scripts. They run on throwaway copies of the project, with the project's virtualenv interpreter when one is found (else python3), a per-check timeout, and confined by the execution sandbox (see execution.exec_sandbox): a check can write only beneath its own copy and a scratch directory that is removed afterwards, has no non-loopback network, and gets an environment built from an allowlist (PATH, the locale variables, TZ, a few Python I/O settings, and on Windows the system variables a process needs; HOME and the temp directory in the scratch directory; VIRTUAL_ENV when the interpreter is a virtualenv's). The same applies to the processes that run the implementation under an oracle check and to the constructor's reference implementation. Where the sandbox cannot confine a check, the check is undecided and nothing runs. Reads are not restricted: a check can read files you can read, and on macOS (where sandbox-exec cannot deny KERN_PROCARGS2) the arguments and environment of your other processes, Ouroboros included; under Landlock on Linux it cannot. Turn the check package off where that matters. A project .env cannot set OUROBOROS_CHECK_PACKAGE.

Constructor runtimes: the constructor's model call must keep no session on disk (its reply holds the held-out cases), so it runs only on runtimes with a no-persistence mode: Claude Code (--no-session-persistence, through the Agent SDK or the claude -p worker of --runtime claude-cli) and Codex CLI (codex exec --ephemeral). With any other runtime the run has no check package (constructor_session_not_ephemeral:<backend>, before any model call) and the existing verifier decides every criterion, deterministically.

A resumed run keeps the mode and the boundary settings it started with (check_timeout_seconds is recorded on the run's boundary.check_package.enabled journal record): changing the switch or the config between the start and the resume changes nothing for that run.

On resume the package decision is recovered from the journal alone. A journal the run could not have written (a missing, duplicated or conflicting record, or a gap in the package versions) leaves every criterion undecided (boundary_record_missing) and runs no check. When the same process still holds the admitted package, its full decision runs, held-out cases included. In another process the held-out cases are gone (they are never written to disk), so no check runs: covered criteria are undecided (held_out_unavailable) and the existing verifier decides the rest.

resilience

Controls Phase 3 — stagnation detection and lateral thinking.

resilience:
  stagnation_enabled: true
  lateral_thinking_enabled: true
  lateral_model_tier: frontier   # "frugal" | "standard" | "frontier"
  lateral_temperature: 0.8
  wonder_model: auto
  reflect_model: auto
Option Type Default Description
stagnation_enabled bool true Whether stagnation detection is active. When false, the system does not check for SPINNING / OSCILLATION / NO_DRIFT / DIMINISHING_RETURNS patterns.
lateral_thinking_enabled bool true Whether lateral thinking persona rotation is active when stagnation is detected.
lateral_model_tier "frugal" | "standard" | "frontier" "frontier" PAL tier used for lateral thinking calls. Frontier is the default because creative re-framing requires high model capability.
lateral_temperature float [0.0, 2.0] 0.8 LLM sampling temperature for lateral thinking prompts. Higher values produce more divergent outputs.
wonder_model string "auto" Model for the Wonder phase (divergent exploration). Overridable via OUROBOROS_WONDER_MODEL.
reflect_model string "auto" Model for the Reflect phase (convergent synthesis). Overridable via OUROBOROS_REFLECT_MODEL.

evaluation

Controls Phase 4 — the 3-stage evaluation pipeline.

evaluation:
  stage1_enabled: true         # Currently inert in config.yaml; see below
  stage2_enabled: true         # Currently inert in config.yaml; see below
  stage3_enabled: true         # Currently inert in config.yaml; see below
  satisfaction_threshold: 0.8  # Currently inert; the pipeline gate is hardcoded to 0.8
  uncertainty_threshold: 0.3   # Currently inert in config.yaml; see below
  semantic_model: auto
  assertion_extraction_model: auto
Option Type Default Description
stage1_enabled bool true Currently inert in config.yaml. Runtime builders do not copy this field into PipelineConfig.
stage2_enabled bool true Currently inert in config.yaml. Runtime builders do not copy this field into PipelineConfig.
stage3_enabled bool true Currently inert in config.yaml. Runtime builders do not copy this field into PipelineConfig.
satisfaction_threshold float [0.0, 1.0] 0.8 Currently inert. The field is validated but the pipeline compares Stage 2 scores against a hardcoded 0.8; changing this value does not change the gate. See Evaluation Pipeline Guide.
uncertainty_threshold float [0.0, 1.0] 0.3 Currently inert in config.yaml. Runtime builders do not copy it into TriggerConfig.
semantic_model string "auto" Model used for Stage 2 semantic evaluation. Overridable via OUROBOROS_SEMANTIC_MODEL.
assertion_extraction_model string "auto" Model used for extracting verification assertions from seed criteria. Overridable via OUROBOROS_ASSERTION_EXTRACTION_MODEL.

Configuration boundary: the top-level evaluation.stage1_enabled, stage2_enabled, stage3_enabled, and uncertainty_threshold keys are schema-validated placeholders, not runtime controls. The similarly named direct-Python PipelineConfig.stage*_enabled fields and TriggerConfig.uncertainty_threshold are separate and active when explicitly supplied to EvaluationPipeline; see Disabling Stages and Trigger Configuration.


consensus

Controls Phase 5 — multi-model consensus voting and deliberation.

Inert fields in evaluation and consensus. Eight fields in these two sections are schema-only: they validate, persist, and appear in ouroboros config show, but no production code reads them. In evaluation: stage1_enabled, stage2_enabled, stage3_enabled, satisfaction_threshold, and uncertainty_threshold. In consensus: min_models, threshold, and diversity_required. Each is marked in its field table below with what actually controls the behaviour instead. The model fields in both sections are wired.

consensus:
  min_models: 3             # Inert — runtime requires 2 successful post-filter votes
  threshold: 0.67           # Inert — runtime ratio threshold defaults to 0.66
  diversity_required: true  # Currently inert — see the field table below
  models: [auto]
  advocate_model: auto
  devil_model: auto
  judge_model: auto
Option Type Default Description
min_models int >= 2 3 Currently inert. After reviewer-independence filtering, simple consensus separately requires at least two successfully collected votes; this top-level field is not wired to that rule.
threshold float [0.0, 1.0] 0.67 Currently inert. Runtime simple consensus compares approvals divided by successful post-filter votes with direct-Python ConsensusConfig.majority_threshold (default 0.66); this top-level field is not copied into it.
diversity_required bool true Currently inert. The field exists on ConsensusConfig and in the schema, but nothing reads it. Provider diversity depends on actual adapter routing; neither this flag nor differently named roster entries attest it. See Evaluation Pipeline Guide.
models list[string] ["auto"] Model roster for Stage 3 simple voting. auto repeats the consensus role's model three times; on litellm it keeps the shipped OpenRouter roster (openrouter/openai/gpt-4o, openrouter/anthropic/claude-opus-5, openrouter/google/gemini-2.5-pro). With llm.backend: litellm, use provider/model or openrouter/provider/model. With llm.backend: codex, use Codex/OpenAI model IDs such as gpt-5.4. Overridable via OUROBOROS_CONSENSUS_MODELS (comma-separated).
advocate_model string "auto" (litellm: "openrouter/anthropic/claude-opus-5") Model that argues in favor of the proposed solution in deliberative consensus. With llm.backend: codex, this can be a Codex/OpenAI model ID such as gpt-5.4. Overridable via OUROBOROS_CONSENSUS_ADVOCATE_MODEL.
devil_model string "auto" (litellm: "openrouter/openai/gpt-4o") Model that argues against (devil's advocate) in deliberative consensus. With llm.backend: codex, this can be a Codex/OpenAI model ID such as gpt-5.4. Overridable via OUROBOROS_CONSENSUS_DEVIL_MODEL.
judge_model string "auto" (litellm: "openrouter/google/gemini-2.5-pro") Model that renders a final verdict after deliberation. With llm.backend: codex, this can be a Codex/OpenAI model ID such as gpt-5.4. Overridable via OUROBOROS_CONSENSUS_JUDGE_MODEL.

Configuration boundary: consensus.min_models and consensus.threshold are schema-validated placeholders. Runtime simple consensus hardcodes a minimum of two successful post-filter votes and reads the separate direct-Python ConsensusConfig.majority_threshold. Changing these YAML keys does not change either rule.

Backend note: With llm.backend: litellm, consensus models typically go through OpenRouter/LiteLLM and require the corresponding provider credentials (commonly OPENROUTER_API_KEY). With llm.backend: codex, the configured model strings are sent through Codex CLI instead.


persistence

Controls the SQLite event store.

persistence:
  enabled: true
  database_path: data/ouroboros.db   # Relative to ~/.ouroboros/
Option Type Default Description
enabled bool true Whether event sourcing is active. Setting to false disables all persistence — not recommended for production use.
database_path string "data/ouroboros.db" Path shared by the MCP runtime, status/resume recovery commands, and TUI. Relative paths resolve from ~/.ouroboros/. Existing installs keep using a legacy ~/.ouroboros/ouroboros.db until the configured target exists, preserving prior history during migration.

drift

Controls drift monitoring thresholds. Drift measures how far execution has strayed from the original seed (goal + constraint + ontology weighted formula).

drift:
  warning_threshold: 0.3    # Drift score that triggers a warning
  critical_threshold: 0.5   # Drift score that triggers intervention
Option Type Default Description
warning_threshold float [0.0, 1.0] 0.3 Drift score above which a warning event is emitted.
critical_threshold float [0.0, 1.0] 0.5 Drift score above which the system triggers a critical intervention (re-alignment step). Must be ≥ warning_threshold.

runtime_controls

Controls long-running MCP/evolution liveness. These defaults are intended for normal local use: complex productive generations can run for hours, silent hangs are bounded, and repeated activity without material progress is eventually stopped.

runtime_controls:
  mcp_tool_timeout_seconds: 0                 # 0 = no adapter wall-clock cap
  generation_idle_timeout_seconds: 7200       # No EventStore activity for 2 hours
  generation_no_progress_timeout_seconds: 14400  # Activity but no material progress for 4 hours
  generation_safety_timeout_seconds: 0        # Optional final hard cap; 0 = disabled
  watchdog_poll_seconds: 15.0

Material progress is stricter than liveness. Heartbeats, messages, and tool calls keep the generation from being considered idle; phase changes, workflow status changes, stage/subtask completion, and terminal execution events reset the no-progress timer.

Recommended tuning examples:

# Long-running local work
runtime_controls:
  generation_idle_timeout_seconds: 7200
  generation_no_progress_timeout_seconds: 43200

# Strict CI / bounded automation
runtime_controls:
  generation_idle_timeout_seconds: 900
  generation_no_progress_timeout_seconds: 3600
  generation_safety_timeout_seconds: 14400
Option Type Default Description
mcp_tool_timeout_seconds float >= 0 0 Adapter-level MCP timeout for progress-aware tools such as ouroboros_evolve_step. Keep 0 for normal use so the watchdog, not wall-clock time, decides liveness.
generation_idle_timeout_seconds float >= 0 7200 Timeout when no lineage/execution activity is observed. 0 disables idle detection.
generation_no_progress_timeout_seconds float >= 0 14400 Timeout when activity continues but material progress does not. 0 disables no-progress detection.
generation_safety_timeout_seconds float >= 0 0 Optional final hard cap for a generation. 0 disables the hard cap.
watchdog_poll_seconds float > 0 15.0 EventStore polling interval for generation watchdog decisions.

logging

Controls log output.

logging:
  level: info                      # "debug" | "info" | "warning" | "error"
  log_path: logs/ouroboros.log     # Relative to ~/.ouroboros/
  include_reasoning: true
Option Type Default Description
level "debug" | "info" | "warning" | "error" "info" Minimum log level. Set to "debug" for verbose output.
log_path string "logs/ouroboros.log" Path to the log file, relative to ~/.ouroboros/. The resolved absolute path is ~/.ouroboros/logs/ouroboros.log.
include_reasoning bool true Whether to log LLM reasoning traces. Disable to reduce log volume when reasoning output is not needed.

credentials.yaml

API keys are stored separately from the main config. This file is created with chmod 600 permissions by ouroboros config init.

# ~/.ouroboros/credentials.yaml
providers:
  openrouter:
    api_key: YOUR_OPENROUTER_API_KEY
    base_url: https://openrouter.ai/api/v1
  openai:
    api_key: YOUR_OPENAI_API_KEY
  anthropic:
    api_key: YOUR_ANTHROPIC_API_KEY
  google:
    api_key: YOUR_GOOGLE_API_KEY

Alternative — environment variables (recommended for CI/CD):

export ANTHROPIC_API_KEY="sk-ant-..."
export OPENAI_API_KEY="sk-..."
export OPENROUTER_API_KEY="sk-or-..."

Environment variables take precedence over credentials.yaml.


Environment Variables

All environment variables have higher priority than the corresponding config.yaml value.

Runtime / Backend

Variable Overrides Description
OUROBOROS_AGENT_RUNTIME orchestrator.runtime_backend Active runtime backend (claude_mcp for Claude CLI, claude for the isolated SDK runtime, or another supported runtime).
OUROBOROS_AGENT_PERMISSION_MODE orchestrator.permission_mode Stored permission preference; runner-driven seed execution forces the native bypassPermissions equivalent for fresh and resumed dispatches on approval-aware backends. Pi and GJC have no separate approval flag.
OUROBOROS_OPENCODE_PERMISSION_MODE orchestrator.opencode_permission_mode Stored OpenCode preference. Seed execution still forces bypassPermissions, translated to --dangerously-skip-permissions.
OUROBOROS_EXEC_SANDBOX execution.exec_sandbox Execution sandbox for controller-run commands; on by default. off (or 0, false, no) is unsafe: verifier replay then runs unconfined, able to write outside its workspace copy and to use the network. Honored only from trusted sources (process environment, ~/.ouroboros/.env); a project .env cannot set it.
OUROBOROS_MODEL_TIER_ROUTING (routing kill switch) Model-tier routing is enabled by default. Set to 0, off, or false (case- and whitespace-insensitive) to disable routing and emit no routing events.
OUROBOROS_SHADOW_REPLAY (experiment arm) Default OFF. Only 1, true, or on arms the opt-in shadow-baseline harness. Current live decompositions lack deterministic MECE attestation and are skipped before baseline model dispatch; bundled runtimes also lack the complete filesystem/external-effect isolation attestation, so production emits no shadow baseline today.
OUROBOROS_MAX_PARALLEL_WORKERS orchestrator.max_parallel_workers Requested maximum concurrent Acceptance Criteria workers for parallel execution. Must be a positive integer.
OUROBOROS_MAX_CONCURRENCY (initial fan-out estimate) Overrides the backend-aware starting window. The legacy name is retained for compatibility; the value is no longer a permanent cap in runner-owned execution. Live 429/success feedback may shrink or grow the window, never above OUROBOROS_MAX_PARALLEL_WORKERS. Must be a positive integer; blank/invalid values are ignored.
OUROBOROS_<BACKEND>_RPM (rate budget) Per-backend requests-per-minute ceiling for the shared dispatch rate bucket. <BACKEND> is the runtime name (the same value you set for runtime_backend / OUROBOROS_AGENT_RUNTIME) upper-cased with non-alphanumerics collapsed to _ (e.g. OUROBOROS_HERMES_RPM, OUROBOROS_CODEX_RPM, OUROBOROS_OPENCODE_RPM). Internal *_cli adapter handles (hermes_cli, codex_cli, gemini_cli, copilot_cli) canonicalize to these names, so the user-facing key always applies. Dormant unless set. Must be a positive integer; blank/invalid values are ignored.
OUROBOROS_<BACKEND>_TPM (rate budget) Per-backend tokens-per-minute ceiling for the shared dispatch rate bucket (same naming as _RPM). Dormant unless set. Must be a positive integer; blank/invalid values are ignored.
OUROBOROS_BACKEND_LIMITS (config path) Path to the backend-limits YAML file (default ~/.ouroboros/backend_limits.yaml). See Backend concurrency & rate limits.
OUROBOROS_CLI_PATH orchestrator.cli_path Path to the Claude CLI binary.
OUROBOROS_CODEX_CLI_PATH orchestrator.codex_cli_path Path to the Codex CLI binary.
OUROBOROS_OPENCODE_CLI_PATH orchestrator.opencode_cli_path Path to the OpenCode CLI binary.
OUROBOROS_PI_CLI_PATH orchestrator.pi_cli_path Path to the Pi CLI binary.
OUROBOROS_OMP_CLI_PATH orchestrator.omp_cli_path Path to the OMP (Oh My Pi) CLI binary.
OUROBOROS_OUROCODE_CLI_PATH orchestrator.ourocode_cli_path Path to the ourocode CLI binary used by the LLM-only ourocode backend.
OUROBOROS_DSH_CLI_PATH orchestrator.dsh_cli_path Path to the dsh-acp-demo binary used by the LLM-only dsh backend.
OUROBOROS_DSH_CONFIG_PATH orchestrator.dsh_config_path Absolute path to the trusted Cordis composition the dsh backend loads. Required by that backend.
OUROBOROS_CHECK_PACKAGE boundary.check_package on or off for the ooo run check package boundary (on by default); overrides config, and is overridden by --check-package/--no-check-package. Only the exact value on turns it on: any other set value (off, an alias such as 1, true, yes or ON, empty or whitespace) means off. Not accepted from a project .env.
OUROBOROS_SKIP_VERSION_CHECK (none) Controls the Claude Agent SDK per-call version compatibility check. Defaults to "1" (skip the check, saving ~0.3-0.8 s per LLM call). Set to "0" to re-enable the check for debugging version-mismatch issues. Maps to CLAUDE_AGENT_SDK_SKIP_VERSION_CHECK internally.

LLM Flow

Variable Overrides Description
OUROBOROS_LLM_BACKEND llm.backend Default LLM backend for non-agent flows.
OUROBOROS_LLM_PERMISSION_MODE llm.permission_mode Permission mode for LLM flows.
OUROBOROS_QA_MODEL llm.qa_model Model for post-execution QA.
OUROBOROS_DEPENDENCY_ANALYSIS_MODEL llm.dependency_analysis_model Model for AC dependency analysis.
OUROBOROS_ONTOLOGY_ANALYSIS_MODEL llm.ontology_analysis_model Model for ontological analysis.
OUROBOROS_CONTEXT_COMPRESSION_MODEL llm.context_compression_model Model for context compression.

Phase Models

Every OUROBOROS_*_MODEL variable applies only with OUROBOROS_PIN_MODELS=1 (or models.pin: true), except on litellm and copilot; see models. A project .env cannot set any of them.

Variable Overrides Description
OUROBOROS_MODEL models.default auto, a tier name, or a model id for every role.
OUROBOROS_PIN_MODELS models.pin 1 runs persisted model ids; 0 resolves automatically.
OUROBOROS_CLARIFICATION_MODEL clarification.default_model Model for interview and seed generation.
OUROBOROS_WONDER_MODEL resilience.wonder_model Model for the Wonder phase.
OUROBOROS_REFLECT_MODEL resilience.reflect_model Model for the Reflect phase.
OUROBOROS_SEMANTIC_MODEL evaluation.semantic_model Model for Stage 2 semantic evaluation.
OUROBOROS_ASSERTION_EXTRACTION_MODEL evaluation.assertion_extraction_model Model for assertion extraction.
OUROBOROS_CONSENSUS_MODELS consensus.models Comma-separated model roster for Stage 3 voting.
OUROBOROS_CONSENSUS_ADVOCATE_MODEL consensus.advocate_model Advocate model for deliberative consensus.
OUROBOROS_CONSENSUS_DEVIL_MODEL consensus.devil_model Devil's advocate model for deliberative consensus.
OUROBOROS_CONSENSUS_JUDGE_MODEL consensus.judge_model Judge model for deliberative consensus.

MCP Evolution

Variable Default Description
OUROBOROS_EXECUTION_MODEL null (runtime default) Model used for agent execution inside the MCP evolve loop. Applies to runtimes that expose a per-call model override; unset it to preserve automatic runtime selection.
OUROBOROS_VALIDATION_MODEL null (runtime default) Model used for import/validation fix passes during MCP evolution. Applies to runtimes that expose a per-call model override; unset it to preserve automatic runtime selection.
OUROBOROS_EVOLVE_STAGE1 "true" Stage 1 mechanical checks (lint/build/test from .ouroboros/mechanical.toml, authored by the detector when missing) for an evolve generation whose output has no ### Task N markers. On by default because these executed checks are the only thing that can approve such a generation; semantic review there is advisory feedback. Set to "false" (or "0") to skip them: every such generation is then unverified, never approved. Accepted values are true/1 and false/0; any other value is logged and ignored. Honored only from trusted sources (process environment, ~/.ouroboros/.env); a project .env cannot set it.
OUROBOROS_MCP_TOOL_TIMEOUT_SECONDS runtime_controls.mcp_tool_timeout_seconds Adapter-level MCP timeout for progress-aware tools. 0 disables the wall-clock cap.
OUROBOROS_GENERATION_IDLE_TIMEOUT_SECONDS runtime_controls.generation_idle_timeout_seconds Idle timeout when no generation/execution activity is observed.
OUROBOROS_GENERATION_NO_PROGRESS_TIMEOUT_SECONDS runtime_controls.generation_no_progress_timeout_seconds Timeout when activity continues without material progress.
OUROBOROS_GENERATION_SAFETY_TIMEOUT_SECONDS runtime_controls.generation_safety_timeout_seconds Optional final hard cap for one generation.
OUROBOROS_WATCHDOG_POLL_SECONDS runtime_controls.watchdog_poll_seconds EventStore polling interval for watchdog decisions.
OUROBOROS_GENERATION_TIMEOUT legacy alias Backwards-compatible alias for generation_no_progress_timeout_seconds. It no longer creates a separate hard 2-hour MCP adapter timeout. Prefer runtime_controls in config.yaml for persistent tuning.

Observability & Agents

Variable Default Description
OUROBOROS_LOG_MODE "dev" Logging output format. "dev" = human-readable console output; "prod" = structured JSON (suitable for log aggregation).
OUROBOROS_AGENTS_DIR null Path to a directory of custom agent .md prompt files. When set, overrides the bundled agents from the installed package. Useful for developing custom agent personas without reinstalling.
OUROBOROS_WEB_SEARCH_TOOL "" MCP tool name to use for web search during the Big Bang interview (e.g., mcp__tavily__search). An empty string disables web-augmented interview. Only applicable when running with an MCP-capable host.

API Keys

Variable Description
ANTHROPIC_API_KEY Anthropic API key (Claude models).
OPENAI_API_KEY OpenAI API key (Codex CLI, GPT models).
GOOGLE_API_KEY Google API key (Gemini models used in frugal and standard tiers).
OPENROUTER_API_KEY OpenRouter API key (multi-provider model access for consensus).

Backend concurrency & rate limits

Ouroboros plans delivery fan-out — the parallel execution of acceptance criteria — and is responsible for adapting it to the connected LLM backend's concurrency and rate limits rather than relying on the agent runtime to throttle itself. Two independent controls govern this, and every pre-flight value is configurable without source-level changes:

  • Adaptive fan-out: max_concurrency is the pre-flight starting estimate (the legacy field name is retained for compatibility), while orchestrator.max_parallel_workers is the hard controller ceiling. Unknown CLI runtimes start at 1. A short/generic 429 or explicit concurrency rejection halves the window; Retry-After pauses new provider entrances and is saturated at 24 hours before clock arithmetic; three successful completions add one worker. The complete AIMD policy is part of the durable execution-semantics fingerprint. Explicit usage/quota exhaustion stays on the durable PAUSED → resume path and is never converted into a concurrency retry.
  • Rate budget (requests_per_minute / tokens_per_minute): a shared sliding-window bucket that paces dispatch across all concurrent workers. For non-Claude runtimes it is dormant until you declare a budget.

Resolution precedence

Each dimension is resolved independently, highest precedence first:

  1. Environment variables — OUROBOROS_MAX_CONCURRENCY (initial estimate, any backend) and per-backend OUROBOROS_<BACKEND>_RPM / OUROBOROS_<BACKEND>_TPM.
  2. Config file — ~/.ouroboros/backend_limits.yaml (path overridable via OUROBOROS_BACKEND_LIMITS).
  3. Built-in registry — Claude's ceilings; otherwise the serialize-by-default initial value 1.

The config file is loaded lazily, cached by mtime (edits apply without a restart), and is fully fault-tolerant: a missing, malformed, or non-regular file is ignored and resolution falls back to the registry. Backend keys are canonicalized, so aliases (anthropic, claude_code) map to claude. Only positive integers are honored; 0/negative/blank values are ignored so an invalid value never silently replaces the serialize-by-default starting estimate.

~/.ouroboros/backend_limits.yaml

# Fallback applied to any backend without its own entry below.
default:
  max_concurrency: 1

backends:
  # Override the native Claude ceilings without touching source.
  claude:
    requests_per_minute: 40
    tokens_per_minute: 32000

  # Declare a CLI runtime's starting fan-out estimate and rate budget.
  # Live feedback still controls the window within max_parallel_workers.
  # Use the runtime name you select with `runtime_backend` (hermes, codex,
  # gemini, copilot, opencode, goose, pi, kiro, gjc) — the `*_cli` adapter handles
  # canonicalize to these, so either form resolves to the same entry.
  hermes:
    max_concurrency: 4
    requests_per_minute: 20
    tokens_per_minute: 60000

A flat top-level mapping (backend → fields, without the backends: wrapper) is also accepted.


Minimal Config Examples

Claude Code Runtime (recommended default)

# ~/.ouroboros/config.yaml
orchestrator:
  runtime_backend: claude

logging:
  level: info

Codex CLI Runtime

orchestrator:
  runtime_backend: codex
  codex_cli_path: /usr/local/bin/codex   # omit if codex is already on PATH

llm:
  backend: codex

logging:
  level: info

Codex CLI Runtime With Explicit Role Overrides

# ~/.ouroboros/config.yaml
orchestrator:
  runtime_backend: codex
  codex_cli_path: /usr/local/bin/codex

models:
  pin: true             # run the ids below instead of Codex's own default

llm:
  backend: codex
  qa_model: gpt-5.4

clarification:
  default_model: gpt-5.4

evaluation:
  semantic_model: gpt-5.4

consensus:
  advocate_model: gpt-5.4
  devil_model: gpt-5.4
  judge_model: gpt-5.4

This is the recommended Ouroboros-side pattern for Codex users who pin models. Without models.pin: true these ids are ignored and Codex runs its own default. Keep ~/.codex/config.toml limited to the MCP/env block created by setup.

OpenCode Runtime

# ~/.ouroboros/config.yaml
orchestrator:
  runtime_backend: opencode
  opencode_cli_path: /usr/local/bin/opencode   # omit if opencode is already on PATH

llm:
  backend: opencode

logging:
  level: info

OpenCode supports multiple model providers (Anthropic, OpenAI, Google, and others). Model selection is configured in OpenCode itself (~/.config/opencode/opencode.jsonc or opencode.json), not in config.yaml. The orchestrator.opencode_permission_mode defaults to bypassPermissions since OpenCode runs non-interactively via opencode run --format json. The llm.opencode_permission_mode defaults to acceptEdits, but the factory forces bypassPermissions for interview/seed use cases to avoid CLI sandbox blocking.

GitHub Copilot CLI Runtime

# ~/.ouroboros/config.yaml
orchestrator:
  runtime_backend: copilot
  copilot_cli_path: null                   # omit if `copilot` is already on PATH

llm:
  backend: copilot

clarification:
  default_model: claude-opus-4.6           # example live-discovered Copilot ID

The Copilot CLI runtime is unique in that ouroboros setup --runtime copilot live-discovers the available models from the GitHub Copilot models API at setup time. There is no llm.default_model contract: setup removes that key and writes the selected dotted Copilot ID into supported per-role fields that are absent or still carry shipped defaults, while preserving explicit user overrides. Re-run setup after GitHub publishes new models. Authentication uses gh auth login; no separate API key is required.

Model-ID normalization is catalog-gated. map_to_copilot_model() first accepts an exact Copilot ID, then removes the known openrouter/anthropic/ prefix or applies an explicit alias, and finally converts only the trailing Anthropic version separator (claude-opus-4-8 → claude-opus-4.8). Every derived value must exist in the discovered or bundled Copilot catalog before it is returned. The current direct and OpenRouter Opus defaults therefore resolve to claude-opus-5; unknown IDs remain unchanged so Copilot emits its normal unavailable-model error instead of silently choosing another model. See the Copilot CLI runtime guide for full details.

Pi CLI Runtime

# ~/.ouroboros/config.yaml
orchestrator:
  runtime_backend: pi
  pi_cli_path: null                       # omit if `pi` is already on PATH

llm:
  backend: pi

Pi is available as an agent runtime backend and, when the Pi LLM adapter is installed, an LLM-only backend for interview, ambiguity scoring, seed-extraction, and structured JSON flows. ouroboros setup --runtime pi records the Pi executable and installs a managed Pi extension at ~/.pi/agent/extensions/ouroboros-ooo-bridge.ts, so interactive Pi/roach-pi sessions can route exact-prefix ooo ... input back into Ouroboros after Pi restart or /reload. The Pi LLM adapter supports structured response_format requests through prompt-level JSON/schema instructions plus adapter-side extraction and validation; Pi does not expose a Codex-style native --output-schema hard-enforcement flag. The runtime uses documented JSON mode (pi --mode json <prompt>) and preserves Pi native session IDs for targeted resume.

OMP CLI Runtime

# ~/.ouroboros/config.yaml
orchestrator:
  runtime_backend: omp
  omp_cli_path: null                      # omit if `omp` is already on PATH

llm:
  backend: omp

OMP (Oh My Pi) is available as an agent runtime backend and an LLM-only backend for interview, ambiguity scoring, seed-extraction, and structured JSON flows. ouroboros setup --runtime omp records the OMP executable and installs a managed OMP extension at ~/.omp/agent/extensions/ouroboros-ooo-bridge.ts, so interactive OMP sessions can route exact-prefix ooo ... input back into Ouroboros after an OMP restart (dispatch timeout: OUROBOROS_OMP_BRIDGE_TIMEOUT_MS). The OMP LLM adapter supports structured response_format requests through prompt-level JSON/schema instructions plus adapter-side extraction and validation; OMP does not expose a hard --output-schema flag. The runtime uses documented JSON mode (omp --mode json <prompt>) and resumes prior sessions natively with --resume <id>. OMP selects its own model (roles smol/slow/plan) unless an explicit --model override is passed; the generic default sentinel is never forwarded. See the OMP CLI runtime guide.

Full Config Skeleton

orchestrator:
  runtime_backend: claude
  permission_mode: acceptEdits
  opencode_permission_mode: bypassPermissions
  max_parallel_workers: 3
  cli_path: null
  codex_cli_path: null
  opencode_cli_path: null
  pi_cli_path: null
  omp_cli_path: null
  ourocode_cli_path: null
  default_max_turns: 10

llm:
  backend: claude_code
  permission_mode: default
  opencode_permission_mode: acceptEdits
  qa_model: claude-sonnet-4-6
  dependency_analysis_model: claude-sonnet-4-6
  ontology_analysis_model: claude-sonnet-4-6
  context_compression_model: gpt-4

economics:
  default_tier: frugal
  escalation_threshold: 2
  downgrade_success_streak: 5
  tiers:
    frugal:
      cost_factor: 1
      intelligence_range: [9, 11]
      models:
        - provider: openai
          model: gpt-5.1-codex-mini
        - provider: google
          model: gemini-2.0-flash
        - provider: anthropic
          model: claude-haiku-4-5
      use_cases: [routine_coding, log_analysis, stage1_fix]
    standard:
      cost_factor: 10
      intelligence_range: [14, 16]
      models:
        - provider: openai
          model: gpt-5-codex
        - provider: anthropic
          model: claude-sonnet-4-6
        - provider: google
          model: gemini-2.5-pro
      use_cases: [logic_design, stage2_evaluation, refactoring]
    frontier:
      cost_factor: 30
      intelligence_range: [18, 20]
      models:
        - provider: openai
          model: gpt-5.2
        - provider: anthropic
          model: claude-opus-5
      use_cases: [consensus, lateral_thinking, big_bang]

clarification:
  ambiguity_threshold: 0.2
  max_interview_rounds: 10
  model_tier: standard
  default_model: claude-opus-5

execution:
  max_iterations_per_ac: 10
  retrospective_interval: 3

resilience:
  stagnation_enabled: true
  lateral_thinking_enabled: true
  lateral_model_tier: frontier
  lateral_temperature: 0.8
  wonder_model: claude-opus-5
  reflect_model: claude-opus-5

evaluation:
  stage1_enabled: true         # Currently inert in config.yaml
  stage2_enabled: true         # Currently inert in config.yaml
  stage3_enabled: true         # Currently inert in config.yaml
  satisfaction_threshold: 0.8  # Currently inert; score gate is hardcoded to 0.8
  uncertainty_threshold: 0.3   # Currently inert in config.yaml
  semantic_model: claude-opus-5
  assertion_extraction_model: claude-sonnet-4-6

consensus:
  min_models: 3               # Inert; runtime needs 2 successful post-filter votes
  threshold: 0.67             # Inert; runtime ratio threshold defaults to 0.66
  diversity_required: true    # Currently inert
  models:
    - openrouter/openai/gpt-4o
    - openrouter/anthropic/claude-opus-5
    - openrouter/google/gemini-2.5-pro
  advocate_model: openrouter/anthropic/claude-opus-5
  devil_model: openrouter/openai/gpt-4o
  judge_model: openrouter/google/gemini-2.5-pro

persistence:
  enabled: true
  database_path: data/ouroboros.db

drift:
  warning_threshold: 0.3
  critical_threshold: 0.5

runtime_controls:
  mcp_tool_timeout_seconds: 0
  generation_idle_timeout_seconds: 7200
  generation_no_progress_timeout_seconds: 14400
  generation_safety_timeout_seconds: 0
  watchdog_poll_seconds: 15.0

logging:
  level: info
  log_path: logs/ouroboros.log
  include_reasoning: true