Configuration Reference (.github/agents.yml)
EPURE centralizes all orchestration, LLM routing, polyglot verification, web exploration, and non-weakenable security invariants in a single versioned declarative file: .github/agents.yml.
Complete Canonical Flat Schema (serializeAgentsConfigYaml / parseAgentsYaml)
Below is the canonical flat .github/agents.yml file generated by setup-repo (src/bootstrap/setup-repo.ts) with all supported keys and their defaults (Q78, Q91–Q96, ADR-0028–ADR-0033):
# EPURE Consumer Repository Configuration (.github/agents.yml)
# Generated by setup-repo — security boundaries are enforced by shared workflows (ADR-0010, ADR-0023).
harness: pi
models:
planning: anthropic/claude-sonnet-4-20250514
implementation: anthropic/claude-sonnet-4-20250514
review: openai/gpt-4.1
providers:
azure-prod:
provider: azure
api_key: os.environ/AZURE_OPENAI_API_KEY
base_url: os.environ/AZURE_OPENAI_ENDPOINT
api_version: 2024-10-21
foundry-deepseek:
provider: azure_ai
api_key: os.environ/AZURE_AI_API_KEY
base_url: https://my-foundry.services.ai.azure.com
merge_policy: strict
task_auto_merge_enabled: true
max_code_fix_attempts: 3
max_epic_phases: 2
max_transient_api_retries: 4
transient_retry_backoff_seconds: [5, 15, 45, 120]
llm_request_timeout_seconds: 900
job_timeout_minutes: 60
review_timeout_minutes: 30
max_concurrent_tasks_per_feature: 3
trace_retention_days: 30
preview_retention_days: 14
github_pages_previews: false
reviewers_dir: .agents/reviewers
llm_environment: epure-agent
verification:
wait_for_external_ci: true
external_ci_timeout_minutes: 15
grace_window_seconds: 45
targets:
- name: root
working_directory: .
kind: node
path_patterns: ["**"]
install_command: "npm ci --ignore-scripts"
typecheck_command: "npm run typecheck"
lint_command: "npm run lint"
test_command: "npm test"
build_command: "npm run build"
web_tools:
enabled: true
phases: ["plan", "spec", "implementation"]
max_requests_per_turn: 4
max_requests_per_run: 12
max_bytes_per_response: 16384
max_fetch_bytes: 16384
timeout_ms: 10000
security_invariants:
require_write_permission: true
sandbox_isolation: true
allow_agent_release: false
auto_merge_dev: false
auto_merge_main: falseConfigurable & LLM-Agnostic GitHub Environment (<llm-env>)
In .github/workflows/epure-issue-command.yml, the orchestrate-command job runs inside a dedicated GitHub Environment (environment: <llm-env>) only after the secretless authorize-command job verifies effective write or admin permission:
- Free & Provider-Agnostic Naming: The
<llm-env>environment name defaults toepure-agent(llm_environmentin.github/agents.yml) and is completely configurable viasetup-repo --llm-env <env-name>andsetup-github-apps-wizard.sh --llm-env <env-name>(use the same value for both). It is agnostic of the underlying LLM provider (other examples:llm,openai,anthropic, orgemini) and must only contain[A-Za-z0-9._-]. - Zizmor-Compliant Secret Isolation:
EPURE_AGENT_APP_ID,EPURE_AGENT_APP_PRIVATE_KEY, andLLM_API_KEY(or custom provider secret referenced viaos.environ/VAR_NAME) are scoped strictly to--env <llm-env>, whileEPURE_RELEASE_APP_IDandEPURE_RELEASE_APP_PRIVATE_KEYare scoped to the human-approved--env releaseenvironment. Public pull request workflows andauthorize-commandnever have access to any secret.
Section-by-Section Reference
Root Execution, Retry & Governance Keys
| Key | Type | Default | Description |
|---|---|---|---|
harness | "pi" | "codex" | "pi" | Agent execution adapter harness used for sandboxed runs. |
merge_policy | "strict" | "relaxed" | "strict" | Stale-base policy for task/* -> feature/* integration (strict requires up-to-date base; relaxed allows clean 3-way merge when approved diff is unchanged). |
task_auto_merge_enabled | boolean | false (true when CI & branch protections exist) | Enables autonomous squash-merge of validated task/* -> feature/* PRs; forced to false (AUTO_MERGE_DISABLED_MISSING_PROTECTIONS) if required CI or branch protections are missing. |
max_code_fix_attempts | integer | 3 | Maximum number of automated remediation rounds (Stage 1 sandbox, external CI, or multi-agent review) per ticket before escalating to needs-human (Q78). |
max_epic_phases | integer (1..3) | 2 | Maximum number of epic execution phases when runEpicConvergenceAudit detects unwired entrypoints ([Phase 2 - Ticket N], ADR-0032). |
max_transient_api_retries | integer | 4 | Per-HTTP-request retry ceiling for transient LLM errors (5xx, 408, network timeout). HTTP 429 rate limits honor Retry-After (up to 10 waits) without consuming this quota. |
transient_retry_backoff_seconds | [int, int, int, int] | [5, 15, 45, 120] | Exponential backoff schedule in seconds between transient HTTP retries. |
llm_request_timeout_seconds | integer | 900 | Unitary HTTP request timeout in seconds (15 minutes) for each LLM API call. |
job_timeout_minutes | integer | 60 | Wall-clock timeout in minutes for orchestrate-command in .github/workflows/epure-issue-command.yml (Q78). |
review_timeout_minutes | integer | 30 | Timeout in minutes for multi-agent PR review jobs (with up to max_parallel_reviewers = 4 specialist/transverse reviewers selected per PR diff alongside spec-parity-reviewer). |
max_concurrent_tasks_per_feature | integer | 3 | Maximum number of unblocked task/* tickets executed concurrently per feature/* branch. |
trace_retention_days | integer | 30 | Retention period in days for minimized audit trajectories (iFixAi). |
preview_retention_days | integer | 14 | Number of days before hosted /prototype previews are automatically purged (Q71). |
github_pages_previews | boolean | false | Opt-in flag (legacy alias enable_pages_preview: false) to publish static /prototype previews to an isolated GitHub Pages origin; requires github_pages_human_consent: true and preview_separate_origin: true (Q70, Q73). |
reviewers_dir | string (relative path) | ".agents/reviewers" | Reviewer roster directory where setup-repo writes and the orchestrator reads reviewer prompt files <name>.md (always from the trusted dev branch, falling back to EPURE's embedded templates). Configurable via setup-repo --reviewers-dir <path> (for example .cursor/agents for a team that wants Cursor to load the reviewers as subagents). Absolute paths, paths with . or .. segments, and paths under src/ are rejected fail-closed with SECURITY_INVARIANT_VIOLATION. |
llm_environment | string ([A-Za-z0-9._-]) | "epure-agent" | GitHub Environment name used by the orchestrate-command job of the generated .github/workflows/epure-issue-command.yml (environment: <llm-env>). Configurable via setup-repo --llm-env <name>; must match the environment created by setup-github-apps-wizard.sh --llm-env <name>. |
models & providers (Multi-Provider Routing — ADR-0029)
EPURE includes a zero-dependency multi-provider LLMClient supporting Google Gemini (google/ or gemini/), OpenAI (openai/), Azure OpenAI (azure/), Azure AI Foundry (azure_ai/), and Anthropic (anthropic/).
models.planning, models.implementation, and models.review are required: EPURE has no fallback model, consistent with ADR-0012's no-automatic-fallback principle. A non-empty model input on the orchestrate-issue action overrides all three phases; otherwise each phase uses its models.<phase> value verbatim. When a command that calls an LLM finds a key missing, it suspends with a needs-human escalation (model_config_missing) naming the key to set, before any LLM call; commands that make no LLM call (status, cancel, replies outside grilling/spec, continue with nothing waiting) are unaffected. EPURE reads .github/agents.yml from dev (from main only when dev has no such file); a file that fails to parse is never ignored or replaced by the main copy: commands that call an LLM escalate to needs-human with the parser message (Invalid .github/agents.yml on 'dev': ...).
| Key | Type | Default | Description |
|---|---|---|---|
models.planning | string | (Required; setup-repo writes "anthropic/claude-sonnet-4-20250514") | Model used for /epure plan Grill interviews, /epure to-spec / /epure to-tickets, and plan-to-feature ADR reconciliation (<provider>/<model> or bare model). Override with setup-repo --planning-model (or --model for all phases). |
models.implementation | string | (Required; setup-repo writes "anthropic/claude-sonnet-4-20250514") | Model used for incremental workspace tool loops (write_repo_file, patch_repo_file), 3-way conflict resolution, /epure sync ADR reconciliation, and Stage 1/2/3 auto-remediation. Override with setup-repo --implementation-model (or --model). |
models.review | string | (Required; setup-repo writes "openai/gpt-4.1") | Model used for the multi-agent PR review roster, runGoldfishSpecAudit, runEpicConvergenceAudit, user-advocate, and squash commit synthesis (requested at temperature 0.0; no temperature is sent to Claude Opus 4.7+ / Claude 5.x+). Override with setup-repo --review-model (or --model). |
providers.<alias>.provider | "google" | "openai" | "azure" | "azure_ai" | "anthropic" | (Optional) | Underlying protocol family for a custom provider alias referenced in models.* (<alias>/<model>). |
providers.<alias>.api_key | string | os.environ/<VAR> | Secret-free environment variable reference (os.environ/VAR_NAME) resolved at runtime from process.env. Never commit literal API keys in .github/agents.yml. |
providers.<alias>.base_url | string | Provider default | Custom base URL or os.environ/VAR_NAME. |
providers.<alias>.api_version | string | "2024-10-21" | Azure OpenAI deployment API version (?api-version=...). |
providers.<alias>.default_headers | Record<string, string> | {} | Optional custom HTTP headers attached to every request for this provider alias. |
providers.<alias>.effort | "low" | "medium" | "high" | "xhigh" | "max" | (Optional, model default) | Anthropic only: sent as output_config.effort on every request for every model routed through this alias (e.g. effort: high for anthropic/claude-haiku-5-5). Omitted when unset. Any other value makes .github/agents.yml invalid: commands that call an LLM escalate to needs-human with the parse error. temperature is automatically omitted for Claude Opus 4.7+ and Claude 5.x+ models, which reject sampling parameters. |
An Anthropic response with stop_reason: refusal raises an LLMError with reasonCode: "REFUSAL". It is not retried: the orchestrator suspends with a needs-human escalation (llm_refusal) without consuming the ticket's max_code_fix_attempts budget.
verification & verification.targets (Polyglot Stage 1 & Stage 2 CI — ADR-0028, ADR-0030)
Always loaded at runtime from the trusted dev branch so an untrusted task/* branch can never weaken its own test commands:
| Key | Type | Default | Description |
|---|---|---|---|
verification.wait_for_external_ci | boolean | true | Enables Stage 2 external CI waiting (waiting_ticket_ci / waiting_feature_ci) on task/* and feature/* PRs. |
verification.external_ci_timeout_minutes | integer | 15 | Maximum external CI wait ceiling in minutes before timeout classification. |
verification.grace_window_seconds | integer | 45 | Grace window in seconds after PR push to allow GitHub Actions check runs to register before evaluating completion. |
verification.deferred_external_workflows | string[] | [] | Optional workflow names deferred from per-ticket task/* blocking and enforced only on feature/* -> dev. |
verification.targets | VerificationTargetSpec[] | Auto-discovered by setup-repo | Per-subproject Stage 1 verification targets (name, working_directory, kind: node | python | rust | go | make | custom, path_patterns, install_command, codegen_command, typecheck_command, lint_command, test_command, build_command) executed inside the 2-phase Ubuntu 26.04 kernel sandbox (landrun + bubblewrap). |
web_tools (Confined Public Web Exploration — ADR-0031)
| Key | Type | Default | Description |
|---|---|---|---|
web_tools.enabled | boolean | true | Enables zero-API-key read-only public Web exploration tools (web_search, web_fetch) constrained to https:// port 443 with strict Anti-SSRF filtering and <untrusted_web_content> wrapping. |
web_tools.phases | string[] | ["plan", "spec", "implementation"] | Lifecycle phases allowed to invoke web_search and web_fetch (excluded from review by default). |
web_tools.max_requests_per_turn | integer | 4 | Maximum Web tool requests permitted within a single LLM turn. |
web_tools.max_requests_per_run | integer | 12 | Maximum Web tool requests permitted across an entire orchestrator run. |
web_tools.max_bytes_per_response | integer | 16384 | Maximum UTF-8 byte budget returned per tool response. |
web_tools.max_fetch_bytes | integer | 16384 | Maximum UTF-8 byte budget returned per web_fetch call after HTML stripping. |
web_tools.timeout_ms | integer | 10000 | Per-request HTTP timeout in milliseconds for Web exploration calls. |
security_invariants & Sensitive Paths
Any attempt in .github/agents.yml to weaken security_invariants (require_write_permission: false, sandbox_isolation: false, allow_agent_release: true, auto_merge_dev: true, auto_merge_main: true) is rejected fail-closed with SECURITY_INVARIANT_VIOLATION. Furthermore, any modification to .github/workflows/**, .github/agents.yml, the reviewer roster directory (the configured reviewers_dir/** and the default .agents/reviewers/**), or CODEOWNERS on a task/* branch triggers a Sensitive Path Escalation (needs-human) unless a maintainer explicitly adds the approved-sensitive-change label (Q81, Q93).
Supply-Chain & Renovate Security Policy (renovate.json & .github/zizmor.yml)
Every EPURE repository ships a hardened renovate.json and .github/zizmor.yml generated by /setup-repo (Q46–Q48):
- Quarantine Cooldown (No Day-1 Merges): Enforces
minimumReleaseAge: "3 days"globally ("7 days"for minor updates and GitHub Actions/Docker images,"14 days"for major npm updates) coupled withinternalChecksFilter: "strict"andprCreation: "not-pending"so newly published or compromised packages are quarantined before any PR is opened. - Maximum Safe Automation: Groups minor, patch, pin, and digest updates into single weekly PRs (
npm-minor-and-patch,github-actions,docker), pins exact versions (rangeStrategy: "pin",helpers:pinGitHubActionDigests,docker:pinDigests), runs monthlylockFileMaintenance, and automatically rebases behinddev(rebaseWhen: "behind-base-branch"). - Post-Install Script Isolation: Injects
npmrc: "ignore-scripts=true"during Renovate lockfile resolution to prevent untrusted lifecycle scripts from executing. - No Unreviewed Auto-Merge into
dev:automerge: falseandplatformAutomerge: falseare strictly enforced; Renovate PRs targetdev, run the full CI + multi-agent review pipeline, and optionally trigger the assistedauto_migrateagent if a breaking change is detected on a verified Renovate App PR.

