crux-protocol
v0.1.0
Published
CRUX/1 — a dialectical exchange protocol for two LLMs to debate a claim, with typed challenges, provenance tracking, and a cost-guardrailed orchestrator.
Maintainers
Readme
crux-protocol
Runs a CRUX/1 dialectical exchange between two LLM peers debating a
claim — typed challenges, provenance tracking, credence updates, and a required
RES/IMP/HALT exit, instead of free-form chat that tends to collapse into
agreement without actually testing anything.
Install
npm install crux-protocol
npx crux initcrux init scaffolds config.yaml and .env.example into your project (skipping
either if it already exists), and adds .env / transcripts/ to .gitignore. It
also drops a SKILL.md into .claude/skills/crux-protocol/, so a Claude Code
session in this project knows how to invoke it.
Then:
cp .env.example .env
# fill in GOOGLE_API_KEY and/or FIREWORKS_API_KEYUsage
npx crux "<topic claim to debate>" [--a=google|fireworks] [--b=google|fireworks] \
[--a-model=<model id>] [--b-model=<model id>] [--max-turns=12]Example:
npx crux "For a small team's internal tools, a monolith beats microservices." \
--a=google --b=fireworks --max-turns=8Peers default to --a=google --b=fireworks. Anthropic's adapter ships in the
package (providers/anthropic.js) but isn't wired into orchestrate.js by
default — see Anthropic below.
Output
- Live CRUX frames print to stdout as the exchange runs.
- Full accepted transcript:
transcripts/<timestamp>-<topic-slug>.md. - Every raw API call — system prompt, user prompt, raw response, token usage,
estimated cost, including retries and failures — logged to
transcripts/<timestamp>-<topic-slug>.calls.md.
Cost guardrail
config.yaml sets a spend cap:
budget:
maxUsd: 1.00
pricing:
google:
gemini-3.1-pro-preview:
input: 2.00 # USD per 1,000,000 tokens
output: 12.00
fireworks:
accounts/fireworks/models/glm-5p3:
input: 1.40
output: 4.40
defaultPricing:
input: 0
output: 0Every call's actual token usage (from the API response, not estimated) is
multiplied by these rates and added to a running total. The exchange halts
cleanly — partial transcript and call log preserved — the moment that total
reaches maxUsd. Prices are not fetched live; re-check them against each
vendor's pricing page periodically, and add an entry under pricing for any
model you point --a-model/--b-model at. An unpriced model reports $0 cost
(with a one-time warning) rather than guessing.
Anthropic
providers/anthropic.js and a matching CLAUDE_API_KEY in .env.example are
included for a third peer, but scripts/orchestrate.js doesn't register it in
its provider map by default. To enable it: uncomment the callAnthropic import
and its PEERS entry near the top of scripts/orchestrate.js, and add a price
entry under config.yaml's pricing.anthropic.
Known limitations
- Termination is simplified. The orchestrator stops at the first
RESorIMPmove it sees, not when every claim on the crux stack is resolved (the spec's actual Rule 7 intent). Fine for exploration; not a literal implementation of the full termination rule. - Frame validation is pragmatic, not exhaustive.
validateFrameinscripts/orchestrate.jscatches the mechanically checkable frame errors from CRUX-1.md §8.1 (missing credence/provenance, missing attack codes, malformedU/RES/IMPforms, unrecognized move codes) — it does not check the semantic rules (§6.2 uncertainty inheritance, §7.1 unforced-movement smells).
Protocol spec
See CRUX-1.md for the full CRUX/1 specification — move vocabulary, attack codes, provenance tags, and a worked example.
