Off-chain services for interpretive-markets — prediction markets resolved by AI judges against registered evaluation frameworks on Ritual L1.
This repo houses the indexer, public read API, consistency-audit watcher, and the eval-harness. The contracts and the framework specs live in the companion repo; this one is where chain events become queryable state and where verdicts get evaluated.
packages/
├── shared/ # typed ABIs, content addressing, ritual primitives, type defs
├── prisma/ # Postgres schema + generated client
├── api/ # Fastify HTTP API (markets, frameworks, executors, evidence)
├── seeder/ # chain → Postgres event indexer
├── watcher/ # consistency audit + dispute filing
└── eval-harness/ # held-out cases, scorers, blind labelling, foundry oracle
Six workspaces total. shared is consumed by every other package. prisma exports the generated client. api, seeder, watcher, eval-harness are runnable services.
nvm exec 22 npm install
nvm exec 22 npm run build --workspaces --if-presentNode 22 required (the eval-harness uses tsx + ES module loading). All five build-script workspaces (api, eval-harness, seeder, shared, watcher) tsc clean.
The eval-harness is the headline package. Mock provider runs without external credentials and is safe to run anywhere:
nvm exec 22 npm run eval --workspace @interpretive/eval-harness -- --suite=all --provider=mockReal-LLM provider requires one of ANTHROPIC_API_KEY / OPENAI_API_KEY / GEMINI_API_KEY / OPENROUTER_API_KEY in packages/eval-harness/.env:
nvm exec 22 npm run eval --workspace @interpretive/eval-harness -- --suite=all --provider=llmThe --provider=llm path runs the judge blind (model never sees case.expectedVerdict) and stores proposals in packages/eval-harness/src/judge-validation/human-labels.json awaiting human review. Once human labels populate, the report renders judge-vs-human agreement.
The rules scorer subprocess-invokes the actual Solidity HarnessRules bytecode via forge script script/eval/HarnessOracle.s.sol in the companion repo — TS fallback if forge isn't on PATH. Drift between off-chain eval and on-chain enforcement is structurally impossible because the eval is running the on-chain code.
Ten scorers total, split between the dossier (investigator output) and the verdict (judge output).
Investigator scorers grade the dossier shape:
investigator/schema— does the dossier validate againstdossierV1.json?investigator/completeness— required Tier-1 fields populated per subject?investigator/citations— every fact-bearing field has asources[]array?investigator/balance— multi-subject questions have proportional coverage?investigator/source-trust— every cited URL matches thesourceAllowlistprefix?
Judge scorers grade the verdict:
judge/schema— parses against the framework'soutputSchema?judge/rules— survives the on-chainHarnessRules(confidence floor, Tier-3 cap, citation prefix, subject membership) via the Foundry oracle?judge/determinism— byte-stable across--runs=N?judge/grounding— LLM-meta-scorer: do citations actually contain the cited claims?judge/reasoning— LLM-meta-scorer: does the declareddriving_tiermatch the cited evidence?
# Fastify read API (port from API_PORT, default 3000)
nvm exec 22 npm run start --workspace @interpretive/api
# Chain event indexer (one-shot — call from cron / scheduler)
nvm exec 22 npm run start --workspace @interpretive/seeder
# Consistency audit watcher (loops; files disputes on hash mismatch)
nvm exec 22 npm run start --workspace @interpretive/watcherEach service has its own .env in packages/<name>/.env. The prisma package also expects DATABASE_URL + DIRECT_URL.
The watcher does no LLM inference. Byte-equality is undefined on Ritual L1 (FP8 + GPU non-associativity), so re-execution byte-matching does not port from EigenCloud-style restaking designs. Instead, the audit:
- Recomputes
keccak256(abi.encode(marketId, frameworkId, question, sourceAllowlist))from currentMarket.get(marketId)and compares to the emittedInvestigationStarted.requestBinding. - Recomputes the canonical messagesJson hash by fetching
judge.mdfrom the framework tarball, fetching the dossier JSON from IPFS, and assembling per the canonical pre-assembly contract. - Snapshots
TEEServiceRegistryand pins the workload identity against a known value.
On any mismatch: Market.disputeAttestation(marketId, evidence).
Given a finalized market on Ritual, the eval-harness can reconstruct a Langfuse-shaped trace from on-chain events alone:
nvm exec 22 npm run replay-from-chain --workspace @interpretive/eval-harness -- \
--rpc-url https://rpc.ritualfoundation.org \
--market-address 0x... --market-id 1 --sink jsonSet LANGFUSE_PUBLIC_KEY + LANGFUSE_SECRET_KEY and --sink langfuse to push instead of writing JSON. The reconstruction is from primary on-chain sources only — anyone with the RPC can re-derive it.
Top-level Dockerfile-API, Dockerfile-Seeder, and Dockerfile-Watcher each produce a minimal linux/amd64 image. The eval-harness is a CLI, not a long-running service. The api needs to be publicly reachable; seeder and watcher are headless workers. All three default to RITUAL_RPC_URL=https://rpc.ritualfoundation.org.
MIT.