Configuration field reference
This page is the exhaustive field reference for config.yaml. The code source
of truth is the Zod schema in packages/core/src/config.ts. This page is
audited against that schema; when they disagree, the schema wins. For the
narrative behind each namespace, follow the linked configuration page.
Each namespace below links to its narrative page and lists every field the schema validates, with type, default, and a one-line description. Optional fields without a schema default are marked — in the Default column.
Enum reference
Section titled “Enum reference”These enum values appear across multiple namespaces and are collected here for quick lookup.
| Concept | Enum values |
|---|---|
Trigger kind |
gitea, forgejo, github, gitlab, p4, svn, scheduled, manual |
| Agent execution mode | kilo, opencode, zoo, copilot-cli, claude-code, pi, oh-my-pi, native-llm |
Sandbox kind |
native, docker, podman, docker_socket, k8s_pod, firecracker |
Sandbox engine |
auto, docker, podman |
Queue kind |
memory, sqlite, redis, rabbitmq (reserved) |
Storage database.kind |
sqlite, postgres |
Storage cache.kind |
memory, redis, none |
Storage object.kind |
filesystem, s3 |
Model catalog cache.backend |
sqlite, redis, memory |
Context repository kind |
git, p4, svn |
LLM provider kind |
openai_compatible, azure_openai, anthropic, vertex_ai, bedrock, google_ai_studio, ollama, copilot |
Narrative: LLM providers and models.
| Field | Type | Default | Description |
|---|---|---|---|
llm.providers[] |
array | [] |
LLM provider connections; each has id, kind, and optional base_url, api_key_env, api_version, catalog_provider, catalog_id |
llm.providers[].id |
string | — | Provider identifier referenced from model_chain |
llm.providers[].kind |
enum | — | Provider kind (see LLM provider enum above) |
llm.providers[].base_url |
URL | — | Provider API base URL |
llm.providers[].api_key_env |
string | — | Env var name holding the API key |
llm.providers[].api_key |
string | — | Literal API key; mutually exclusive with api_key_env (literal wins); sealed when published to the database |
llm.providers[].api_version |
string | — | API version (Azure, etc.) |
llm.providers[].catalog_provider |
string | — | Override the models.dev provider id for catalog lookup |
llm.providers[].catalog_id |
string | — | Override the models.dev <provider>/<model> id for catalog lookup |
llm.providers[].reasoning_effort |
enum | — | Reasoning effort tier: minimal, low, medium, high, max (passthrough) |
llm.providers[].thinking_level |
enum | — | Coarser thinking tier: off, minimal, low, medium, high, max (passthrough) |
llm.providers[].thinking_budget_tokens |
int | — | Explicit thinking budget in tokens (passthrough) |
llm.model_chain |
map | {} |
Group names mapped to ordered model lists |
llm.model_chain.<id>[] |
array | — | Non-empty list; first entry is primary, later entries are tried in order. Each has provider, model, role (light/heavy/any) |
llm.default_model_chain |
string | default |
Global main-group name; must exist when groups are configured |
llm.triage_model_chain |
string | inherit | Lifecycle-analysis group name; omitted inherits the current workspace main group |
llm.author_resolution_model_chain |
string | inherit | Directory identity-analysis group; omitted uses llm.default_model_chain |
llm.retry |
object | — | Per-call retry policy |
llm.retry.max_attempts |
int > 0 | — | Max attempts per LLM call |
llm.retry.respect_retry_after |
boolean | — | Honor Retry-After headers |
llm.retry.backoff |
object | — | kind (exponential/linear/constant), base_ms, max_ms, jitter |
llm.retry.give_up_after_seconds |
number > 0 | — | Hard wall-clock give-up |
llm.budget |
object | — | Spend caps |
llm.budget.per_run_usd |
number ≥ 0 | — | Per-run USD cap |
llm.budget.per_repo_daily_usd |
number ≥ 0 | — | Per-repo daily USD cap |
llm.per_provider_overrides |
map | — | Per-provider max_attempts / give_up_after_seconds keyed by provider id |
llm.model_catalog |
object | see below | models.dev metadata catalog |
llm.model_catalog
Section titled “llm.model_catalog”| Field | Type | Default | Description |
|---|---|---|---|
llm.model_catalog.enabled |
boolean | false |
Enable models.dev metadata lookup |
llm.model_catalog.source_url |
URL | https://models.dev/api.json |
Catalog source |
llm.model_catalog.refresh_interval_hours |
int > 0 | 24 |
Refresh interval |
llm.model_catalog.fetch_timeout_ms |
int > 0 | 10000 |
Fetch timeout |
llm.model_catalog.offline |
boolean | false |
Never touch the network; use cache + bundled snapshot only |
llm.model_catalog.apply_to_model_spec |
boolean | true |
Merge catalog metadata into the resolved ModelSpec |
llm.model_catalog.cache.backend |
enum | sqlite |
Refresh cache backend; redis requires storage.cache.kind: redis + redis.url_env |
llm.model_catalog.overrides |
map | {} |
Hand-edited per-<provider>/<model> overrides; explicit values always win over catalog data. Supports supported_reasoning_efforts, default_reasoning_effort, and more |
triggers
Section titled “triggers”Narrative: VCS providers.
| Field | Type | Default | Description |
|---|---|---|---|
triggers[].name |
string | — | Trigger profile name; referenced from workspaces.instances.<id>.source_repo.trigger |
triggers[].kind |
enum | — | Trigger kind (see enum table) |
triggers[].enabled |
boolean | — | Enabled when omitted; false stops new admission for this profile while retaining historical snapshots |
triggers[].watch_path |
string[] | — | Only analyze files under these depot/repo-relative subpaths |
triggers[].include_cr_file |
string[] | — | Glob patterns; a file must match at least one to be analyzed |
triggers[].exclude_cr_file |
string[] | — | Glob patterns; a file matching any is skipped |
triggers[].commit_url_template |
string | — | URL template for commit links (variables are URL-encoded) |
triggers[].revision_url_template |
string | — | URL template for revision links |
triggers[].change_url_template |
string | — | URL template for changelist links (P4 Swarm, etc.) |
triggers[].app |
object | — | GitHub App auth block; kind: github only, mutually exclusive with token/token_env |
triggers[].app.app_id |
string | int | — | GitHub App ID (at least one of this or client_id) |
triggers[].app.client_id |
string | — | GitHub App client ID (alternative to app_id) |
triggers[].app.private_key_env |
string | — | Env var holding the App private key PEM (exactly one of this, private_key or private_key_path) |
triggers[].app.private_key_path |
string | — | Path to the App private key PEM file (exactly one of this, private_key or private_key_env) |
triggers[].app.private_key |
string | — | Literal App private key PEM or base64 PEM (exactly one of this, private_key_env or private_key_path); sealed when published to the database |
triggers[].app.installation_id |
string | int | — | Fixed installation id; resolved per owner/repo when omitted |
triggers[].token |
string | — | Literal outbound VCS token (git kinds); mutually exclusive with token_env and app; sealed when published to the database |
triggers[].webhook_secret |
string | — | Literal inbound webhook secret (git kinds); mutually exclusive with webhook_secret_env; sealed when published to the database |
triggers[].user |
string | — | Literal P4 user (identifier, not sealed); mutually exclusive with user_env |
triggers[].ticket |
string | — | Literal P4 ticket; mutually exclusive with ticket_env; sealed when published to the database |
triggers[].password |
string | — | Literal P4/SVN password; mutually exclusive with password_env; sealed when published to the database |
triggers[].username |
string | — | Literal SVN username (identifier, not sealed); mutually exclusive with username_env |
Provider-specific fields (webhook_secret_env, token_env, port,
user_env, password_env, depot_path, workspace, repository_url) are
accepted via passthrough validation and documented under
VCS providers and
Authentication & secrets.
workspaces
Section titled “workspaces”Narrative: Configuration overview.
| Field | Type | Default | Description |
|---|---|---|---|
workspaces.root |
string | — | Layout root for workspace instances; relative paths resolve against the server base directory. Default: <baseDir>/workspaces |
workspaces.cache.max_total_gb |
number > 0 | 50 |
Max total workspace cache size in GB |
workspaces.cache.eviction |
enum | lru |
Eviction policy: lru, mru, ttl |
workspaces.cache.ttl_days |
int > 0 | 30 |
TTL in days for ttl eviction |
workspaces.defaults |
object | {} |
Defaults merged into every instance (sandbox, review, agent, outputs, prompt, context_repositories) |
workspaces.defaults.sandbox |
object | — | Default sandbox config (see agent.sandbox) |
workspaces.defaults.review |
object | — | Default review config (see review) |
workspaces.defaults.model_chain |
string | inherit | Main-group override referencing llm.model_chain |
workspaces.defaults.triage_model_chain |
string | inherit | Lifecycle-group override; if absent at all layers, uses this workspace’s main group |
workspaces.defaults.author_resolution_model_chain |
string | inherit | Identity group override over llm.author_resolution_model_chain |
workspaces.defaults.agent.default |
enum | — | Default execution mode for this workspace set; resolved per run through global → defaults → instance → route analysis (see note below) |
workspaces.defaults.agent.timeout_seconds |
int > 0 | — | Hard per-run timeout; on timeout the whole process tree is killed |
workspaces.defaults.agent.auto_approve |
boolean | — | Passed to the selected adapter; false removes automatic approval where supported |
workspaces.defaults.agent.context_compaction.auto |
boolean | — | Enable auto-compaction |
workspaces.defaults.agent.context_compaction.threshold_percent |
int 1–100 | — | Compaction trigger threshold |
workspaces.defaults.agent.context_compaction.prune |
boolean | — | Prune compacted history |
workspaces.defaults.agent.web_search.enabled |
boolean | — | Enable the agent’s built-in web search tool for reviews (omp web_search.enabled; kilo/opencode permission + activation env; claude-code/copilot-cli CLI switch) |
workspaces.defaults.agent.web_search.providers |
string[] | — | Ordered providers: full omp chain; kilo accepts exa; opencode selects the first exa/parallel |
workspaces.defaults.agent.web_search.exclude |
string[] | — | Provider ids removed from the search chain → providers.webSearchExclude |
workspaces.defaults.agent.web_search.timeout_seconds |
int 1–300 | — | Per-provider transport timeout → providers.webSearchTimeoutSeconds |
workspaces.defaults.agent.web_search.credentials.<id> |
string | object | — | Search credential id → host env var name (string) or { value } literal; enabled adapters inject supported native env vars via ${VAR} references, literals inject directly |
workspaces.defaults.agent.web_search.credentials.<id>.value |
string | — | Literal search credential (object form); sealed when published to the database |
workspaces.defaults.agent.web_search.searxng.endpoint |
string | — | SearXNG endpoint URL |
workspaces.defaults.agent.web_search.searxng.categories |
string | — | SearXNG categories filter |
workspaces.defaults.agent.web_search.searxng.engines |
string | — | SearXNG engines filter |
workspaces.defaults.agent.web_search.searxng.language |
string | — | SearXNG language filter |
workspaces.defaults.agent.web_search.searxng.safesearch |
int 0–2 | — | SearXNG safe-search level |
workspaces.defaults.outputs |
object | — | Default outputs (see outputs workspace fields) |
workspaces.defaults.prompt.base_system_prompt_file |
string | — | Custom base system prompt file (deployment-root-relative) |
workspaces.defaults.prompt.system_prompt |
string | — | Named reference into prompts.system.<name>; replaces the base prompt (wins over base_system_prompt_file and the built-in default) |
workspaces.defaults.prompt.extra_system_prompt |
string | — | Named reference into prompts.system.<name>; appended after the resolved base prompt |
workspaces.defaults.prompt.force_skills |
string[] | — | Skill names always activated, ignoring Applies To globs |
workspaces.defaults.context_repositories[].alias |
string | — | Path-safe alias (^[A-Za-z0-9][A-Za-z0-9._-]*$, unique per workspace) |
workspaces.defaults.context_repositories[].kind |
enum | — | git, p4, svn |
workspaces.defaults.context_repositories[].url |
string | — | Git repository URL (required for kind: git) |
workspaces.defaults.context_repositories[].ref |
string | — | Git branch/tag pin |
workspaces.defaults.context_repositories[].token_env |
string | — | Env var name holding the git http(s) token |
workspaces.defaults.context_repositories[].token |
string | — | Literal git http(s) token; mutually exclusive with token_env; sealed when published to the database |
workspaces.defaults.context_repositories[].repository_url |
string | — | SVN repository URL (required for kind: svn) |
workspaces.defaults.context_repositories[].revision |
string | int | — | Revision pin for svn/p4 |
workspaces.defaults.context_repositories[].port |
string | — | P4 port |
workspaces.defaults.context_repositories[].user_env |
string | — | Env var name holding the P4 user |
workspaces.defaults.context_repositories[].user |
string | — | Literal P4 user (identifier, not sealed); mutually exclusive with user_env |
workspaces.defaults.context_repositories[].ticket_env |
string | — | Env var name holding the P4 ticket |
workspaces.defaults.context_repositories[].ticket |
string | — | Literal P4 ticket; mutually exclusive with ticket_env; sealed when published to the database |
workspaces.defaults.context_repositories[].password_env |
string | — | Env var name holding the P4 password |
workspaces.defaults.context_repositories[].password |
string | — | Literal P4 password; mutually exclusive with password_env; sealed when published to the database |
workspaces.defaults.context_repositories[].depot_path |
string | — | P4 depot path (required for kind: p4) |
workspaces.defaults.context_repositories[].max_mb |
int > 0 | 512 |
Per-repository post-materialization size cap in MB |
workspaces.instances |
map | {} |
Per-workspace instances keyed by workspace id |
workspaces.instances.<id>.source_repo.trigger |
string | — | Trigger profile name |
workspaces.instances.<id>.source_repo.repo |
string | — | Repository reference |
workspaces.instances.<id>.match[] |
array | — | Multi-project match rules (OR-ed; fields within a rule are AND-ed). Mutually exclusive with source_repo. Admission-time matching is live for git webhooks (GitHub/GitLab/Gitea/Forgejo); P4/SVN use background routing receipts (see note below) |
workspaces.instances.<id>.match[].id |
string | — | Optional rule id; unique per definition |
workspaces.instances.<id>.match[].triggers |
string[] | — | Trigger profile names; every name must exist in triggers[] |
workspaces.instances.<id>.match[].source.<id>.exact |
string | — | Case-sensitive plain equality on the source field (vcs, repo_ref, repository, namespace, project_key, branch, or ref); exactly one of exact / glob / regex per matcher |
workspaces.instances.<id>.match[].source.<id>.glob |
string | — | Full-field glob; * crosses /, ? matches one code point; no extglob/brace/class semantics |
workspaces.instances.<id>.match[].source.<id>.regex |
string | — | RE2 regex; substring match unless anchored with ^ / $ |
workspaces.instances.<id>.match[].source.<id>.ignore_case |
boolean | — | Case folding for matching only; never rewrites identity |
workspaces.instances.<id>.work_path |
string | — | Handlebars path template (whitelisted AST: segment/default/hash/lower helpers only; output must be a relative / path). Requires match. Default: {{workspace.id}} |
workspaces.instances.<id>.enabled |
boolean | — | Enabled when omitted; false stops new admission while retaining existing snapshots |
workspaces.instances.<id>.model_chain |
string | inherit | Main-group override referencing llm.model_chain |
workspaces.instances.<id>.triage_model_chain |
string | inherit | Lifecycle-group override; if absent at all layers, uses this workspace’s main group |
workspaces.instances.<id>.author_resolution_model_chain |
string | inherit | Identity group override over workspace defaults, then global identity group, then llm.default_model_chain |
workspaces.instances.<id>.agent.default |
enum | — | Execution mode override; selected per run through the merged workspace layers (see note below) |
workspaces.instances.<id>.agent.timeout_seconds |
int > 0 | — | Hard per-run timeout; on timeout the whole process tree is killed |
workspaces.instances.<id>.agent.auto_approve |
boolean | — | Passed to the selected adapter; false removes automatic approval where supported |
workspaces.instances.<id>.agent.context_compaction.auto |
boolean | — | Enable auto-compaction |
workspaces.instances.<id>.agent.context_compaction.threshold_percent |
int 1–100 | — | Compaction trigger threshold |
workspaces.instances.<id>.agent.context_compaction.prune |
boolean | — | Prune compacted history |
workspaces.instances.<id>.agent.web_search.enabled |
boolean | — | Enable the agent’s built-in web search tool for reviews (omp web_search.enabled; kilo/opencode permission + activation env; claude-code/copilot-cli CLI switch) |
workspaces.instances.<id>.agent.web_search.providers |
string[] | — | Ordered providers: full omp chain; kilo accepts exa; opencode selects the first exa/parallel |
workspaces.instances.<id>.agent.web_search.exclude |
string[] | — | Provider ids removed from the search chain → providers.webSearchExclude |
workspaces.instances.<id>.agent.web_search.timeout_seconds |
int 1–300 | — | Per-provider transport timeout → providers.webSearchTimeoutSeconds |
workspaces.instances.<id>.agent.web_search.credentials.<id> |
string | object | — | Search credential id → host env var name (string) or { value } literal; enabled adapters inject supported native env vars via ${VAR} references, literals inject directly |
workspaces.instances.<id>.agent.web_search.credentials.<id>.value |
string | — | Literal search credential (object form); sealed when published to the database |
workspaces.instances.<id>.agent.web_search.searxng.endpoint |
string | — | SearXNG endpoint URL |
workspaces.instances.<id>.agent.web_search.searxng.categories |
string | — | SearXNG categories filter |
workspaces.instances.<id>.agent.web_search.searxng.engines |
string | — | SearXNG engines filter |
workspaces.instances.<id>.agent.web_search.searxng.language |
string | — | SearXNG language filter |
workspaces.instances.<id>.agent.web_search.searxng.safesearch |
int 0–2 | — | SearXNG safe-search level |
workspaces.instances.<id>.review |
object | — | Review config override (see review) |
workspaces.instances.<id>.outputs |
object | — | Outputs override |
workspaces.instances.<id>.sandbox |
object | — | Sandbox override; deep-merged per run over global → defaults → instance (see note below) |
workspaces.instances.<id>.triage |
object | — | Issue triage override (Gitea/Forgejo only) |
workspaces.instances.<id>.prompt |
object | — | Prompt override (same shape as workspaces.defaults.prompt) |
workspaces.instances.<id>.context_repositories[].alias |
string | — | Path-safe alias (^[A-Za-z0-9][A-Za-z0-9._-]*$, unique per workspace); names the mount path |
workspaces.instances.<id>.context_repositories[].kind |
enum | — | git, p4, svn |
workspaces.instances.<id>.context_repositories[].url |
string | — | Git repository URL (required for kind: git) |
workspaces.instances.<id>.context_repositories[].ref |
string | — | Git branch/tag pin (defaults to the remote default branch) |
workspaces.instances.<id>.context_repositories[].token_env |
string | — | Env var name holding the git http(s) token (injected via http.extraHeader, never written to disk) |
workspaces.instances.<id>.context_repositories[].token |
string | — | Literal git http(s) token; mutually exclusive with token_env; sealed when published to the database |
workspaces.instances.<id>.context_repositories[].repository_url |
string | — | SVN repository URL (required for kind: svn) |
workspaces.instances.<id>.context_repositories[].revision |
string | int | — | Revision pin for svn/p4 (defaults to latest) |
workspaces.instances.<id>.context_repositories[].port |
string | — | P4 port (e.g. ssl:p4.example.com:1666) |
workspaces.instances.<id>.context_repositories[].user_env |
string | — | Env var name holding the P4 user |
workspaces.instances.<id>.context_repositories[].user |
string | — | Literal P4 user (identifier, not sealed); mutually exclusive with user_env |
workspaces.instances.<id>.context_repositories[].ticket_env |
string | — | Env var name holding the P4 ticket |
workspaces.instances.<id>.context_repositories[].ticket |
string | — | Literal P4 ticket; mutually exclusive with ticket_env; sealed when published to the database |
workspaces.instances.<id>.context_repositories[].password_env |
string | — | Env var name holding the P4 password (alternative to ticket_env) |
workspaces.instances.<id>.context_repositories[].password |
string | — | Literal P4 password; mutually exclusive with password_env; sealed when published to the database |
workspaces.instances.<id>.context_repositories[].depot_path |
string | — | P4 depot path (required for kind: p4, usually ending in /...) |
workspaces.instances.<id>.context_repositories[].max_mb |
int > 0 | 512 |
Per-repository post-materialization size cap in MB; exceeding it fails and cleans up |
workspaces.instances.<id>.auth.api_key_env |
string | — | Per-workspace API key env var |
workspaces.instances.<id>.auth.api_key |
string | — | Literal per-workspace API key; mutually exclusive with api_key_env; sealed when published to the database |
workspaces.instances.<id>.auth.enabled |
boolean | true |
Toggle per-workspace API key |
Workspace ids must not collide with the reserved keys cache, defaults,
instances. When an instance defines its own context_repositories, the list
replaces workspaces.defaults wholesale instead of merging per entry.
outputs
Section titled “outputs”Narrative: Output channels and routing.
| Field | Type | Default | Description |
|---|---|---|---|
outputs.template_engine |
enum | handlebars |
Template engine. eta is accepted by the schema but unimplemented; only handlebars works |
outputs.templates.<id> |
string | — | Named Handlebars template document (markdown, optional frontmatter metadata; only the body is rendered), referenced by channel templates.* |
outputs.no_problems |
object | — | Global zero-problem policy |
outputs.no_problems.action |
enum | — | publish, suppress, or publish_if_summary |
outputs.channels[] |
array | [] |
Output channel definitions |
outputs.channels[].name |
string | — | Channel name used in routes and template resolution |
outputs.channels[].kind |
string | — | Channel kind (free-form; constrained by the output registry) |
outputs.channels[].trigger |
string | — | Trigger name this channel is bound to |
outputs.channels[].mention_author |
boolean | — | @-mention the resolved commit author |
outputs.channels[].mention_fallback |
enum | — | all or skip when the author cannot be resolved |
outputs.channels[].no_problems |
object | — | Per-channel zero-problem policy |
outputs.channels[].commit_url_template |
string | — | Commit link template |
outputs.channels[].revision_url_template |
string | — | Revision link template |
outputs.channels[].change_url_template |
string | — | Changelist link template |
outputs.channels[].templates.problem |
string | — | Problem-template reference into outputs.templates.<name>; wins over workspace-directory and built-in lookup |
outputs.channels[].templates.summary |
string | — | Summary-template reference into outputs.templates.<name>; wins over workspace-directory and built-in lookup |
outputs.channels[].marker_prefix |
string | — | Managed-issue title prefix (default [AICR]) |
outputs.channels[].marker_label |
string | — | Hidden body marker scoping managed issues |
outputs.channels[].label_ids |
int[] | — | Gitea label IDs to attach |
outputs.channels[].labels |
string[] | — | GitHub label names to attach |
outputs.channels[].issue_mode |
enum | — | per_problem, consolidated, per_commit |
outputs.channels[].issue_link_card |
enum | — | Feishu card content when the summary route also records a managed issue and the card links to it: brief (headline + count + link), titles (adds one title line per problem; runtime default), full (complete problem sections); feishu_bot/feishu_app only |
outputs.channels[].resolved_action |
enum | — | none, close, mark_resolved, delete (Gitea only) |
outputs.channels[].assign_committer |
boolean | — | Assign the resolved review author to new managed issues (default true); email blacklist blocks API/pusher fallback; OWNERS remain independent |
outputs.channels[].owners_file |
string | — | Owners file path (default OWNERS) |
outputs.channels[].add_owners_as_assignees |
boolean | — | Add matched OWNERS entries as assignees |
outputs.channels[].severity_label_prefix |
string | — | Auto-create/attach a severity label such as aicr:problem:high |
outputs.channels[].severity_label_colors |
map | — | Severity-to-color map for auto-created labels |
outputs.channels[].review_mode |
enum | — | auto, review, comment |
outputs.channels[].review_event |
enum | — | COMMENT or REQUEST_CHANGES |
outputs.channels[].review_update_strategy |
enum | — | always_new or update_existing |
outputs.channels[].notify_feishu |
object | — | Issue-created Feishu notification (webhook_url_env or literal webhook_url, optional secret_env/literal secret) |
outputs.channels[].token |
string | — | Literal channel API token; mutually exclusive with token_env; sealed when published to the database |
outputs.channels[].webhook_url |
string | — | Literal bot webhook URL (feishu_bot/wecom_bot); mutually exclusive with webhook_url_env; sealed when published to the database |
outputs.channels[].secret |
string | — | Literal Feishu signing secret; mutually exclusive with secret_env; sealed when published to the database |
outputs.author_resolution |
object | — | email_mappings map, email_blacklist array and directory_cache_ttl_seconds |
outputs.author_resolution.directory_cache_ttl_seconds |
int | 43200 | Global member-directory cache duration, 0–604800 seconds; 0 disables reuse |
outputs.routes.default |
object | — | Default route applied when no rule matches |
outputs.routes.rules[] |
array | [] |
Ordered routing rules |
outputs.routes.rules[].match.trigger |
string | — | Trigger name to match |
outputs.routes.rules[].match.target_kind |
enum | — | Target kind (pull_request, push, commit, issue, manual, scheduled); pr is normalized to pull_request. GitLab MRs are reported as pull_request. |
outputs.routes.rules[].line_comments |
string[] | — | Channel names to receive line-comment output |
outputs.routes.rules[].summary |
string[] | — | Channel names to receive summary output |
Feishu application channel fields
Section titled “Feishu application channel fields”These fields apply to feishu_app; see the IM bots guide.
| Field | Type | Default | Meaning |
|---|---|---|---|
outputs.channels[].app_id |
string | — | Required custom application ID |
outputs.channels[].app_secret |
string | — | Literal App Secret; sealed in database config; mutually exclusive with app_secret_env |
outputs.channels[].app_secret_env |
string | — | Environment variable holding the App Secret |
outputs.channels[].receive_id |
string | — | Required report recipient |
outputs.channels[].receive_id_type |
enum | — | chat_id, open_id, user_id, union_id or email; runtime default chat_id |
outputs.channels[].member_directory.chat_id |
string | — | Source group whose members form the identity directory |
outputs.channels[].member_directory.cache_ttl_seconds |
int | — | Directory cache duration, 0–604800 seconds; overrides outputs.author_resolution.directory_cache_ttl_seconds; runtime default 43200 (12h) |
outputs.channels[].user_mappings.<id> |
string | — | Exact author/workspace identifier to the application’s open_id |
outputs.channels[].guess_author |
boolean | — | Runtime default true; permits workspace heuristics and dedicated model fallback after exact matching; mention_author separately enables notifications |
Member directory sources and IM channel links
Section titled “Member directory sources and IM channel links”member_directory accepts the historical Feishu API form (chat_id, optionally
cache_ttl_seconds), an explicit source: feishu_api form with the same fields,
and a source: file form backed by a strict YAML/JSON member file. The file
source, author_mappings, connection and target are schema-validated while
their sender/directory wiring lands progressively (see the IM bots
guide); database publishing still rejects channel
records that have no runtime consumer, so these fields do not form a working
integration on their own.
| Field | Type | Default | Meaning |
|---|---|---|---|
outputs.channels[].member_directory.source |
enum | — | feishu_api (explicit form of the historical directory) or file (external member directory); omitted means the legacy {chat_id, cache_ttl_seconds} form |
outputs.channels[].member_directory.path |
string | — | file only: member file path resolved against the config file’s base directory, never the process working directory |
outputs.channels[].member_directory.directory_id |
string | — | file only: name of the directory inside the member file that this channel uses |
outputs.channels[].member_directory.identity_scope.kind |
enum | — | file only: identity namespace kind — wecom_corp, feishu_app or feishu_tenant |
outputs.channels[].member_directory.identity_scope.id |
string | — | file only: corp/app/tenant namespace id; equal ids in different scopes never merge |
outputs.channels[].member_directory.watch |
boolean | — | file only: runtime default true; false keeps only the periodic reload check |
outputs.channels[].member_directory.debounce_ms |
int 50–2000 | — | file only: watch debounce window; runtime default 300 |
outputs.channels[].member_directory.poll_interval_seconds |
int 5–300 | — | file only: periodic content-digest check interval; runtime default 30 |
outputs.channels[].member_directory.allowed_root |
string | — | file only: trusted root for real-path boundary checks; defaults to the config base directory |
outputs.channels[].author_mappings.<id> |
string | — | Exact author/workspace identifier to a file member key; mutually exclusive with user_mappings |
outputs.channels[].connection |
string | — | Reference into im.connections; required for wecom_app channels and replaces inline credentials on feishu_app channels |
outputs.channels[].target |
object | — | wecom_app only: {kind: recipients, users/parties/tags} or {kind: appchat, chat_id} — recipients lists and appchat targets are mutually exclusive |
IM connections and command bindings (IM bots guide).
The schema accepts this namespace while the senders, callbacks and workers land
progressively; every binding is disabled by default and database publishing keeps
rejecting consumer-less records, so configuring im alone does not enable chat
commands. review commands additionally require a persistent config store.
| Field | Type | Default | Description |
|---|---|---|---|
im.connections.<id> |
object | — | Named connection map; connection ids match [A-Za-z0-9][A-Za-z0-9_-]*; kind selects wecom_app (corp_id, agent_id, app_secret/app_secret_env), wecom_aibot (corp_id; callback mode adds callback and an optional aibot_id, long-connection mode adds aibot_id plus secret/secret_env) or feishu_app (app_id, app_secret/app_secret_env, optional base_url, tenant_key), each with an optional callback (enabled defaults to false at runtime; WeCom token/encoding_aes_key and Feishu verification_token/encrypt_key, each as a literal or *_env pair that enabled callbacks must provide). Both wecom_aibot and feishu_app pick their receive mode by callback.enabled: true serves the event callback (GET/POST /callbacks/im/<id>); without an enabled callback the server dials out instead (WeCom needs aibot_id+secret, Feishu needs app_id+app_secret via the official SDK long connection) |
im.command_bindings.<id>.enabled |
boolean | — | Runtime default false; disabled drafts stay savable while wiring lands |
im.command_bindings.<id>.connection |
string | — | Named reference into im.connections; enabling a binding requires an existing, enabled connection |
im.command_bindings.<id>.actors[] |
object | — | Matcher array; any hit authorizes. Scope matchers discriminate on kind: any (any authenticated actor), wecom_department (recursive defaults to true), wecom_tag (the role/user-group carrier), wecom_position, wecom_extattr (name+value custom field), feishu_chat (chat_id membership), feishu_department, feishu_job_title; matchers adapt to the connection’s platform and resolve against server-side directory snapshots — a missing directory fails that dimension closed |
im.command_bindings.<id>.actors[].type |
enum | — | Exact-principal form: wecom_userid, wecom_encrypted_userid or feishu_open_id; the identity namespace comes from the connection |
im.command_bindings.<id>.actors[].id |
string | — | Exact typed platform id; no fuzzy or directory-derived authorization |
im.command_bindings.<id>.actors[].expires_at |
string | — | RFC 3339 timestamp; an expired matcher (exact principals and any alike) stops matching on its own — the general temporary-authorization mechanism |
im.command_bindings.<id>.conversations[] |
object | — | app_direct (WeCom/Feishu applications), bot_direct (WeCom API bot) or group with a non-empty id; kinds must match the connection protocol |
im.command_bindings.<id>.allow_all_repositories |
boolean | — | Runtime default false; when enabled, repo aliases additionally resolve by exact workspace id or full repo name against observed projects (the projects table), applying to query and review commands |
im.command_bindings.<id>.commands |
enum[] | — | Unique subset of help, chat-id, review, status, cancel plus the query commands projects, reviews, commits, prs, detail, prdetail, queue, running |
im.command_bindings.<id>.repositories.<id>.workspace |
string | — | Repo alias target: workspace id, validated against routing/VCS scope at publish |
im.command_bindings.<id>.repositories.<id>.source_trigger |
string | — | Repo alias target: source trigger name, validated against routing/VCS scope at publish |
im.command_bindings.<id>.repositories.<id>.repo_ref |
string | — | Repo alias target: repository reference, validated against routing/VCS scope at publish |
im.command_bindings.<id>.report_policy |
enum | — | workspace_routes only; runtime default |
prompts
Section titled “prompts”Named system-prompt documents (markdown, optional frontmatter metadata; only the
body reaches the model). A workspace’s prompt.system_prompt references one name
to replace the built-in base prompt; prompt.extra_system_prompt references one
to append after the base. The built-in base
(prompts/system/code-reviewer.system.md) is never stored; the management UI
offers it read-only with a “copy as new config” action.
| Field | Type | Default | Description |
|---|---|---|---|
prompts.system.<id> |
string | — | Name → system-prompt document |
Narrative: Agent and sandbox.
| Field | Type | Default | Description |
|---|---|---|---|
agent.default |
enum | kilo |
Execution mode: kilo, opencode, zoo, copilot-cli, claude-code, pi, oh-my-pi, or native-llm (direct gateway call) |
agent.timeout_seconds |
int > 0 | 1800 |
Hard timeout for a CLI agent pass; on timeout the whole process tree is killed; unused by native-llm |
agent.auto_approve |
boolean | true |
Passed to the selected CLI adapter; false removes automatic approval where supported; unused by native-llm |
agent.sandbox |
object | {} |
CLI sandbox backend; unused by native-llm |
agent.sandbox.kind |
enum | — | Sandbox kind (see enum table) |
agent.sandbox.engine |
enum | — | Container engine selection |
agent.sandbox.image |
string | — | Container image to use |
agent.context_compaction |
object | { auto: true, prune: true } |
Conversation-level auto-compaction injected into each agent |
agent.context_compaction.auto |
boolean | true |
Enable auto-compaction |
agent.context_compaction.threshold_percent |
int 1–100 | — | Compaction trigger threshold |
agent.context_compaction.prune |
boolean | true |
Prune compacted history |
agent.web_search |
object | { enabled: false } |
Built-in search tool control per agent (omp/kilo/opencode config-level, claude-code/copilot-cli switch-only, zoo/pi none); AICR always materializes the explicit switch |
agent.web_search.enabled |
boolean | false |
Enable the agent’s built-in web search tool for reviews (omp web_search.enabled; kilo/opencode permission + activation env; claude-code/copilot-cli CLI switch) |
agent.web_search.providers |
string[] | [] |
Ordered providers: full omp chain; kilo accepts exa; opencode selects the first exa/parallel |
agent.web_search.exclude |
string[] | [] |
Provider ids removed from the search chain → providers.webSearchExclude |
agent.web_search.timeout_seconds |
int 1–300 | — | Per-provider transport timeout → providers.webSearchTimeoutSeconds |
agent.web_search.credentials |
map | {} |
Search credential id → host env var name; enabled adapters inject supported native env vars via ${VAR} references |
agent.web_search.searxng |
object | — | Self-hosted SearXNG settings (keeps queries inside your network) |
agent.web_search.searxng.endpoint |
string | — | SearXNG endpoint URL |
agent.web_search.searxng.categories |
string | — | SearXNG categories filter |
agent.web_search.searxng.engines |
string | — | SearXNG engines filter |
agent.web_search.searxng.language |
string | — | SearXNG language filter |
agent.web_search.searxng.safesearch |
int 0–2 | — | SearXNG safe-search level |
review
Section titled “review”| Field | Type | Default | Description |
|---|---|---|---|
review.languages_auto_detect |
boolean | true |
Auto-detect review languages |
review.include |
string[] | ["**/*"] |
Path globs: * stays within a directory; ** spans zero or more directories |
review.exclude |
string[] | ["**/vendor/**", "**/*.min.js", "**/*.lock"] |
Glob patterns to exclude (applied after include) |
review.max_files |
int > 0 | 2000 |
Max analyzed files per review; counts only files that pass the review.include/exclude filters — filter-passing paths beyond the cap are dropped with a visible truncation log, excluded files never count |
review.max_patch_bytes |
int > 0 | 20971520 |
UTF-8 patch budget (20 MiB) over the analyzed diff only: files dropped by the review filters never count toward it; oversized analyzed patches are rejected before model calls and the rejection reason shows as a failed run in Recent Runs |
review.incremental |
boolean | true |
False adds complete head files, bounded by max_patch_bytes |
review.skip_lgtm |
boolean | true |
Skip reviews that look clean |
review.output_language |
string | zh-CN |
Output language for summaries |
review.commit_strategy |
enum | aggregate |
Aggregate endpoints, labelled per-commit patches in one analysis, or head-only; rewritten history retains endpoint comparison |
review.log_thinking |
boolean | true |
Log orchestrator thinking/execution traces (set false to silence) |
review.git.allow_deepen |
boolean | false |
Allow git fetch --deepen for shallow clones |
review.labels.ignore |
string[] | ["aicr:ignore", "aicr-ignore"] |
Labels that skip review |
review.labels.auto_tag |
string | — | Fixed tag added when AICR starts |
review.labels.reviewed_tag |
string | — | Tag added when review completes |
review.problem_issue.max_recent_issues |
int 1–200 | 30 |
Cap on recent open managed issues reconciled per run |
review.fetch_extra.max_bytes |
int > 0 | — | Total UTF-8 extra-context bytes per run, including concurrent requests |
review.fetch_extra.max_files |
int > 0 | — | Distinct extra-context paths per run |
review.fetch_extra.allow_paths |
string[] | — | Allowed path globs for extra-context fetch |
review.reflection.enabled |
boolean | false |
Enable reflection memory |
review.reflection.mode |
enum | — | off, light, thorough |
review.reflection.memory.max_size_kb |
int > 0 | — | Max memory size in KB |
review.reflection.memory.max_entries |
int > 0 | — | Max memory entries |
review.reflection.memory.retention_days |
int > 0 | 90 |
Memory TTL in days |
review.auto_commit.delay_seconds |
int 0–31536000 | 300 |
First-receive delay before an automatic commit becomes due; 0 disables the wait |
review.auto_commit.queued_timeout_hours |
int 0–8760 | 72 |
Maximum wait since admission for automatic entries/batches and IM requests; expired work closes with queued_timeout and IM notification; running work with a valid lease is preserved; 0 disables expiration |
review.auto_commit.schedule.timezone |
string | UTC |
IANA timezone for the execution schedule |
review.auto_commit.schedule.rules[] |
object[] | — | Weekly rule groups (days weekday set + windows HH:mm ranges, union across groups); rules: [] lifts all weekly limits |
review.auto_commit.exclude_sources[] |
object[] | — | Bot/CI source exclusion rules (id, vcs, match field matchers with exactly one of glob/regex); [] clears inherited rules |
review.auto_commit.include_branches |
string[] | — | Exact, case-sensitive branch names without refs/heads/ (no glob/regex); unlisted automatic pushes are ignored before receipt persistence, PR/MR/comment flows and branchless P4/SVN hooks are unaffected; nearest layer wins, [] accepts all branches |
review.pull_request.schedule.timezone |
string | UTC |
IANA timezone for the PR/MR execution schedule |
review.pull_request.schedule.rules[] |
object[] | — | Weekly rule groups for PR/MR review (days weekday set + windows HH:mm ranges, union across groups); rules: [] lifts all weekly limits; unset everywhere falls back to review.auto_commit.schedule |
review.pull_request.include_target_branches |
string[] | — | Exact, case-sensitive PR/MR target (base) branch names (no glob/regex); unlisted targets are ignored at reception, unknown targets are allowed, and push/issue/manual flows are unaffected; nearest layer wins, [] accepts all target branches |
Narrative: Queue and retry.
| Field | Type | Default | Description |
|---|---|---|---|
queue.kind |
enum | memory |
Queue backend: memory, sqlite, redis; rabbitmq is reserved (warns and falls back to memory) |
queue.sqlite.path |
string | data/queue.sqlite |
SQLite queue DB path |
queue.sqlite.lock_ttl_seconds |
int > 0 | 300 |
Stale-running job reclaim TTL |
queue.workers.concurrency |
int > 0 | 4 |
Global worker concurrency |
queue.workers.per_workspace_concurrency |
int > 0 | 1 |
Per-workspace concurrency cap |
queue.workers.lock_ttl_seconds |
int > 0 | 1800 |
Reserved; lock expiry is configured by the queue backend |
queue.rate_limit.per_provider_rps |
map | — | Per-provider requests-per-second cap |
queue.retry.attempts |
int > 0 | 3 |
Trigger-level retry attempts (legacy max_attempts normalized) |
queue.retry.backoff |
object | exponential, 5000→60000ms with jitter |
kind, base_ms, max_ms, jitter |
queue.dead_letter.enabled |
boolean | — | Reserved — accepted by the schema but not consumed at runtime |
queue.dead_letter.max_age_hours |
int > 0 | — | Reserved — accepted by the schema but not consumed at runtime |
config_sources
Section titled “config_sources”Dynamic configuration source switch (file-only by default). This section is bootstrap-owned: the database can never edit it.
| Field | Type | Default | Description |
|---|---|---|---|
config_sources.database.enabled |
boolean | false |
Enable the database configuration source. When enabled, admissions adopt the durable config head and the admin config API can publish revisions; when disabled the process stays file-only. |
config_sources.database.backend |
enum | storage |
storage rides storage.database (SQLite/PostgreSQL); redis reuses the storage.cache.redis connection declaration. |
config_sources.database.namespace |
string | default |
Config namespace: 1–64 letters, digits, dots, underscores or hyphens; starts with a letter or digit. |
config_sources.runtime.refresh_interval_seconds |
int 1–3600 | 5 |
Background generation refresh cadence. Not the admission consistency barrier: every webhook admission re-reads the durable head regardless. |
config_sources.secret_refs[].env |
string | — | Deployment-authorized environment variable name |
config_sources.secret_refs[].target |
string[] | — | Exact path tokens; entity IDs/names and context repository aliases replace array indices |
config_sources.secret_refs[].destinations.<id> |
unknown | — | Exact destination context, including kind, endpoints and linked trigger destination; see example/config.yaml |
storage
Section titled “storage”Narrative: Storage.
| Field | Type | Default | Description |
|---|---|---|---|
storage.database.kind |
enum | sqlite |
Database backend (sqlite or postgres) |
storage.database.sqlite.path |
string | /app/data/aicr.sqlite |
SQLite DB path |
storage.database.postgres.url_env |
string | — | Postgres connection-string env var |
storage.database.migrate |
enum | auto |
Startup schema-migration mode (auto applies, verify checks config and business schemas) |
storage.cache.kind |
enum | memory |
Cache backend |
storage.cache.redis.url_env |
string | — | Redis connection-string env var |
storage.cache.ttl_seconds |
int > 0 | — | Cache TTL |
storage.object.kind |
enum | filesystem |
Object storage backend |
storage.object.filesystem.root |
string | /app/data/objects |
Filesystem object root |
storage.object.s3.endpoint_url_env |
string | — | S3-compatible endpoint env var (AWS S3, MinIO, RustFS) |
storage.object.s3.bucket |
string | — | Bucket name |
storage.object.s3.region_env |
string | — | Region env var |
storage.object.s3.access_key_id_env |
string | — | Access key id env var |
storage.object.s3.secret_access_key_env |
string | — | Secret access key env var |
storage.object.s3.force_path_style |
boolean | — | Use path-style addressing (MinIO/RustFS) |
storage.retention.deleted_project_grace_days |
int ≥ 0 | 30 |
Soft-deleted project grace period before hard delete |
storage.retention.recent_runs.max_count |
int 1–1000000 | 2000 |
Most recent run details to retain; aggregate statistics remain available |
storage.retention.recent_runs.max_age_months |
int 1–1200 | 6 |
Maximum age of run details in UTC calendar months, from start time |
storage.retention.events.max_count |
int 1–1000000 | 2000 |
Most recent received events to retain |
storage.retention.events.max_age_months |
int 1–1200 | 6 |
Maximum age of events in UTC calendar months, from receipt time |
storage.retention.queue.max_count |
int 1–1000000 | 1000 |
Most recent terminal automatic-commit batches to retain; active and retrying work is protected |
storage.retention.queue.max_age_months |
int 1–1200 | 6 |
Maximum age of terminal batch history in UTC calendar months, from creation time |
compression
Section titled “compression”| Field | Type | Default | Description |
|---|---|---|---|
compression.trigger_tokens |
int > 0 | derived from the model, see below | Token threshold that triggers diff compression |
compression.max_input_ratio |
number 0–1 | 0.6 |
Max input ratio before compression |
compression.summarize_model_role |
string | light |
Model role used for summarize stage (light/heavy/any) |
compression.keep_hunks_top_k |
int > 0 | 30 |
Number of highest-risk hunks to keep verbatim |
compression.context_lines |
int > 0 | 5 |
Context lines retained around kept hunks |
compression.per_model_overrides |
map | — | Per-model trigger_tokens overrides |
When compression is omitted, bootstrap derives a default
trigger_tokens = min(131072, max(8192, floor(contextWindow × 0.6))) from the
review model’s context window.
server
Section titled “server”| Field | Type | Default | Description |
|---|---|---|---|
server.port |
int > 0 | 8080 |
HTTP listen port |
server.hostname |
string | 0.0.0.0 |
Listen hostname |
server.trust_proxy |
boolean | enum | string[] | false |
Trust proxy setting (loopback/linklocal/uniquelocal or CIDR list) |
server.base_url |
string | — | External base URL |
server.path_prefix |
string | — | URL path prefix (reverse-proxy subpath) |
server.auth.api_key_env |
string | — | Global API key env var (protects /triggers/*) |
server.auth.api_key |
string | — | Literal global API key; mutually exclusive with api_key_env (literal wins) |
server.auth.enabled |
boolean | true |
Toggle global API key |
Narrative: Dashboard and logs.
| Field | Type | Default | Description |
|---|---|---|---|
admin.username_env |
string | AICR_ADMIN_USERNAME |
Admin username env var |
admin.password_env |
string | AICR_ADMIN_PASSWORD |
Admin password env var |
admin.password_hash_env |
string | — | Admin password hash env var (sha256:<hex>); takes precedence over password_env |
admin.password |
string | — | Literal admin password; mutually exclusive with password_env (literal wins) |
admin.password_hash |
string | — | Literal admin password hash (sha256:<hex>); mutually exclusive with password_hash_env |
admin.session_ttl_seconds |
int > 0 | 86400 |
Session TTL in seconds (not minutes) |