Ask Three, Trust The Overlap
Take the answers you already collected — one prompt run through several models, or the same model run several times — and fold them into what they agree on: the agreed value (if enough concur), the full tally of who said what, and the dissent. A pure counting fold — same answers in, same verdict out, every run. Agreement is declared, never guessed: answers merge only after a conservative normalize (trim + collapse whitespace by default; --fold-case, --json opt in), and the threshold is declared too (strict majority default; --threshold N, --unanimous). If the top answer misses the needed count the verdict is no-quorum, never a quiet plurality; a tie yields no winner. The exit code is a gate — 0 on a clean quorum, 2 on no-quorum or tie — so it fails a pipeline when sources don't converge. Zero dependencies, no network, no API keys; runs in Node or the browser (window.quorum).
node quorum.js --answer gpt=42 --answer claude=42 --answer llama=43 # or: node quorum.js FILE.jsonl
test_quorum.js (10/10) + --selftest (22/22: consensus fold, folds-twice-identical, declared normalize, declared threshold, no-quorum/tie gates, verbatim-winner-or-null, exhaustive/disjoint agree+dissent, empty-answer-counts)
Zero dependencies, Node or browser, deterministic
quorum.js436 lineson GitHub →
#!/usr/bin/env node
/* SPDX-License-Identifier: MIT */
/* quorum.js — ask three, trust the overlap. A consensus fold over N answers.
*
* A tiny, dependency-free fold that takes the answers you already collected —
* one prompt run through several models, or the same model run several times —
* and decides what they AGREE on. It emits the agreed value (if enough of them
* concur), the full tally of who said what, and the dissent (who disagreed and
* with what). Runs identically in Node and in a browser (no DOM, no deps).
*
* [ {label, answer}, ... ] ──▶ quorum(answers, opts) ──▶ {
* verdict: "quorum" | "no-quorum" | "tie",
* value: <the agreed answer, or null>,
* agree: [labels that gave the winning value],
* dissent: [ {label, answer} that did not ],
* tally: [ {answer, count, labels} ... ] sorted high→low,
* n, threshold, needed
* }
*
* WHAT QUORUM IS, AND THE LINE IT WILL NOT CROSS (the honest ceiling — printed).
* Quorum is a COUNTING fold, not a truth oracle. It answers one question — "how
* many of these answers are the same, and is that enough?" — under a normalize
* rule and a threshold you declare. It does NOT — and this is stated on the tool
* itself, every run — know whether the agreed answer is CORRECT. N models can
* agree and all be wrong; a majority can be a shared blind spot. Concordance is
* not correctness. Quorum tells you where the answers converge and where they
* split; it never certifies the winner is right. (This is the same honesty the
* `grain` gift prints as "a smell, not a proof" and `scrub` as "a smoke alarm,
* not a vault".) Treat a quorum as "N sources independently landed here", and
* keep your judgment about whether here is correct.
*
* WHY THIS EXISTS AS A GIFT — AND WHY IT DOES NOT CALL THE MODELS.
* The seam it fills is: you ran a prompt through several models (or several
* times) and now hold a fistful of answers, and the cheap, deterministic,
* substrate-free thing to do is FOLD them into a consensus. Quorum is that fold.
* It deliberately does NOT run the models for you — that would make it depend on
* a network, an API, keys, a substrate, and its output would stop being a pure
* function of its input. Shipping the "call N models" step would be the exact
* rot the `weir` gift declines when it budgets in rows-not-tokens: substrate is
* where a gift goes to die. Quorum ships the SHAPE (the consensus fold), names
* the want (you wanted it to call the models), and declines it. You bring the
* answers; Quorum tells you what they agree on. Because the fold is pure, the
* same answers always yield the same verdict — auditable, replayable, offline.
*
* THE NORMALIZE RULE (declared, never guessed). Two answers "agree" iff they are
* equal AFTER normalization. The default normalize is conservative: trim outer
* whitespace and collapse internal runs of whitespace to one space — nothing
* else, because a fold that silently lowercased or stripped punctuation could
* merge answers that a caller meant to keep distinct. Stronger normalizers are
* opt-in and declared: `--fold-case` (case-insensitive), `--json` (parse each
* answer as JSON and compare by a canonical key-sorted re-serialization, so
* {"a":1,"b":2} and {"b":2,"a":1} agree). A normalize you did not ask for is a
* silent merge, so Quorum asks for it.
*
* THE THRESHOLD (declared, never guessed). A "quorum" is reached when the top
* answer's count is >= the needed count. The default threshold is a strict
* majority: needed = floor(n/2) + 1. `--threshold N` sets an absolute count
* (needed = N). `--unanimous` requires all n. If the top count does not reach
* needed, the verdict is "no-quorum" (nobody won) — never a quiet plurality
* passed off as agreement. If two or more answers tie for the top count AND
* that count reaches needed, the verdict is "tie" and value is null: a tie is
* not a winner, and Quorum will not pick one for you.
*
* STRUCTURAL PROMISES (so the fold cannot betray its own purpose):
* 1. It is pure and deterministic. quorum(a) === quorum(a): same answers,
* same options, same verdict, every run — no clock, no randomness, no I/O
* in the core. The tally is sorted by (count desc, then first-appearance
* order) so the order is a fixed function of the input, never insertion
* -hash-dependent. --selftest proves it.
* 2. It never invents an answer. `value` is always one of the input answers
* verbatim (the first-seen raw form of the winning normalized group), or
* null. A consensus fold that emitted a value nobody said would be broken.
* 3. Every input answer is accounted for. agree ∪ dissent = all labels, with
* no label in both. Exhaustiveness: no answer is silently dropped from the
* count. (An empty answer is a real answer — it counts.)
*
* INPUT FORMS. On the CLI, answers come from a JSONL file / stdin (one
* {"label":..,"answer":..} per line) OR from repeated --answer "label=text"
* flags OR from plain lines of a file with --lines (label = 1-based line no.).
* As a library, pass an array of {label, answer} (or bare strings — the index
* becomes the label). A missing/blank answer is kept as the empty string, never
* dropped (promise 3).
*/
'use strict';
/* ---- the normalize rule ---------------------------------------------------
* Returns the normalized comparison key for an answer under the declared opts.
* Default: trim + collapse internal whitespace. Opt-in: fold-case, json-canon.
*/
function normalizeKey(answer, opts) {
opts = opts || {};
let s = String(answer == null ? '' : answer);
if (opts.json) {
// Canonical JSON: parse, sort object keys recursively, re-serialize.
// A parse failure is NOT silently treated as agreement — it falls back to
// the raw-string path below, so two unparseable answers agree only if their
// text agrees. (Declared in the ceiling: --json compares parseable answers
// by structure; unparseable ones by text.)
try {
const parsed = JSON.parse(s);
s = canonicalJson(parsed);
return opts.foldCase ? s.toLowerCase() : s;
} catch (_e) {
// fall through to text normalize
}
}
s = s.trim().replace(/\s+/g, ' ');
if (opts.foldCase) s = s.toLowerCase();
return s;
}
/* Deterministic canonical JSON: object keys sorted, arrays kept in order. */
function canonicalJson(v) {
if (v === null || typeof v !== 'object') return JSON.stringify(v);
if (Array.isArray(v)) return '[' + v.map(canonicalJson).join(',') + ']';
const keys = Object.keys(v).sort();
return '{' + keys.map(function (k) {
return JSON.stringify(k) + ':' + canonicalJson(v[k]);
}).join(',') + '}';
}
/* ---- the fold -------------------------------------------------------------
* answers: array of {label, answer} (or bare strings; index becomes label).
* opts: { foldCase, json, threshold (abs count), unanimous }
*/
function quorum(answers, opts) {
opts = opts || {};
const rows = normalizeInput(answers);
const n = rows.length;
// Group by normalized key, preserving first-appearance order and raw value.
const groups = []; // {key, count, labels[], rawFirst, order}
const byKey = new Map();
rows.forEach(function (r, i) {
const key = normalizeKey(r.answer, opts);
let g = byKey.get(key);
if (!g) {
g = { key: key, count: 0, labels: [], rawFirst: r.answer, order: i };
byKey.set(key, g);
groups.push(g);
}
g.count += 1;
g.labels.push(r.label);
});
// Sort tally: count desc, then first-appearance order asc (fully determined
// by the input — no hash/insertion nondeterminism).
const tally = groups.slice().sort(function (a, b) {
if (b.count !== a.count) return b.count - a.count;
return a.order - b.order;
});
// Needed count (the threshold).
let needed;
if (opts.unanimous) needed = n;
else if (typeof opts.threshold === 'number') needed = opts.threshold;
else needed = Math.floor(n / 2) + 1; // default: strict majority
const top = tally.length ? tally[0] : null;
const topCount = top ? top.count : 0;
const topGroups = tally.filter(function (g) { return g.count === topCount; });
let verdict, value, agreeLabels;
if (n === 0 || topCount < needed) {
verdict = 'no-quorum';
value = null;
agreeLabels = [];
} else if (topGroups.length > 1) {
// Tie for the top count AND it meets needed → tie, no winner.
verdict = 'tie';
value = null;
agreeLabels = [];
} else {
verdict = 'quorum';
value = top.rawFirst; // promise 2: a real input answer, verbatim
agreeLabels = top.labels.slice();
}
const agreeSet = new Set(agreeLabels);
const dissent = rows
.filter(function (r) { return !agreeSet.has(r.label); })
.map(function (r) { return { label: r.label, answer: r.answer }; });
return {
verdict: verdict,
value: value,
agree: agreeLabels,
dissent: dissent,
tally: tally.map(function (g) {
return { answer: g.rawFirst, count: g.count, labels: g.labels.slice() };
}),
n: n,
needed: needed,
threshold: opts.unanimous ? 'unanimous'
: (typeof opts.threshold === 'number' ? opts.threshold : 'majority')
};
}
/* Coerce mixed input into [{label, answer}], filling labels from index. */
function normalizeInput(answers) {
if (!Array.isArray(answers)) return [];
return answers.map(function (a, i) {
if (a && typeof a === 'object' && 'answer' in a) {
return {
label: (a.label == null ? String(i + 1) : String(a.label)),
answer: String(a.answer == null ? '' : a.answer)
};
}
// bare string / other scalar
return { label: String(i + 1), answer: String(a == null ? '' : a) };
});
}
const CEILING =
'quorum counts agreement; it does not judge correctness. N sources can ' +
'agree and all be wrong. A quorum means "this many independently landed ' +
'here", never "here is right". It does not call the models for you — you ' +
'bring the answers, it folds them. Concordance, not truth.';
/* ---- selftest (the determinism + promise proof) --------------------------- */
function selftest() {
const cases = [];
function check(name, cond) { cases.push({ name: name, ok: !!cond }); }
// majority default
let r = quorum([
{ label: 'gpt', answer: '42' },
{ label: 'claude', answer: '42' },
{ label: 'llama', answer: '43' }
]);
check('majority verdict', r.verdict === 'quorum');
check('majority value', r.value === '42');
check('majority agree', r.agree.length === 2);
check('majority dissent', r.dissent.length === 1 && r.dissent[0].label === 'llama');
check('needed=2 for n=3', r.needed === 2);
// no-quorum: 3-way split
r = quorum([{ answer: 'a' }, { answer: 'b' }, { answer: 'c' }]);
check('no-quorum verdict', r.verdict === 'no-quorum');
check('no-quorum value null', r.value === null);
check('no-quorum dissent = all', r.dissent.length === 3);
// tie: 2 vs 2, needed=3 default majority of 4 → floor(4/2)+1 = 3, top=2 < 3
r = quorum([{ answer: 'x' }, { answer: 'x' }, { answer: 'y' }, { answer: 'y' }]);
check('2v2 majority is no-quorum (needs 3)', r.verdict === 'no-quorum');
// tie that MEETS threshold: threshold=2, 2 vs 2 → tie
r = quorum([{ answer: 'x' }, { answer: 'x' }, { answer: 'y' }, { answer: 'y' }],
{ threshold: 2 });
check('2v2 at threshold 2 is a tie', r.verdict === 'tie');
check('tie value null', r.value === null);
// unanimous
r = quorum([{ answer: 'q' }, { answer: 'q' }], { unanimous: true });
check('unanimous pass', r.verdict === 'quorum' && r.value === 'q');
r = quorum([{ answer: 'q' }, { answer: 'r' }], { unanimous: true });
check('unanimous fail', r.verdict === 'no-quorum');
// whitespace normalize (default)
r = quorum([{ answer: ' hello world ' }, { answer: 'hello world' }, { answer: 'x' }]);
check('whitespace collapses', r.verdict === 'quorum' && r.agree.length === 2);
// but value is the FIRST raw form (promise 2)
check('value is first raw form', r.value === ' hello world ');
// case fold opt-in
r = quorum([{ answer: 'Yes' }, { answer: 'yes' }, { answer: 'no' }], { foldCase: true });
check('fold-case merges', r.verdict === 'quorum' && r.agree.length === 2);
r = quorum([{ answer: 'Yes' }, { answer: 'yes' }, { answer: 'no' }]);
check('no fold-case keeps distinct (no-quorum)', r.verdict === 'no-quorum');
// json canonical opt-in
r = quorum([
{ answer: '{"a":1,"b":2}' },
{ answer: '{"b":2,"a":1}' },
{ answer: '{"a":9}' }
], { json: true });
check('json key-order agrees', r.verdict === 'quorum' && r.agree.length === 2);
// determinism: same input twice → identical serialization
const inp = [{ label: 'A', answer: 'k' }, { label: 'B', answer: 'k' }, { label: 'C', answer: 'm' }];
const a = JSON.stringify(quorum(inp));
const b = JSON.stringify(quorum(inp));
check('deterministic (folds twice identical)', a === b);
// exhaustiveness: agree ∪ dissent = all labels, disjoint
r = quorum([{ label: 'p', answer: '1' }, { label: 'q', answer: '1' }, { label: 'r', answer: '2' }]);
const seen = new Set(r.agree.concat(r.dissent.map(function (d) { return d.label; })));
check('exhaustive: every label accounted once',
seen.size === 3 && r.agree.length + r.dissent.length === 3);
// empty answer counts as a real answer (not dropped)
r = quorum([{ label: 'a', answer: '' }, { label: 'b', answer: '' }, { label: 'c', answer: 'z' }]);
check('empty answer counts', r.verdict === 'quorum' && r.value === '' && r.agree.length === 2);
// n=0 edge
r = quorum([]);
check('empty input is no-quorum', r.verdict === 'no-quorum' && r.n === 0);
const passed = cases.filter(function (c) { return c.ok; }).length;
return { passed: passed, total: cases.length, cases: cases };
}
/* ---- CLI ------------------------------------------------------------------ */
function parseArgs(argv) {
const opts = { foldCase: false, json: false, mode: 'fold', lines: false,
inlineAnswers: [], file: null, out: 'human' };
for (let i = 0; i < argv.length; i++) {
const a = argv[i];
if (a === '--selftest') opts.mode = 'selftest';
else if (a === '--help' || a === '-h') opts.mode = 'help';
else if (a === '--fold-case') opts.foldCase = true;
else if (a === '--json') opts.json = true;
else if (a === '--unanimous') opts.unanimous = true;
else if (a === '--lines') opts.lines = true;
else if (a === '--out-json') opts.out = 'json';
else if (a === '--threshold') { opts.threshold = parseInt(argv[++i], 10); }
else if (a === '--answer') { opts.inlineAnswers.push(argv[++i]); }
else if (a[0] !== '-') opts.file = a;
}
return opts;
}
function readInput(opts) {
// Priority: inline --answer flags, else file/stdin.
if (opts.inlineAnswers.length) {
return opts.inlineAnswers.map(function (s, i) {
const eq = s.indexOf('=');
if (eq === -1) return { label: String(i + 1), answer: s };
return { label: s.slice(0, eq), answer: s.slice(eq + 1) };
});
}
let text = '';
if (opts.file) {
try {
text = require('fs').readFileSync(opts.file, 'utf8');
} catch (e) {
// Fail clean on adversarial input (missing file, dir-as-file, unreadable):
// a named error to stderr and a nonzero exit — never an uncaught traceback.
process.stderr.write('quorum: cannot read ' + opts.file + ': ' + e.message + '\n');
process.exit(2);
}
} else if (!process.stdin.isTTY) {
try {
text = require('fs').readFileSync(0, 'utf8');
} catch (e) {
process.stderr.write('quorum: cannot read stdin: ' + e.message + '\n');
process.exit(2);
}
} else {
return [];
}
const lines = text.split(/\r?\n/).filter(function (l) { return l.length > 0; });
if (opts.lines) {
return lines.map(function (l, i) { return { label: String(i + 1), answer: l }; });
}
// JSONL: one {label,answer} per line
return lines.map(function (l, i) {
try {
const o = JSON.parse(l);
return { label: (o.label == null ? String(i + 1) : String(o.label)),
answer: String(o.answer == null ? '' : o.answer) };
} catch (_e) {
// a non-JSON line under JSONL mode: treat the whole line as an answer
return { label: String(i + 1), answer: l };
}
});
}
function renderHuman(r) {
const out = [];
out.push('verdict: ' + r.verdict + (r.value !== null ? ' → ' + JSON.stringify(r.value) : ''));
out.push('n=' + r.n + ' needed=' + r.needed + ' (' + r.threshold + ')');
out.push('tally:');
r.tally.forEach(function (t) {
out.push(' ' + t.count + '× ' + JSON.stringify(t.answer) +
' [' + t.labels.join(', ') + ']');
});
if (r.dissent.length) {
out.push('dissent:');
r.dissent.forEach(function (d) {
out.push(' ' + d.label + ': ' + JSON.stringify(d.answer));
});
}
return out.join('\n');
}
function main() {
const opts = parseArgs(process.argv.slice(2));
if (opts.mode === 'help') {
process.stderr.write(
'quorum — ask three, trust the overlap. A consensus fold over N answers.\n\n' +
'Usage:\n' +
' node quorum.js FILE.jsonl # {"label":..,"answer":..} per line\n' +
' node quorum.js --lines FILE.txt # each line is an answer (label = line no.)\n' +
' node quorum.js --answer a=42 --answer b=42 --answer c=43\n' +
' cat answers.jsonl | node quorum.js\n\n' +
'Options:\n' +
' --fold-case case-insensitive agreement\n' +
' --json compare answers as canonical JSON (key order ignored)\n' +
' --threshold N need N matching answers (default: strict majority)\n' +
' --unanimous need all N to agree\n' +
' --out-json emit the full result object as JSON\n' +
' --selftest run the golden selftest\n\n' +
'CEILING: ' + CEILING + '\n');
process.exit(0);
}
if (opts.mode === 'selftest') {
const r = selftest();
r.cases.forEach(function (c) {
process.stdout.write((c.ok ? 'ok ' : 'FAIL ') + c.name + '\n');
});
process.stdout.write('\n' + r.passed + '/' + r.total + ' passed\n');
process.exit(r.passed === r.total ? 0 : 1);
}
const answers = readInput(opts);
const r = quorum(answers, opts);
if (opts.out === 'json') {
process.stdout.write(JSON.stringify(r, null, 2) + '\n');
} else {
process.stdout.write(renderHuman(r) + '\n');
process.stderr.write('\n(' + CEILING + ')\n');
}
// exit code: 0 on a clean quorum, 2 on no-quorum/tie (so it gates in a pipe)
process.exit(r.verdict === 'quorum' ? 0 : 2);
}
/* ---- exports (browser attach + Node require) ------------------------------ */
const API = { quorum: quorum, normalizeKey: normalizeKey, canonicalJson: canonicalJson,
selftest: selftest, CEILING: CEILING };
if (typeof module !== 'undefined' && module.exports) {
module.exports = API;
if (require.main === module) main();
} else if (typeof window !== 'undefined') {
window.quorum = API;
}
test_quorum.js107 lineson GitHub →
#!/usr/bin/env node
/* SPDX-License-Identifier: MIT */
/* test_quorum.js — the shipped test for the quorum gift.
*
* Re-runs the in-file golden selftest AND asserts the two things a shipped gift
* must carry that a selftest alone does not check: the printed CEILING is
* present in the shipped source and emitted, and the structural promises hold
* on adversarial input. Exit 0 iff everything passes; non-zero otherwise, so it
* drops into CI unchanged.
*/
'use strict';
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const Q = require('./quorum.js');
let pass = 0, fail = 0;
function t(name, fn) {
try { fn(); pass++; console.log('ok ' + name); }
catch (e) { fail++; console.log('FAIL ' + name + ' — ' + e.message); }
}
// 1. The in-file golden selftest is GREEN.
t('golden selftest all green', function () {
const r = Q.selftest();
assert.strictEqual(r.passed, r.total, r.passed + '/' + r.total);
});
// 2. The CEILING string exists, is non-trivial, and is present in the source
// (a gift with no printed edge is a loud stop — profile B1/§3).
t('CEILING present and substantive', function () {
assert.ok(typeof Q.CEILING === 'string' && Q.CEILING.length > 40);
assert.ok(/correctness|concordance|truth/i.test(Q.CEILING));
});
t('CEILING is emitted by the shipped artifact (--help)', function () {
// The CEILING is built by concatenation in source, so it is not one contiguous
// literal in the file — assert the shipped BEHAVIOR instead: --help emits it to
// stderr, verbatim, every run. This is the presence check that actually matters
// to a stranger running the gift (profile B1/§3 printed-edge).
const { spawnSync } = require('child_process');
const r = spawnSync('node', [path.join(__dirname, 'quorum.js'), '--help'],
{ encoding: 'utf8' });
const emitted = (r.stdout || '') + (r.stderr || '');
assert.ok(emitted.indexOf(Q.CEILING) !== -1, 'CEILING not emitted by --help');
});
t('CEILING core phrase is present in the shipped source', function () {
// A weaker, robust source check: the load-bearing phrase is literally present.
const src = fs.readFileSync(path.join(__dirname, 'quorum.js'), 'utf8');
assert.ok(/does not judge correctness/.test(src), 'ceiling phrase not in source');
});
// 3. Promise 1 — pure & deterministic: fold twice, byte-identical.
t('promise: folds twice identical', function () {
const inp = [{ label: 'A', answer: 'k' }, { label: 'B', answer: 'k' },
{ label: 'C', answer: 'm' }, { label: 'D', answer: 'k' }];
assert.strictEqual(JSON.stringify(Q.quorum(inp)), JSON.stringify(Q.quorum(inp)));
});
// 4. Promise 2 — value is always a verbatim input answer or null.
t('promise: value never invented', function () {
const r = Q.quorum([{ answer: ' spaced out ' }, { answer: 'spaced out' },
{ answer: 'x' }]);
assert.strictEqual(r.verdict, 'quorum');
// winner group normalized-merged, but value is the FIRST raw form
assert.strictEqual(r.value, ' spaced out ');
});
// 5. Promise 3 — exhaustive & disjoint: agree ∪ dissent = all, no overlap.
t('promise: exhaustive and disjoint', function () {
const rows = [];
for (let i = 0; i < 20; i++) rows.push({ label: 'r' + i, answer: (i % 3) + '' });
const r = Q.quorum(rows);
const inAgree = new Set(r.agree);
const inDissent = new Set(r.dissent.map(function (d) { return d.label; }));
// disjoint
r.agree.forEach(function (l) { assert.ok(!inDissent.has(l)); });
// exhaustive
assert.strictEqual(r.agree.length + r.dissent.length, 20);
const all = new Set(r.agree.concat(r.dissent.map(function (d) { return d.label; })));
assert.strictEqual(all.size, 20);
});
// 6. Order independence of the tally is by rule, not by input hash: the tally
// is sorted (count desc, first-appearance asc) — a re-labeled but identically
// -shaped input yields the same counts in the same order.
t('tally order is a fixed function of input', function () {
const a = Q.quorum([{ answer: 'z' }, { answer: 'z' }, { answer: 'a' }]);
assert.deepStrictEqual(a.tally.map(function (t) { return t.count; }), [2, 1]);
});
// 7. Empty answers are counted, never dropped.
t('empty answers counted', function () {
const r = Q.quorum([{ answer: '' }, { answer: '' }, { answer: 'x' }]);
assert.strictEqual(r.verdict, 'quorum');
assert.strictEqual(r.value, '');
assert.strictEqual(r.n, 3);
});
// 8. Tie meeting threshold yields null, never a silent pick.
t('tie yields no winner', function () {
const r = Q.quorum([{ answer: 'x' }, { answer: 'y' }], { threshold: 1 });
assert.strictEqual(r.verdict, 'tie');
assert.strictEqual(r.value, null);
});
console.log('\n' + pass + '/' + (pass + fail) + ' passed');
process.exit(fail === 0 ? 0 : 1);