Skip to main content

Eval capture — NDJSON schema reference

Status: stable from v0.21.0. Schema versioning via schema_version on every row; additive changes increment the minor version; removals are breaking-schema-v2. Audience: downstream consumers (primarily the sibling modusbrain-evals repo) that replay captured real-world queries as a BrainBench-Real fixture.

The pipeline

modusbrain eval export — the consumer contract

Emits NDJSON to stdout. One JSON object per \n-terminated line. stderr receives progress heartbeats. Every line starts with "schema_version": 1 so a forward-compat parser can fail loudly on schema v2 instead of silently misparsing. Typical usage from modusbrain-evals:

Row schema (v1)

Every exported row has this shape. Field order in JSON output is not guaranteed; consumers MUST key by name, not position.

Ordering + determinism

listEvalCandidates orders by created_at DESC, id DESC. Same- millisecond inserts tie on created_at; id DESC is the stable tiebreaker. Replay tools can consume rows in order and assume:
  • no duplicate rows across calls with non-overlapping --since windows
  • no missed rows across calls that chain --since windows (window end of run 1 is the strict upper bound, not a soft cursor)

Schema versioning promise

  • v1 (shipped v0.21.0) — this document. All fields listed above.
  • Additive changes increment modusbrain minor version (v0.25.0, v0.23.0 …) and ship with new optional fields. Consumers keyed on known fields ignore unknown keys and keep working.
  • Breaking changes (rename, type change, removal) increment schema_version to 2. Consumers MUST branch on schema_version to stay compatible.

eval_capture_failures — companion audit table

Not exported by modusbrain eval export. Surfaced via modusbrain doctor:
Reason enum (stable): db_down | rls_reject | check_violation | scrubber_exception | other. Cross-process visibility is the whole point — modusbrain doctor runs in its own process and reads the table directly, so in-process counters wouldn’t work.

Config + CONTRIBUTOR_MODE

Capture is off by default as of v0.25.0 (was on for everyone in earlier drafts). Two paths to turn it on: Path A — env var (contributor opt-in, the common case):
Path B — explicit config (~/.modusbrain/config.json, file-plane only):
Resolution order (most explicit wins):
  1. eval.capture: true in config → on
  2. eval.capture: false in config → off (overrides CONTRIBUTOR_MODE=1)
  3. MODUSBRAIN_CONTRIBUTOR_MODE === '1' → on
  4. otherwise → off
scrub_pii defaults to true independent of capture. Set eval.scrub_pii: false to preserve raw query text (only if you control the brain’s distribution). modusbrain config set eval.capture false does not work — that command writes the DB-plane config, and the MCP server reads the file-plane. Edit the JSON directly or use the env var.