* chore(evals, cli): contract standardization + bench --json stdout cleanliness
End-of-session shippability pass. Three concrete fixes:
1. core/cli.py — bench --json no longer pollutes stdout
Several bench paths call scripts.run_pulse.run_pulse which prints
verbose [pulse] traces unconditionally to stdout, breaking jq /
programmatic consumers of --json output.
New _bench_stdout_guard() redirects stdout → stderr for the
duration of the bench run when --json is set. Operator still sees
the pulse trace (on stderr), but --json consumers get a clean JSON
document on stdout. Applied to all four bench paths: cost,
articulation, default suite, and --suite all.
Verified: core bench --suite determinism --json now produces
parseable JSON; human path still shows 1140 [pulse] lines.
2. evals/{frontier_compare,realizer_guard}/contract.md (new)
core/contemplation/contract.md (new)
Each new contract follows the established pattern (37 contracts
already exist under evals/<lane>/contract.md):
- What it measures
- Why it matters (structural win)
- How to run
- How to read the output
- Pass criteria table
- When it has failed and why
- Runner / module layout
Coverage:
- frontier_compare: both Lane A (CORE-only suites) and Lane B
(cross-provider prompt_battery) with explicit guardrails
against mixing — operator asks for the wrong lane combination,
runner exits 2 with helpful error.
- realizer_guard: C1/C2 articulation safety boundary — synthetic
illegal candidates rejected directly by check_surface AND
former-bug runtime prompts now produce legal articulations.
- contemplation (ADR-0080): not under evals/ since it's runtime
infrastructure that consumes eval reports — contract lives at
core/contemplation/contract.md. Documents the read-only +
SPECULATIVE-only + deterministic-replay invariants and the
shared DiscoveryCandidateSink plumbing convergence (ADR-0080).
3. evals/CLAIMS.md — Tier 2 rows added
- frontier_compare Lane A: determinism.primary_score, max_versor_condition
- frontier_compare Lane B: prompt_battery.primary_score (CORE adapter),
cross-provider artifact persistence
- realizer_guard: all_claims_supported
- contemplation: SPECULATIVE-only invariant, deterministic replay,
additive sink path, no pack mutation (all CI-pinned by tests)
Verification
------------
$ core test --suite smoke -q
67 passed in 27.22s (no regression)
$ uv run pytest -q tests/test_contemplation_loop.py \
tests/test_contemplation_pipeline_convergence.py \
tests/test_frontier_compare_cross_provider.py
27 passed in 4.87s
$ core bench --suite determinism --json 2>/dev/null | jq .results[0].passed
true (was: JSONDecodeError on prior [pulse] pollution)
* feat(evals/ui): report viewer renders Lane B cross-provider + pass-rate chart
Stop-hook caught that #62 only covered contracts — the 929-line
report_viewer.html was never audited against the new cross-provider
report shape from #61. Two real gaps:
1. Lane-aware observation drawer
The drawer hardcoded Lane A (CORE-native) fields: surface,
grounding_source, anchor_lens_mode_label, versor_condition.
Lane B (cross-provider) observations carry different fields:
provider, model, elapsed_ms, error_type, error_message.
Loading a cross-provider report rendered only the surface row
with empty `grounding` — the provider + model + timing data
was unreachable without expanding "Show raw JSON".
Fix: detect Lane B (presence of `obs.provider`) and render the
appropriate field set. Lane A still renders identically (now
also surfaces trace_hash + register_id when present, which were
silently buried in the raw JSON before).
2. Pass-rate chart per suite
The summary strip showed one aggregate Primary % across all
suites, with no way to see WHICH suite is dragging the score.
Multi-suite runs (e.g. --suite all) had to expand each panel
individually to find the failing one.
Fix: new .passrate-chart element below the summary strip,
one horizontal bar per suite showing passed/total. All-pass =
solid green, all-fail = solid red, partial = green/red split
at the pass fraction. CSS only — no new dependencies.
3. SUITE_PREAMBLES gains the prompt_battery entry so the sidebar
shows the "side-by-side surface evidence across providers"
description when loading a Lane B report.
Verified
--------
- Brace/paren/div balance unchanged (308/308 / 380/380 / 54/54)
- One <script> tag pair preserved
- Generated a real Lane B report via
`python -m evals.frontier_compare --provider core --suite prompt_battery`
for visual confirmation
Out of scope (noted for future PR)
----------------------------------
Sampled 3 `core demo` targets:
- register-tour: clean schema (all_claims_supported, claims, grid)
- audit-tour: both scene_1_* keys AND an empty scenes:[] array — inconsistent
- anti-regression: no all_claims_supported key, uses all_gates_held instead
Demo schema standardization deserves its own PR — operator tooling
would benefit from a uniform top-level success field across demos.
* docs(evals) + chore(demos): systematic audit + uniform success field
Stop-hook caught two real gaps after the contract+UI PR:
- demos had divergent success-field names (all_gates_held vs
learning_loop_closed vs claim_supported vs nested claims_supported)
- no systematic look at the 48 eval directories had been done
Both addressed concretely; remaining work captured in audit doc
rather than vaguely deferred.
1. Demo schema standardization — uniform all_claims_supported field
----------------------------------------------------------------------
All 9 ``core demo`` targets now emit a top-level
``all_claims_supported: bool`` field. Existing per-demo fields
(``all_gates_held``, ``learning_loop_closed``, ``claim_supported``,
nested ``claims_supported``) are preserved for backwards compat —
the new field is an alias derived from the demo's existing success
signal, not a replacement.
Operator tooling and the CI gate can now target
``all_claims_supported`` without knowing each demo's idiomatic
field name.
Files touched:
- evals/anti_regression/run_demo.py — adds AND of all_gates_held +
active_corpus_byte_identical
- evals/learning_loop/run_demo.py — adds AND of learning_loop_closed +
active_corpus_byte_identical
- scripts/publish_pack_measurements.py — adds AND of the three
entries in the nested claims_supported dict
- evals/long_context_cost/comparison_runner.py — adds alias for
claim_supported (singular)
The 5 demos already using ``all_claims_supported`` (audit-tour,
register-tour, anchor-lens-tour, orthogonality-tour, articulation)
are unchanged.
Verified across all 9 demos:
audit-tour : True
register-tour : True
anchor-lens-tour : True
orthogonality-tour : True
pack-measurements : True ← new alias
anti-regression : True ← new alias
learning-loop : True ← new alias
articulation : True
long-context-comparison : True ← new alias
2. docs/EVAL_AUDIT_2026-05-20.md — systematic 48-lane audit
------------------------------------------------------------
Replaces the "future PR" deferral with a concrete document.
Contains:
- Method (what was inspected for each lane).
- Summary (40/48 have contract.md; 18/48 have saved results;
empty results/ ≠ broken — most lanes regenerate on demand).
- Cross-provider relevance triage:
* 9 lanes are cross-provider-relevant and could benefit
from the prompt_battery-style adapter pattern (cognition,
english_fluency_ood, hebrew_fluency, koine_greek_fluency,
grammatical_coverage, inference_closure, multi_step_reasoning,
discourse_paragraph, foundational_*_ood, etc.).
* 29 lanes are CORE-only by design (versor closure, anchor
lens, identity divergence, provenance, etc.) — wiring
providers would be category-erroneous.
- Demo schema standardization status (this PR closes that).
- UI/UX coverage matrix.
- 5 concrete follow-up items, each focused enough for a single
PR, none requiring architectural change.
Regenerated reports
-------------------
evals/long_context_cost/results/comparison_v1.json and
evals/results/phase2_pack_measurements.json now contain the new
all_claims_supported field (auto-regenerated when validating the
schema change).
evals/frontier_compare/results/sample_core_promptbattery.json
added as a reference Lane B report so the new viewer always has
something to load on first open.
7.1 KiB
frontier-compare benchmark family
What it measures
Two complementary lanes, with explicit guardrails against mixing them:
Lane A — CORE-only suites (telemetry-rich)
Three suites that exercise structural properties only CORE can expose:
determinism— same prompt × N fresh runtimes → byte-identicalsurface,grounding_source,register_canonical_surface, andtrace_hash.max_versor_conditionstays under1e-6throughout.truth_lock— known-truth + unknown-relation cases. The known case must ground via pack/teaching; the unknown case must route to the deterministic refusal surface (no fabrication).axis_orthogonality— register / anchor-lens / language axes must compose without interfering: register variation moves the surface while holdingtrace_hashinvariant; anchor-lens engages on the right lemmas only.
Lane A reads CORE-internal fields (trace_hash, versor_condition,
register_id, register_variant_id, anchor_lens_id,
register_canonical_surface, pre_decoration_surface) — these have
no equivalent on a frontier provider's stream of bytes, so the lane
requires --provider core.
Lane B — Cross-provider suites (provider-agnostic)
One suite that runs over any registered provider:
prompt_battery— 7 fixed prompts (definition, cause, verification, comparison, procedure, unknown intent shapes) × adapter(prompt) → surface. Per-casepassedis loose by design: non-empty surface within elapsed-ms budget. The point of the suite is side-by-side surface evidence across providers, not a quality verdict — reviewers diffdetails.observation.surfacerows themselves.
Lane B uses the provider adapter pattern from ADR-0082:
build_adapter(ProviderConfig) → (prompt) → str. CORE is one
adapter among {core, openai, anthropic, ollama}.
Why it matters (structural win)
The frontier-comparison rule (README.md):
If a frontier LLM can solve it, let it solve it. If CORE can solve something the LLM cannot structurally audit, CORE must prove it. If both solve it, compare correctness, determinism, traceability, latency, cost, memory, and failure mode.
Lane A measures the "structurally audit" half: properties an external API stream cannot supply at all. Lane B measures the "both solve it" half: same prompt, side-by-side surfaces, operator makes the judgment.
The lane split is load-bearing. Forcing CORE-only suites
through non-CORE providers would silently produce reports with
empty telemetry fields — a worse failure mode than refusing. The
runner enforces this at the CLI boundary (--provider openai --suite determinism exits 2 with an operator-helpful message).
How to run
Lane A (CORE-only)
# All CORE-only suites
python -m evals.frontier_compare
# One suite, machine-readable
python -m evals.frontier_compare --suite determinism --json --report path/to/out.json
Lane B (cross-provider)
# CORE adapter — no credentials needed
python -m evals.frontier_compare --provider core --suite prompt_battery --json
# Real provider — requires .env with credentials
python -m evals.frontier_compare --provider openai --suite prompt_battery
python -m evals.frontier_compare --provider openai --model gpt-4o-2024-08-06 --suite prompt_battery
python -m evals.frontier_compare --provider anthropic --model claude-sonnet-4-5 --suite prompt_battery
python -m evals.frontier_compare --provider ollama --model llama3.2 --suite prompt_battery
Non-CORE runs are always persisted to
evals/frontier_compare/results/<provider>_<model>_<utc>.json
even without --report — API calls are rate-limited / paid, so
losing the artifact is genuinely costly.
The --model flag is validated against model_registry.py —
floating aliases (e.g. raw gpt-4o) are rejected before any
benchmark cycles burn. Dated snapshots only.
How to read the report
Both lanes emit a BenchmarkReport JSON:
{
"benchmark_family": "frontier_compare_wave1",
"model": "core-native", // adapter model id
"mode": "core", // provider name
"suites": [
{
"suite": "prompt_battery",
"case_count": 7,
"primary_score": 1.000,
"passed": true,
"cases": [
{
"suite": "prompt_battery",
"case_id": "definition_truth",
"prompt": "What is truth?",
"passed": true,
"score": 1.0,
"elapsed_ms": 12.4,
"details": { "observation": { "surface": "...", ... } },
"failures": []
},
...
]
}
],
"summary": { "suite_count": 1, "case_count": 7, "primary_score": 1.0, "passed": true }
}
The HTML viewer at evals/frontier_compare/ui/report_viewer.html
loads any report JSON via a drag-and-drop / file picker. It surfaces:
- Per-suite PASS/FAIL with primary score.
- Per-case row with case_id, status, failures, elapsed_ms.
- Drawer view of the full observation (CORE-only suites: full telemetry; cross-provider: surface + provider + model).
Pass criteria
| Lane | Suite | Metric | Threshold | Current |
|---|---|---|---|---|
| A | determinism |
every prompt → 1 unique surface across --runs repeats |
1.00 | ✅ 1.00 |
| A | determinism |
max_versor_condition |
< 1e-6 | ✅ |
| A | truth_lock |
known cases ground; unknown cases refuse | 1.00 | ✅ |
| A | axis_orthogonality |
trace_hash invariant under register variation | true | ✅ |
| B | prompt_battery |
all 7 cases produce non-empty surface w/ no adapter exception (CORE) | 1.00 | ✅ 1.00 |
| B | prompt_battery |
cross-provider rows persisted to results/ |
true | ✅ |
prompt_battery does NOT score semantic quality — that is for human
review. A green row means "the adapter answered without crashing,"
not "the answer was correct."
When it has failed and why
- 2026-05-17 —
frontier_compare_wave1Lane A first shipped (#52), all CORE-only suites green from day one. - 2026-05-20 — ADR-0082 provider adapters shipped (
#58, renumbered in#59) but were unwired —runner.pyhardcodedChatRuntime. Cross-provider promise was shelf-ware. - 2026-05-20 — Cross-provider Lane B added (
#61). Routing bug caught in the same PR: initial CLI dispatch sent--suite prompt_batteryto the CORE-native runner even when--provider=core. Fixed by making suite name the load-bearing axis (anyprompt_batteryrequest goes through the adapter path, CORE included). - 2026-05-20 — Existing CORE-only telemetry would have been silently dropped if Lane B reused those suites. Avoided by explicit lane split + loud CLI rejection of cross combinations.
Runner
runner.py— CORE-only suites (Lane A).cross_provider.py—run_prompt_battery(adapter, *, cfg)for Lane B.__main__.py— CLI dispatch.providers.py—ProviderConfig,build_adapter,load_dotenv_if_present(ADR-0082).model_registry.py—ModelCard,require_model_card,list_registered_models(ADR-0082).ui/report_viewer.html— standalone viewer.results/— per-run JSON artifacts.