Public alpha. Keys may change;
versionguards incompatible changes.
Configuration is layered, later layers winning:
- built-in defaults (below);
- the user config,
config.yamlin the config directory; - a workspace override,
workspaces/<workspace>.yamlin the config directory (see Workspace overrides).
The config directory is $XDG_CONFIG_HOME/boundedcode (usually
~/.config/boundedcode), or $BOUNDEDCODE_HOME/config when
BOUNDEDCODE_HOME is set. boundedcode init (or the first step of
boundedcode setup) writes a config.yaml with every key; a missing file
means all defaults. setup also edits inference.server_binary and
bench_binary after it builds llama.cpp. Tools that setup installs go to
<data dir>/bin (usually ~/.local/share/boundedcode/bin), which BoundedCode
puts first on its own PATH.
Every file is decoded strictly: an unknown key is an error, so a typo fails
loudly instead of being ignored. After decoding, the whole configuration is
validated; all problems are reported at once. Durations are Go duration
strings (90s, 10m, 4h).
| Key | Default | Notes |
|---|---|---|
version |
1 |
Config schema version. Any other value is rejected. |
models_dir |
empty | Directory holding GGUF model files. |
default_model |
qwen3.6-35b-a3b |
Model profile used when --model is not given. |
| Key | Default | Notes |
|---|---|---|
provider |
local |
local (llama.cpp, configured by the keys below) or a cloud API: openai, anthropic, gemini, openai-compatible. See Model provider. |
providers.<name> |
empty | Each cloud provider's settings (below); kept when you switch providers. |
mode |
managed |
Local provider only: managed (boundedcode starts llama-server) or external (you run an OpenAI-compatible server). |
server_binary |
llama-server |
Required in managed mode. |
bench_binary |
llama-bench |
Path to llama-bench, set by init --llama-bench. |
external_url |
empty | Required in external mode. |
host |
127.0.0.1 |
Managed server listen address. |
port |
8765 |
Managed server port, 1–65535. |
startup_timeout |
5m |
Time allowed for the managed server to load the model. |
request_timeout |
10m |
Bound on one completion; also detects stalls. |
idle_sleep |
30m |
The managed server unloads the model after this much inactivity and reloads it on the next request. 0s disables; negative values are rejected. |
default_model names the local model profile (built in, or a YAML file in
<config>/models/ that overrides one by name). bcode model recommend rates
the profiles against this machine and proposes one; bcode model use NAME
sets default_model (and selects the local provider), and bcode model fetch NAME downloads it into models_dir (default ~/.local/share/boundedcode/models).
Profiles record status (validated, experimental, or review for a
license under review, which is never offered), the pinned source.revision,
source.size_bytes and source.sha256, and an optional source.license_notice
shown before download. A user profile that overrides a built-in one for the
same file keeps the built-in revision, checksum and status.
bcode provider use NAME [--model ID] [...] sets these keys, and
bcode provider key set NAME stores the API key (from the terminal without
echo, or stdin). The key is kept in the OS credential store (Secret Service
on Linux, the macOS Keychain, Windows Credential Manager), or in an
owner-only credentials.json next to this file when no credential store is
available, never in config.yaml. BOUNDEDCODE_<PROVIDER>_API_KEY (for
example BOUNDEDCODE_ANTHROPIC_API_KEY) overrides the stored key, for
servers and CI; vendor variables such as ANTHROPIC_API_KEY are not read.
BOUNDEDCODE_SECRETS=file skips the credential store.
inference:
provider: anthropic
providers:
anthropic:
model: claude-opus-5-5
effort: high
openai-compatible:
base_url: https://api.groq.com/openai/v1
model: MODEL-ID
context_window: 131072Key (inference.providers.<name>.) |
Notes |
|---|---|
model |
The provider's model id. Required for the selected provider. bcode provider models NAME lists them. |
base_url |
Endpoint override; required for openai-compatible. OpenAI-style URLs end in the API version (https://api.openai.com/v1). |
context_window |
The model's input limit. 0 asks the provider: Anthropic and Gemini report it; OpenAI and compatible APIs do not, so set it for them. |
context_limit |
Cap on the agent's working context before it condenses its history; 0 = 200000. Cloud tokens are billed on every turn. |
effort |
minimal, low, medium, high, xhigh or max; mapped to Anthropic output_config.effort, Gemini thinkingLevel (up to high), OpenAI reasoning_effort. Empty = the model's default. |
input_price, cached_input_price, output_price |
USD per million tokens, for the cost estimate in bcode stats. No prices are built in. |
The token budget (budgets.max_local_tokens) applies to whichever provider
runs the task. --model on task create/task run overrides the provider's
model for one task.
| Key | Default | Notes |
|---|---|---|
runtime |
openhands |
The only runtime the CLI can run. (A scripted runtime exists for Go tests and cannot be selected here.) |
adapter_dir |
empty | The OpenHands adapter project (adapters/openhands/python). Needed when sandbox.kind is none. |
image |
boundedcode-openhands:local |
Sandbox image, built by sandbox build. |
max_iterations |
150 |
Agent steps per attempt; must be >= 1. |
condenser_max_events |
80 |
History length that triggers OpenHands' summarizing condenser. |
max_output_tokens |
8192 |
Cap on one model response (thinking plus visible output). Thinking alone is capped per model profile by server.reasoning_budget. |
strategy.no_progress_tokens |
40000 |
An attempt is stopped after generating this many tokens without progress (its first edit, a new test file, or an agent-run test going from failing to passing). 0 disables. |
strategy.max_tokens |
50000 |
Hard cap on tokens generated in one attempt. |
strategy.max_duration |
35m |
Hard cap on one attempt's agent turn. |
A stopped attempt is recorded (strategy.stopped event, rejected strategy
with the reason), its work is checkpointed and verified as usual, the
session is compacted, and the next attempt is told what was tried and to
change approach. Stopping does not by itself escalate to the frontier.
| Key | Default | Meaning |
|---|---|---|
contract |
true |
Before the first attempt, derive a compact task contract from the request with the task's model (the local model, or the cloud provider when one is selected): what is required, alternatives the request explicitly allows, constraints, what is out of scope, open questions, and acceptance evidence. It is shown to the agent; the request stays authoritative. HTML comments (issue-template instructions) are removed from the request first, and a contract that names nothing required is asked for once more. |
ambiguity |
ask |
What a material ambiguity does (plausible readings that change behaviour, an API, data, security, compatibility, tests or output; implementation choices do not count). Before either policy applies, each material ambiguity is checked against the request text with one more local-model call: it is dropped, and the reason recorded as a task decision, when the request's own words settle it (for example an expected output) or when fewer than two of its readings are supported by a quote from the request. If that check fails, the ambiguity stays material. ask: the task blocks before implementation with the questions (SPEC_AMBIGUOUS); answer with task run TASK --clarify "...". proceed: the ambiguity is recorded and the agent states and demonstrates the reading it chose (used by benchmarks). Explicitly allowed alternatives never block. |
| Key | Default | Notes |
|---|---|---|
provider |
codebase-memory-mcp |
The only supported value. The key is kept so older files still load. |
binary |
codebase-memory-mcp |
The integration is tested against release 0.11.0 (doctor compares). |
cross_service |
true |
Built-in cross-service contract analyzers in indexing, context packs and escalation. |
compat_gate |
true |
Cross-repository compatibility gate (experimental). After the full gate passes, each gRPC, protobuf or OpenAPI link that the change affects is checked with the dependent repository's own checks, built against the other task repositories' candidate commits. A broken link fails verification; an untested one withholds TASK_VERIFIED. Needs cross_service. Not overridable per workspace. See cross-repo-compatibility.md. |
Optional LSP navigation; see serena.md.
| Key | Default | Notes |
|---|---|---|
enabled |
false |
|
command |
empty | Serena executable. Empty means the copy serena setup installed. |
version |
1.7.0 |
Must be 1.7.0. Other versions fail validation. |
auto_upgrade |
false |
Must be false. |
transport |
stdio |
Must be stdio. |
max_instances |
2 |
Must be >= 1. |
idle_timeout |
10m |
Must be > 0 when enabled. |
startup_timeout |
90s |
Must be > 0 when enabled. |
call_timeout |
30s |
Must be > 0 when enabled. |
| Key | Default | Notes |
|---|---|---|
enabled |
false |
Off means local-only: nothing leaves the machine. |
provider |
codex |
codex or manual (packets are written to disk). |
binary |
codex |
|
model |
empty | Empty uses the provider default. |
require_approval |
true |
Ask before sending each packet. |
max_packet_tokens |
24000 |
Escalation packet cap; must be >= 1. |
timeout |
15m |
|
contain |
true |
Run the frontier CLI in a container that sees only the packet. |
| Key | Default | Notes |
|---|---|---|
kind |
docker |
docker or none. none runs agent tools on the host and is for development only. |
engine |
docker |
docker or podman. |
network |
none |
none or bridge. |
memory |
8g |
Container memory limit. |
cpus |
8 |
Container CPU limit. |
When a budget runs out, the task is parked as blocked. Work is never thrown away.
| Key | Default | Notes |
|---|---|---|
max_attempts |
6 |
Must be >= 1. |
max_wall_clock |
4h |
Must be >= 0. |
max_local_tokens |
4000000 |
Must be >= 0. |
max_escalations |
2 |
Must be >= 0. |
context_pack_tokens |
24000 |
Must be >= 2000. |
| Key | Default | Notes |
|---|---|---|
failed_attempts |
3 |
Z2: consecutive failed verification attempts before escalating; >= 1. |
rejected_strategies |
2 |
Z2: rejected, materially different strategies before escalating; >= 1. |
architectural_risk |
idempotency, outbox, ledger, payment, auth, … | Z1 keywords matched against task text, paths and symbols. |
high_risk_review |
auth, crypto, payment, ledger, terraform, … | Z3 keywords. |
Run boundedcode init and read the generated file for the full keyword lists.
A workspace can change task policy without touching the user config. Put the
file at <config dir>/workspaces/<workspace>.yaml, for example
~/.config/boundedcode/workspaces/payment-platform.yaml:
budgets:
max_attempts: 8
max_escalations: 0 # never escalate for this workspace
escalation:
high_risk_review: [ledger, money, settlement]
repointel:
serena:
enabled: true
frontier:
enabled: falseOnly these keys are allowed:
budgets.*escalation.*repointel.cross_servicerepointel.serena.enabledfrontier.enabled
Machine settings (inference, sandbox, binaries, Serena pins) stay global.
Verification commands are set per repository, in
.boundedcode/verification.yaml, not here.
A key you leave out keeps its value from the user config. A list you set
replaces the user's list; it is not merged with it. The file is decoded
strictly, and the merged result goes through the same validation as
config.yaml. Without the file, the workspace uses the user config as is.
Each repository can define its own verification stages. The file is read from the task's base commit, never from the worktree, so the agent cannot change how its own work is judged; a change to it takes effect for tasks that start after it is committed. Without the file, the built-in presets are used for the languages found at the base commit. The file is decoded strictly: unknown keys are an error.
version: 1
max_changed_files: 200 # diff-scope bound; 0 means 200
deny_paths: ["migrations/*"] # extra patterns the change must not touch
stages:
- name: go-test
run: [go, test, -count=1, "{packages}"]
scope: always # always (default) | targeted | full
timeout: 20m # default 10m
requires: [go.mod] # the stage applies only if these files exist
tests: true # runs tests (see behavioural evidence)
optional: false # skip instead of fail when the tool is missing| Key | Meaning |
|---|---|
name, run |
Required. run is an argument vector, not a shell line; use [sh, -c, "..."] for a script. Every command goes through the same command policy as the agent's commands. {packages} expands to the impact-selected Go packages (or ./... in the full gate). |
scope |
targeted stages run only while iterating, full stages only in the full gate before a task becomes a merge candidate, always in both. |
requires |
Files that must exist for the stage to apply. A required file that exists at the base and is deleted by the change fails the stage, so a change cannot switch a stage off. A stage whose first required file is go.mod is treated as a Go stage by behavioural evidence. |
tests |
Marks a stage that runs tests. Behavioural evidence reruns such stages on the base commit with and without the change's tests. Without the key, a stage whose name or command mentions "test" counts. |
deny_paths |
Repository-relative patterns in Go filepath.Match syntax: * does not cross /, and there is no **. A changed file that matches fails the diff scope. |
optional |
The stage is skipped, not failed, when its tool is not installed (exit code 127). |
The diff-scope and secret-scan (gitleaks) stages always run in addition.
Stages run in the sandbox without network access; dependencies come
read-only from the repository checkout (for example its node_modules).
Without a verification.yaml, stages are chosen from the files at the base
commit, for every language found; a repository with several gets the stages
of each. The toolchains are in the sandbox image (boundedcode sandbox build; boundedcode setup rebuilds an image built from an older
definition).
| Language | Detected by (repository root) | Stages | Dependencies (offline) |
|---|---|---|---|
| Go | go.mod |
gofmt, go-build, go-vet, go-test, golangci-lint (full gate, optional) |
the host's module cache, or the module's own vendor/ when it has vendor/modules.txt |
| JavaScript/TypeScript | package.json, tsconfig.json |
tsc, npm-lint, npm-test, npm-build (full gate), each when the project declares it |
the checkout's node_modules |
| Python | Python test files (test_*.py, *_test.py, conftest.py) |
python-test: pytest, or unittest when the project does not use pytest |
the checkout's .venv or venv |
| Rust | Cargo.toml |
cargo-build, cargo-test |
the host's Cargo registry (~/.cargo/registry, ~/.cargo/git) |
| Java (Maven) | pom.xml |
maven-compile, maven-test |
the host's ~/.m2/repository |
| Java/Kotlin (Gradle) | build.gradle(.kts), settings.gradle(.kts) |
gradle-compile, gradle-test (with ./gradlew when present) |
the host's ~/.gradle caches and wrapper distributions |
| C/C++ | CMakeLists.txt, meson.build, configure.ac, or a Makefile with C/C++ sources |
cmake-build + ctest, meson-build + meson-test, autotools-build + make-check, or make-build |
none (system libraries must be in the image) |
| Ruby | Gemfile, Rakefile, *.gemspec |
ruby-test: RSpec, rake test, or the test/ files with minitest |
the checkout's vendor/bundle (bundle config set --local path vendor/bundle) |
| PHP | composer.json |
php-test: PHPUnit or Pest |
the checkout's vendor/ (composer install) |
| Terraform, Helm | *.tf, Chart.yaml |
terraform-fmt, helm-lint (optional) |
— |
| Any other | a Makefile with a test or check target |
make-test |
— |
Notes:
- A test stage whose dependencies are not installed fails and says how to install them; skipping it would pass a change with no tests run. The same goes for a tool missing from an outdated sandbox image.
- Java builds use JDK 11, 17 or 21: the one the Gradle wrapper version runs
on, or the Java level the
pom.xmldeclares (8 and earlier build with 11). - A Rust toolchain pinned in
rust-toolchain.tomlthat is not in the image is replaced by the image's (installing it would need the network). - A Python venv created from a self-contained interpreter (uv, pyenv, conda)
is used with that interpreter, mounted read-only. One created from the
system Python works when its version matches the image's (3.13);
otherwise recreate it with
uv venv --python 3.X. The tree under test always comes first on the import path, ahead of any installed copy of the project. - Only Go has formatter and linter stages: a newer formatter or lint rule would fail the untouched base of other projects.
- When nothing applies, verification reports a skipped
testsstage saying that no test runner was found, rather than passing silently. - Behavioural evidence (a changed test that fails on the base and passes on
the change) compares failures per test for Go, pytest, unittest, Cargo,
Maven Surefire, Gradle, minitest, RSpec, PHPUnit, CTest and Meson, and
per stage otherwise. Rust unit tests inside a source file (
#[cfg(test)]) cannot be evidence: the file holds the code under test too.
Workspace membership is stored in the state database, not in config files:
boundedcode workspace add PATH [--name NAME]
boundedcode workspace disable REPO # excluded from new tasks, indexing and queries
boundedcode workspace enable REPO
boundedcode workspace remove REPO # only if no task ever used it
boundedcode workspace show # lists disabled repositories, markeddisable is the reversible option. The repository keeps its id, index and
task history, and tasks that already have a worktree in it keep running.
remove is refused once any task has a worktree in the repository, even a
finished task, because task history refers to the repository record. Use
disable for those.