Add a Provider
Build a Quorum adapter for a provider on this machine, by measuring it rather than by reading about it.
The rule this skill exists to enforce
Do not write an adapter from documentation. Write it from measurements.
Any model can produce a plausible-looking adapter for any CLI. The result is a wrapper whose flags were guessed, which fails silently the first time it matters — and silent failure is the exact thing Quorum's whole design is built to prevent. An adapter is worth having only if someone ran the commands and wrote down what came back.
So the order is fixed: probe first, write second. If you cannot run the provider — it isn't installed, there's no key, the machine is wrong — say so and stop. Do not produce an adapter "for them to test later." An untested adapter that looks tested is worse than none.
If the provider isn't installed
Stop, but be useful about it. Look it up in docs/providers.md and give the user the exact install and auth commands for their platform. Then say plainly that you'll run the probes once it's there.
Do not install it yourself. Two reasons, and neither is squeamishness:
- Vendor installers are frequently
curl … | bash, which executes remote code and modifies the machine outside the repo. That is the user's decision to make knowingly, not a step to slip inside a task about writing an adapter. - Authentication almost always needs a browser and binds the user's paid account. You cannot complete it, and you should not try — a half-authenticated CLI fails in ways that look like provider bugs.
Installing is a one-line command for them. Guessing at an adapter for software that isn't there is unrecoverable, because the guess looks like knowledge.
Step 0 — Establish the invocation surface
Ask the user, or find out, what kind of provider this is. The three shapes need different adapters:
| Shape | Examples | Adapter reaches it via |
|---|---|---|
| Agentic CLI | Gemini CLI, Aider, Amp, Cursor CLI | the CLI's own headless/exec mode |
| HTTP endpoint, no CLI | Z.AI, OpenRouter, a hosted API | curl |
| Local server | MLX, Ollama, LM Studio, llama.cpp, vLLM | curl to localhost; see the porting guides |
docs/providers.md lists the ones already known, including which
binary each actually installs as. Then find the real entry point. Do not assume a binary is named after its vendor —
GLM has no glm command at all. command -v returning NOT FOUND proves nothing; it is
already responsible for one panel falsely reporting a working provider as missing.
Establish the invocation the vendor actually documents, then confirm it by running it.
Step 0.5 — Read the whole flag surface first
Before probing, dump the complete help and read all of it:
<cli> --help
<cli> <subcommand> --help # exec, chat, run — wherever the real work happens
Not the flags you expect to need — all of them. Copilot alone exposes around sixty, and
an adapter written from the obvious ones missed --available-tools, --excluded-tools,
--output-format, and --secret-env-vars, any of which changes how the adapter should be
built.
Four things to look for specifically, because they determine whether the adapter can exist at all:
| Looking for | Typical spelling |
|---|---|
| Headless / one-shot | -p, exec, --print, --json, --output-format |
| Don't ask the user | --no-ask-user, --yes, --non-interactive, --autopilot |
| Sandbox / permission | --sandbox, --plan, --allow-tool, --deny-tool, --mode |
| Working directory | -C, --cwd, --add-dir |
Note the variadic ones (<FILE>...) as you go. A variadic flag will swallow a trailing
prompt and hang the run — that is probe 2's most common cause and it is visible in --help
before it costs you a timeout.
Then snapshot it, so drift is detectable later:
# These are Quorum's OWN commands, and they exist only if scripts/install.sh has run.
# `/plugin marketplace add` installs the plugin WITHOUT running it, so on that path they are
# absent — and absence here does not fail loudly. Measured against the live API with
# quorum-sanitize missing: CODE=200, stop_reason=end_turn, TEXT="" — a real answer reported
# as `empty`, "the model had nothing to say". A missing quorum-claude-on in a delegate block
# is worse: the provider never runs and the diff is clean, which reads as "no changes needed".
#
# Refuse instead. A missing prerequisite must never be renderable as an ordinary result.
for _q_need in quorum-flags; do
command -v "$_q_need" >/dev/null 2>&1 || {
echo "status: error — $_q_need is not on PATH."
echo "Run scripts/install.sh from the Quorum repo, then retry."
exit 1
}
done
quorum-flags --capture # writes reference/flags/<provider>.txt
Step 1 — Run the six probes
Full detail, including what each result means: reference/probe-checklist.md.
Record the measurement for each — exit code, byte count, the actual text. Not "works" or "seems fine."
| # | Question | Fails how |
|---|---|---|
| 1 | Does it answer at all? | Wrong entry point, no auth |
| 2 | Does it exit without a TTY? | Hangs to the timeout (exit 124) — missing a "don't ask" flag |
| 3 | Is read-only harness-enforced? | It writes the file anyway — the mode was advisory |
| 4 | What does a broken call look like? | You cannot report failures you cannot recognise |
| 5 | Is a broken call distinguishable from a good one? | Exit code, empty output, or error text — at least one must work |
| 6 | Images, and how is the prompt passed? | Optional; skip if the provider has no vision |
Probe 4 is the one that matters. Everything else confirms the happy path, which anyone can get right. Deliberately break the call — wrong flag, wrong model id, wrong directory — and write down exactly what comes back.
Measure unpiped. provider ... | tail reports tail's exit status, which is how a
failing call gets recorded as a working one. Redirect to separate files:
OUT=$(mktemp); ERR=$(mktemp)
timeout 120 <invocation> >"$OUT" 2>"$ERR"; RC=$?
echo "rc=$RC stdout=$(wc -c <"$OUT") stderr=$(wc -c <"$ERR")"
Keep stdout and stderr apart, including now. Some CLIs print usage errors to stderr and
exit non-zero — a clean, detectable failure. Capture with 2>&1 and that same failure
becomes non-empty output that looks exactly like an answer.
Step 2 — Choose the safety tier from probe 3, not from hope
This is the step where an adapter becomes trustworthy or merely optimistic.
Probe 3 passed (the harness refused the write) → the provider gets a real consult tier using that flag.
Probe 3 failed (the file appeared) → the mode is prompt-enforced. It is not read-only, and there is no consult tier for this provider. Say so in the adapter.
A scratch worktree does not rescue it. That was this repo's advice for a while, and it was wrong: a worktree gives reviewability and disposability, never containment — code inside one reaches the real checkout with a single
git rev-parseand shares its.git.
An adapter may only claim a tier it can enforce, and when it cannot, the guarantee is what has to go — not just the mechanism. Writing "read-only" over a mode that merely asks nicely is the one failure this repo cannot tolerate, because every downstream user trusts that word.
Same reasoning for verify (named-command allowlist, or a scratch worktree) and delegate (throwaway worktree on its own branch, always). See docs/safety-model.md.
Step 3 — Write the three files
Copy the templates, then fill them from your notes — never from memory of the docs.
agents/<name>-agent.md— fromtemplates/adapter.md.template. Frontmatter needsname, adescriptionsaying when to route here and what the provider is uniquely good for,tools: Bash, Read, Glob, Grep(neverWriteorEdit; the adapter reads attacker-influenceable text and must not be able to act on it), and a smallmodel:— an adapter is plumbing.probes/<name>.sh— fromtemplates/probe.sh.template. This is what makes the adapter re-checkable after a vendor update.probe_broken()encodes probe 4.A docs/field-notes.md entry — but only for genuine surprises. Anything that cost you more than one attempt to get right will cost the next person the same. Use the documented format, and mark anything you inferred rather than observed.
Step 4 — Prove it
quorum-verify <name> # does it work?
quorum-flags # do the flags it depends on still exist?
Those are the names scripts/install.sh puts on PATH, so they work from any directory. If
you cloned Quorum but have not run the installer, use ./scripts/quorum-verify from inside
the clone instead — but do not assume the clone is the working directory, because when this
skill runs the working directory is usually the user's own project.
It re-runs the mechanical probes against the live provider. If it does not pass, the adapter is not finished — do not report success. When it contradicts something you believed, the verifier is usually right; measure directly and correct the document. That has already happened once to this repo's own docs, and the correction is in the field notes.
Then test the routing end-to-end: dispatch a real question through the new agent and check
that a well-formed envelope comes back — status, diagnostics, delimiters.
Step 5 — Report what you actually established
State plainly which tiers are enforced and by what mechanism, which probes passed, and what you could not verify. A provider with no harness-level read-only mode does not get a consult tier — a worktree gives reviewability, not containment, so it cannot stand in for one. Report that the provider has no enforced read-only boundary and let the user decide.
Offer to contribute it back: CONTRIBUTING.md covers submitting an adapter, and a verified
one for a provider nobody has covered yet is the most useful PR this repo can receive.
Common shapes
- Local models (MLX, Ollama, LM Studio, llama.cpp, vLLM) — usually an
OpenAI-compatible server on localhost, so consult is a
curladapter. They typically have no agentic harness, which means no native read-only mode and no verify or delegate tier. That is fine — say so rather than inventing one. Start from docs/porting/openai-compatible.md. - Another vendor's coding CLI — closest to the built-in three. Find its headless flag, its "don't ask the user" flag, and its sandbox flag, in that order.
- A hosted API — mirror
agents/glm-agent.md. Capture both%{http_code}and the body, and classify on both. Some providers do return errors inside an HTTP 200, so the status alone is not enough — but do not invert that into "the status is useless": measured on z.ai, a bad model id gives 400 and a bad key 401, which discriminate cleanly. What carries no information iscurl's exit code, which is 0 for all of them.