DocsReference
.settings.toml
ctx.toml tunes how the ctx supervisor behaves; .zirv/.settings.toml is a
separate, zirv-wide file that only answers yes/no questions about what zirv
may use. The rule of thumb: if the question is "yes/no, may zirv use this
thing", it goes in .settings.toml; anything else goes in ctx.toml. Today
that means one section, per-adapter enable/disable:
# .zirv/.settings.toml
[agents.codex]
enabled = falseLayered the same way as ctx.toml -- ~/.zirv/.settings.toml, then
<repo>/.zirv/.settings.toml, then ZIRV_AGENT_<NAME>_ENABLED (a boolean) --
but folded per agent rather than deep-merged:
final(name) = env(name) if set
else home(name).unwrap_or(true) && repo(name).unwrap_or(true)Every known adapter defaults to enabled. The environment is the operator, in
both directions: ZIRV_AGENT_CODEX_ENABLED=true re-enables an agent a repo
disabled, and =false disables one nothing else touched. A repository can
only narrow what it inherited -- enabled = true in a repo's own
.settings.toml is a silent no-op, since there is nothing there for a repo to
refuse. Disabling an agent is checked before that adapter's own readiness, so
--agent codex with codex disabled reports the disable, not codex's own
ready() outcome. zirv ctx status lists every known adapter, whether it is
enabled, and (when not) which file or variable disabled it. A malformed
repo .settings.toml never falls back to a fully permissive gate: zirv ctx optimize and the Stop hook both fall back to the operator's own layers only
(home file, then environment) if the full config cannot be loaded, so a broken
repo file can narrow what an operator already disabled but can never revive it.
A repo disable can also never pick an agent on the operator's behalf: if a
repo-only .settings.toml disables the agent that would otherwise have been
the default (no --agent, no agent = configured, and that adapter is the
first enabled-and-ready one in registry order), zirv refuses rather than
silently falling back to a different, still-enabled adapter -- naming both
the disabled agent and the one it would have picked, and how to choose
explicitly (--agent, agent = in your own ~/.zirv/ctx.toml, or the
ZIRV_CTX_AGENT environment variable — the repo's .zirv/ctx.toml cannot set
agent; it is a forbidden repo key).
Narrowing which agent is possible is a repo's call; narrowing which one you
actually get is not.
A harness you have not installed is a different matter. That is a fact
about your own machine, not something a checked-out repository can assert, so
the same fallback -- and only the fallback: an explicit --agent, an
argv-detected harness, and a configured agent = all still resolve exactly
as before, missing binary and all -- skips a candidate whose program is
confidently absent, and moves on to the next enabled, ready, installed one.
Only a confident absence removes a candidate: a probe that cannot decide
(an unreadable PATH entry, or an install root zirv does not know about)
keeps it, so nothing is ever lost to a guess -- and an agent_bin you
configured yourself is never checked at all, since you have already named the
program (it may not even be a path, as a wrapper command is not).
When presence rather than
registry order decided the answer, zirv says so: zirv ctx chat prints a
zirv ▸ line naming the harness it chose, the one it did not find, and how
to pin the choice yourself, and zirv ctx status's chat: line carries the
same fact as a standing one. That announcement rides [chrome] events, which
a repository cannot turn off ([chrome] banner, which it can, is not relied
on for it); --quiet/ZIRV_CTX_QUIET still silences it, because that is the
operator's own call.
Presence gates the choice, never the reading. Naming which adapter's
transcript to parse (the Stop hook's screening, zirv ctx score,
zirv ctx handoff), which account a usage readout belongs to, or which
program you handed zirv ctx wrap -- ... yourself never consults it: a
transcript written by claude is claude's whether or not claude is on that
process's PATH, and a hook subprocess routinely inherits a reduced one.
Only the question "which harness should zirv start for you" asks whether the
answer exists on the machine, so pure passthrough stays pure and screening
keeps working where the binary is real but invisible to a PATH walk.
Flags are not a program, though. zirv ctx exec -- --model x hands over
nothing to pass through: zirv still builds that launch from the harness's own
program and appends your flags to it, so it is the choosing case too, and it
chooses a harness this machine actually has. zirv ctx wrap -- ... reads the
same argv the other way, because there what you wrote is what gets spawned.
The missing binary is reported before the waiting, not after. Once
zirv ctx exec/zirv ctx agent and zirv ctx loop have resolved the harness
they are about to launch, they check that its program exists before engaging
pacing, usage polling, or the macOS Keychain read those drag in. On a machine
with no harness installed, zirv ctx agent claude "say hi" used to warn about
Keychain access for a harness you do not have, sit out the blind-mode safety
delay ([pace] blind_delay_secs, 60s by default) because that harness has no
usage source, and only then tell you claude is not a program; now the
"program not found" answer arrives immediately, in the same words the spawn
itself would have used -- and those words name all three ways out, the same
three the "nothing is installed" error below gives, because a harness missing
from PATH is very often one you have installed somewhere agent_bin should
be pointed at rather than one you need to install at all. This only
ever makes a failure faster -- it never picks a different harness, so a
harness you named with --agent or agent = still fails under its own name;
only a confident absence refuses, so an undecidable probe launches exactly
as before; an agent_bin you configured yourself is not probed at all; and
a program you supplied yourself (zirv ctx exec -- <command>, zirv ctx wrap -- <command>) is never subject to it, because it is your program, not the
adapter's.
If nothing is installed, the error says so in as many
words and names the ways out (install one onto PATH, point agent_bin at
it in ~/.zirv/ctx.toml, or pass --agent), instead of only reporting the
first harness's own "program not found"; when some candidates were merely
disabled, it names the ones that are missing and claims nothing about the
rest.