Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/
Ephemeral Board of AI Advisors that only convene when you need them.
Put one question to a panel of independent models, in parallel, and read where they disagree.
$ roundtable "Is replacing our message queue with direct calls a mistake?"
==========================================================================
Skeptic (some-vendor/some-model) 2.1s
==========================================================================
...
9/9 lanes answered in 51.2s
transcript: ~/.local/share/flatline-roundtable/transcripts/20260825-163840.jsonOne model asked one question gives you one prior. Ask eight models from eight different training lineages and the disagreement is the product: where independently-trained models land in the same place, that convergence is evidence; where they split, that is where your thinking is underdetermined.
A panel of one model wearing eight hats gives you neither — shared priors produce convergence that looks like corroboration and is not.
This replaced a design where each advisor was a long-lived agent process with its own identity, message-bus membership, and a tool it had to call to publish its answer. Everything that went wrong with that was transport, not models:
- An abandoned run kept billing for 54 hours — a backgrounded agent has no reason to stop.
- A nine-advisor review delivered four answers. The other five produced correct responses that died in log files, because those models never called the publish tool. The harness logged the text and discarded it.
- Advisor names that resolved ambiguously notified nobody, silently.
None of that is possible here:
- No long-lived processes. A lane is one HTTP request or one short-lived subprocess. There is nothing left running to bill.
- No delivery step. The answer arrives in the response or the lane failed loudly. Nothing can "succeed" into a place nobody reads.
- Partial delivery is a failure. If any active lane is silent, the exit code is non-zero. A quiet advisor is the bug, not a rounding error.
Requires Python 3 with PyYAML, and pass if
any lane needs an API key.
$ git clone https://github.com/CryptoJones/FlatlineRoundtable
$ cd FlatlineRoundtable
$ mkdir -p ~/.config/flatline-roundtable
$ cp FlatlineRoundtable.yaml.example ~/.config/flatline-roundtable/FlatlineRoundtable.yaml
$ $EDITOR ~/.config/flatline-roundtable/FlatlineRoundtable.yaml
$ ./roundtable --list # shows the roster; makes no network callsOptionally ./install.sh to expose it as a Claude Code skill.
No key ever appears in a config file, in this repo, or in a process list.
- A lane names a
passentry (key_entry: openrouter/agent/skeptic), never a key. - Keys are read once, before fan-out, and held in memory only. Never in
argv, which is world-readable viaps. - Keys never reach the transcript or
--jsonoutput. - A missing entry fails that lane loudly and names the fix. It never silently falls back to an unauthenticated call.
- Your real config lives at
~/.config/flatline-roundtable/, outside the repo, because.gitignoreis not a security boundary —git add -f, stashes, and editor backups all defeat it.
harness decides cost, not model.
An http lane bills per token. cli and acp lanes drive a vendor's own binary
against an existing subscription and cost nothing extra.
Rewriting a cli lane as an http lane pointed at the same vendor's API
silently moves it onto metered credits. So does leaving an API key in your
shell: these CLIs prefer an env key over the OAuth session. FlatlineRoundtable
scrubs ANTHROPIC_API_KEY, OPENAI_API_KEY and friends from every CLI lane's
environment for that reason.
Prices come from the gateway's own table (cached for a day), so they do not go
stale the way a hand-maintained number does; price_per_mtok overrides it for
vendors with no price endpoint. --max-spend and budget_usd are checked
before dispatch against a worst-case estimate, so an overrun is prevented
rather than reported.
roundtable "the brief" # every active lane, in parallel
cat brief.md | roundtable - # long briefs on stdin
roundtable --lanes Skeptic,Chair "..." # a subset
roundtable --list # roster + route; no network calls
roundtable --json # structured output
roundtable --config PATH
roundtable --max-spend 0.50 # refuses BEFORE dispatch if the estimate exceeds it
roundtable --diff # report only where the lanes disagree
roundtable --no-transcript--diff asks lanes to report AGREED / SPLIT / LONE CLAIMS across the others,
because reading N full answers does not scale and disagreement is the product.
It is opt-in and it is not neutral — a synthesizer is one model with its own
priors deciding what counts as a disagreement, so the raw answers still go to
the transcript. That is also why it uses two readers by default, picked from
different vendors: with a single reading you cannot tell a real split from that
model's taste in splits. Where the two place the same lane differently,
roundtable says so under THE READERS DISAGREE — that is the reading doing the
work rather than the evidence, and a signal to go read that lane's raw answer.
The comparison is mechanical on purpose. Handing it to a third model would just
move the problem one level up. Set synthesizers: 1 to go back to a single
reader, or name a specific one with synthesizer: <lane>. It prefers free
lanes, so the convenience does not quietly cost money.
After a run, any lanes that turn out not to be independent are named:
LANES THAT ARE NOT INDEPENDENT
Twin1, Twin2 — answered from claude / haiku
Agreement between these is an echo, not convergence.
Two lanes collide when a gateway served them the same model, or when they
declare the same lineage:. It is never fatal — a deliberate duplicate is a
legitimate thing to want — but it must not be silent, because it is invisible in
the answers themselves and it falsifies the one claim the tool makes.
Two config files ship with the repo. FlatlineRoundtable.yaml.example is the
annotated template — every option, with the reasoning for each. examples/full-roster.yaml
is a real thirteen-lane roster, kept because a tuned config is mostly numbers
you had to measure, and those are worth reading before you pick your own.
Every run writes a transcript to
~/.local/share/flatline-roundtable/transcripts/, because answers that exist
only in a terminal scrollback are answers waiting to be lost.
$ python3 -m unittest discover -s tests25 tests against a stub HTTP server and fake CLI binaries — no vendor is contacted, nothing is spent, no credential is needed. They cover the behaviours that silently cost money or leak processes: the env scrub, the process-group kill, the missing-secret abort, and partial delivery failing the run.
- Three harnesses:
http(any OpenAI-compatible endpoint),cli(one-shot subscription CLIs), andacp(JSON-RPC-over-stdio agents, driven for exactly one turn). - Lanes have no tools and no file access. This is "read this and tell me what you think," not "go investigate the repo." Put the material in the brief.
- Lanes never see each other's answers. That is the point; it is also why they cannot build on one another.
- A weak lane is worse than an absent one — it still gets counted. Prune the
roster on quality, and record known defects in each lane's
notes.