Skip to content

Why config is layered ​

Agent, model and effort each resolve independently through six layers, the most specific winning, so three people can control the same knob at three scopes without coordinating. One setting deliberately breaks the pattern: a boundary intersects across the scopes instead of overriding (below).

Commands: Configure your defaults. Record: decision 0005.

The harness — which process drives a preset's runs, pi or opencode — rides the three scope layers only: config set me --harness.<agent> opencode moves your own runs of that preset and nobody else's, a channel's word moves its runs, and the deployment's top-level harness: block is the defaults layer. There is no directive for it and a thread does not carry it; a run that is already going keeps the harness it started on (harness.md item 8).

When no layer above the installation defaults names the agent, the request router picks the preset before the defaults do — on unless the deployment sets routing: { auto: false }; a directive, the sticky preset, a user agent or a channel agent each skip it (routing-and-config item 21).

The thread layer follows the conversation ​

The thread layer is derived, not set. A follow-up with no directive keeps what the earlier thread used. The dispatcher resolves that context from available channel history and durable thread records.

The saved conversation and agent working logs are separate from the resolved setting. A thread outlives its runs explains those records.

Agent and model resolve independently ​

config set channel --agent review and config set channel --models.coding openai/gpt-5 are two decisions through the same six layers. A channel can pin the review model without forcing every other agent onto it.

Effort is a layer, not a model detail ​

Effort (low through max) rides the same ladder as the model: request directive, per-agent override at any scope, installation settings extending the shipped profile.

Wall-clock time is an agent's real budget, and effort decides how much of each turn goes to thinking. A channel can want cheap general answers and a high-effort review without faking it through two model configurations.

Boundaries intersect, they do not override ​

A budget:<minutes> directive is the caller's own boundary on one message: it narrows that run's wall clock below the agent's budget and never widens it, the card says what it did (budget 30 min (budget directive; preset asks 120), or that it narrowed nothing), and unlike the other three directives it is never sticky — a thread that wants a lower budget on every turn sets a user boundary.

A boundary (maxMinutes, maxIdentity, machines) is the one scope setting that does not ride the ladder. The installation's, the channel's and the user's boundaries are intersected — the smallest budget, the lowest identity, the machine classes every one of them allows — because a cap that a more specific scope could replace would not be a cap: anyone could widen it with config set me.

Intersection is what makes a user boundary safe to leave open to everyone: it can tighten what the channel allows and never loosen it. The preset's own profile is the starting point, the boundaries narrow it, and the result — the effective profile — is what the run is provisioned with. A budget above a cap is clipped and the card says so; an identity or a machine class above a cap is refused before anything starts, because a preset that needs to push cannot do its job with a read token.

A boundary never grants. Who may run a preset stays the policy table's question (Authorization); how much a run in a scope may have is the boundary's. The two never read each other's fields.

The boundary carries one field that is not a run's cap: confirm, the front door's. It names the first blast-radius class a command the router bound from a plain sentence is handed back at instead of run — write, the built-in, or destructive — and it intersects the same way, toward caution, so the installation's value is a floor. A run never sees it: a scope that sets only confirm caps nothing, and the model's configuration block is the same with or without it.

The gate checks the resolved agent ​

The ladder picks a default; it is not a security boundary. restrict.agents is checked after all six layers produce a final agent, against that agent, on every message.

config set me --agent coding therefore always succeeds as a write. The check happens at run time (Authorization, decision 0007).

Custom instructions are not a seventh layer ​

config instructions me and config instructions channel are scoped like config, but they are free text folded into the system prompt as a labelled advisory block. They never change which agent, model or effort resolves, and never affect a gate.

The two systems stay apart on purpose: a phrasing preference cannot reroute which agent runs, and authorization never reads prompt text. Where resolution sits in the pipeline: How a request flows.