zirvDocs

DocsReference

.settings.toml

README.md, lines 6150–6260View source

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:

toml
# .zirv/.settings.toml
[agents.codex]
enabled = false

Layered 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:

text
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.