Operating multiple CLIs and routing models¶
This runbook is for an operator who has more than one coding-agent CLI or more than one model tier. It covers the safe path from adapter setup to a spawn, and the controls that keep expensive models and provider limits available for the work that needs them. For adapter internals and the executable conformance matrix, see RUNTIMES.md.
1. Choose the read-only or writer preset¶
dacli ships nine presets. Four providers have a read-only/writer pair; the
unpaired generic-exec preset is the base for an operator-defined adapter.
| Provider | Read-only preset | Read-write preset | Boundary |
|---|---|---|---|
| Claude Code | claude-code |
claude-code-rw |
The reader allowlists Read/Grep/Glob/LS and dacli reporting. The writer adds Edit/Write plus scoped git and dacli shell commands. |
| Codex | codex |
codex-rw |
The reader uses --sandbox read-only; the writer uses workspace-write and retains the read-only override for ro spawns. |
| Gemini CLI | gemini |
gemini-rw |
The reader uses approval mode plan; the writer uses auto_edit, not unrestricted yolo mode. |
| GitHub Copilot CLI | copilot |
copilot-rw |
The reader denies write and shell; the writer grants write and only scoped git/dacli shell commands. |
| Any executable | — | generic-exec |
No binary, sandbox, or structured-output contract is assumed. Configure and verify the executable yourself; it is not a read-only safety claim. |
The plain provider preset is intended for reviewers and auditors. Use the
-rw counterpart for implementers. A writer preset can still serve an ro
spawn because dacli applies its read-only sandbox arguments for that grant.
2. Set up and verify adapters¶
Authenticate each vendor CLI using its own login flow first. dacli records the binary, flags, and a narrow environment allowlist; it does not store provider credentials.
Codex is a complete example:
dacli runtime add codex --preset codex
dacli runtime add codex-rw --preset codex-rw
dacli runtime list
dacli runtime doctor
runtime doctor uses local version/help and sandbox probes wherever those are
sufficient. Codex exec adapters additionally send one trivial, versioned JSONL
startup request and stop at turn.started; they do not wait for the model
answer. A compatible result is cached against the exact binary, adapter,
grant, model, and strategy version. Do not treat a declared sandbox as
verified: an ro spawn requires a current verified probe for the installed
binary and exact flags.
The equivalent setup for the other shipped adapters is concise:
dacli runtime add claude --preset claude-code
dacli runtime add claude-rw --preset claude-code-rw
dacli runtime add gemini --preset gemini
dacli runtime add gemini-rw --preset gemini-rw
dacli runtime add copilot --preset copilot
dacli runtime add copilot-rw --preset copilot-rw
dacli runtime add local-agent --preset generic-exec --binary my-agent
dacli runtime doctor
For generic-exec, add the executable's real prompt, model, sandbox, and output
flags with runtime add overrides. Start from the vendor's documented
non-interactive invocation, then inspect the result with runtime list and
runtime doctor; dacli deliberately makes no vendor safety claim for it.
Each runtime declares an opaque harness family. The shipped read/write pairs
share codex, claude, gemini, or copilot; custom read/write adapters must
share an operator-chosen value via --harness.
2.5 Pin one harness or authorize hybrid routing¶
Harness selection and model selection answer different questions. The harness chooses which coding-agent CLI families may execute; the model router then chooses the economical capable role inside that set. Pin normal runs:
dacli start --project app --profile loop --harness codex --dry-run
dacli start --project app --profile loop --harness codex --configure
The pin applies to implementation, continuous-improvement review, and runtime fallback. An incompatible explicit role refuses before a child starts. If cross-harness diversity is intentional, authorize every family and say hybrid:
dacli start --project app --profile loop \
--harness codex --harness claude --hybrid --configure
Never use hybrid merely because another CLI happens to be installed. It is an explicit policy choice and every allowed adapter still requires authentication, doctor evidence, compatible roles, and available quota.
3. Define provider-neutral roles and models¶
A role binds work policy to a runtime and model. The model profile describes selection facts without vendor names:
dacli role add codex-reviewer --kind reviewer --grant ro \
--runtime codex --model-id gpt-5.4-mini --cost-tier 2 \
--max-task-points 3 --context-limit 200000 --capability-tag code
dacli role add codex-fixer --kind implementer --grant rw \
--runtime codex-rw --model-id gpt-5.4 --cost-tier 5 \
--max-task-points 8 --context-limit 200000 --capability-tag code
model_id is passed through the runtime's model flag. cost_tier is an
ordering rank, not a price; lower is cheaper. max_task_points is the largest
task estimate the role may take, context_limit records its declared context
capacity, and capability_tags state requirements such as code or vision.
After estimating a task, ask dacli for the cheapest eligible role whose
capacity covers expected points (Te):
dacli task estimate 415 --estimate 2,3,5
dacli team assign 415
The output names the inferred role kind, runtime, model, cost tier, capacity, context, capabilities, and the proposed spawn command. If every cap is too small, dacli refuses and tells you to decompose the task or add a heavier role.
Treat the recommendation as a cost-and-capacity answer, not a risk judgment. Override it when blast radius, unfamiliar code, privacy, or required reasoning is not represented by the profile:
# Choose a different vetted role (and its runtime/model policy).
dacli spawn --task 415 --role senior-fixer --advise
# One-run model override while retaining the role and runtime.
dacli spawn --task 415 --role codex-fixer --model gpt-5.4 --advise
# Explicit runtime override: dacli will not substitute a fallback for it.
dacli spawn --task 415 --role codex-fixer --runtime codex-rw --advise
--advise prints calibrated sizing and taint status and exits without launching
or billing a run. Re-run without it only after the preview is acceptable.
4. Preflight, then spawn¶
For Codex, verify the exact role/runtime/grant combination before launch:
dacli preflight --role codex-reviewer --runtime codex --grant ro
dacli spawn --task 415 --role codex-reviewer --grant ro --worktree --detach
dacli preflight --role codex-fixer --runtime codex-rw --grant rw
dacli spawn --task 415 --role codex-fixer --grant rw \
--worktree --claim docs --detach --max-tokens 40000
Equivalent provider commands differ only by the configured role (or runtime):
dacli preflight --role claude-fixer && dacli spawn --task 415 --role claude-fixer --worktree --detach
dacli preflight --role gemini-reviewer && dacli spawn --task 415 --role gemini-reviewer --worktree --detach
dacli preflight --role copilot-fixer && dacli spawn --task 415 --role copilot-fixer --worktree --detach
dacli preflight --role local-agent && dacli spawn --task 415 --role local-agent --worktree --detach
Preflight reports every mismatch it can observe: grant versus write capability,
the running dacli binary versus the runtime allowlist, and tools named by the
role prompt versus the adapter. --cooperative is an explicit weakening for an
adapter whose sandbox dacli cannot enforce; use it only when that trust decision
is intentional.
5. Provider limits, breakers, and explicit fallback¶
dacli classifies provider failures into rate_limit, quota_exhausted,
authentication, unavailable, permanent_input, policy_refusal, and
unknown. Rate limits and temporary unavailability are retryable. Rate limits,
quota exhaustion, and unavailability may open a fallback. Authentication,
invalid input, an unknown failure, and a policy refusal do not silently move
work elsewhere.
For a rate limit or temporary outage, the cooldown uses the provider's reset
interval when reported, otherwise a bounded retry-policy delay. A fallbackable
failure opens a persisted circuit breaker under
.dacli/runs/runtime-cooldowns/, so restarting the loop does not hammer the
same provider again. Operator output and transitions.log record the same line:
source=codex-rw destination=claude-rw reason=rate limit cooldown=1h0m0s
Fallbacks are opt-in and ordered. Add role names to the source role file's
frontmatter; dacli persists the field as fallback_to:
fallback_to:
- claude-fixer
- gemini-fixer
Only this named chain is considered. Each destination must preserve the source
grant and every required capability tag, its runtime circuit must be closed,
and its harness must be allowed by the active single/hybrid policy. dacli never
guesses a provider. An explicit --runtime is also never
substituted: remove that override and route through the role if fallback is
desired. If no eligible destination remains, spawn returns exit 3 with the
source runtime's remaining cooldown.
6. Conserve limits and cost¶
- Run
runtime doctorandpreflightbefore spawning; both are observable, credential-free checks that prevent paid runs from discovering flag drift. - Use
team assignand--advise, then keep routine work on the lowest cost tier whose capacity and capabilities fit. Override upward for consequence, not habit. - Give each launch
--max-tokens; use loop-level--window-tokenswith--budget-windowfor a rolling provider budget.dacli calibrateimproves estimates from recorded usage. - Prefer read-only roles for review and narrow claims for writers. Decompose work beyond a role's capacity instead of spending a large-context model on a task that is too broad.
- Do not retry a refusal or bypass an open circuit with repeated explicit runtime spawns. Wait for its printed cooldown or deliberately select another verified role.
7. Troubleshoot from observable state¶
dacli runtime list
dacli runtime doctor
dacli preflight --role codex-fixer
dacli team assign 415
dacli agents --tail
dacli logs <run-id> --tail 80
dacli runs show <run-id>
dacli doctor
Interpret command exits consistently:
| Exit | Meaning | Operator action |
|---|---|---|
| 0 | The command completed. | Continue; for --advise, remember that no child was launched. |
| 1 | Runtime or operational failure. | Read the diagnostic and transcript; repair the install, authentication, or provider condition. |
| 2 | Usage error. | Correct the command or unknown flag. |
| 3 | Policy refusal. | Stop retrying. Follow the named remedy: verify the sandbox, choose an eligible role, decompose, or wait for the circuit. |
| 4 | Object not found. | Check the task, role, runtime, or run reference. |
Common cases are intentionally visible: “binary not on PATH” points to
runtime doctor; an unknown or failed read-only probe refuses an ro spawn;
an rw role on a reader preset fails preflight; a model flag missing from the
installed CLI appears as doctor flag drift; and a provider transition is both
printed and retained beside the cooldown records. Fix the stated condition
rather than changing grants or weakening assertions merely to obtain exit 0.