Skip to content

feat: trace_invariants evaluator and max_invariant_violations budget - #35

Merged
babaliauskas merged 16 commits into
mainfrom
feat/trace-invariants
Oct 7, 2026
Merged

babaliauskas merged 16 commits into
mainfrom
feat/trace-invariants

Conversation

@babaliauskas

@babaliauskas babaliauskas commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Hand-written contracts over tool-call traces, checked independently of the source side.

  • evaluators.trace_invariants: rules forbidden, required, order, call_count (min/max/exact per tool), arguments (JSON Schema); traces: replayed|imported; owner; applies_to enforced; blocking: false for advisory entries
  • migration_policy.max_invariant_violations (default 0, per-slice overridable): counts distinct examples where the target broke a blocking rule. A breach fails the run even when the suite is too small for statistics, and a slice that breaches fails however small it is
  • Report: "Trace rules broken" panel; report.json gets a top-level invariant_violations key
  • capture sync keeps a managed entry's hand-written trace_invariants block
  • evaluate warns when an entry's applies_to matched nothing
  • init scaffolds the new budget

Merge order: evalshift/evalshift-server#23 must be deployed first, because pushed bundles now always carry the field. Release as 1.3.0 (minor). Upgrading moves the resume config_hash (see CHANGELOG).

Tests: make ci passes with 2573 tests and 94.7% coverage. test_the_vendored_schema_matches_the_server_export fails locally against server main until the server PR merges; CI skips it.

🤖 Generated with Claude Code

babaliauskas and others added 16 commits October 6, 2026 15:42
…lations budget

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d traces

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…very sample's violations

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ils the run

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… shared

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Document the trace_invariants evaluator family and the
max_invariant_violations budget across DOCS.md, llms-full.txt and the
docs/ pages, and add the CHANGELOG entries under [Unreleased].

Sites enumerated mechanically:
- `max_tool_divergence` grep: 25 hits; 13 edited in place or extended
  beside (every budget list/table/schema/init block), 12 reviewed and left
  as-is (prose examples, or lists of the four rate budgets that correctly
  exclude a count budget).
- `agent_trace\b` grep: 15 hits; 13 edited (every evaluator-family list,
  plus imported-trace sections now pointing at `traces: imported`), 2
  reviewed and left (README examples table, agents.md flattening note).
- `max_critical_regressions` grep: 10 hits; all 10 budget blocks covered
  (9 edited in place, 1 covered by an adjacent new bullet).

Beyond the greps: the managed-region exception (sync keeps
trace_invariants despite the marker comment), report.json's
invariant_violations key, the INVARIANT_VIOLATION failure category,
decision.policy always carrying the field, and the eval_config_hash note.

init now scaffolds `max_invariant_violations: 0` in every profile, matching
how every other BUDGET_LABELS budget (CLI-only max_tool_divergence
included) is scaffolded; a test pins every profile to every budget.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r row

Each trace_invariants entry writes its own row per example, so two rule sets
with one example breaking both reported 2 of 4 examples on a 2-example suite.
max_invariant_violations and the shared-break note now count distinct
(prompt_id, example_id) pairs. Also pins the run-level override that turns a
slice-demoted conditional_pass into fail on a breached trace-rule budget.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An applies_to that matches no prompt (replayed) or no imported trace pair
left no row and no coverage entry, so the run read clean without a word
about the rules. evaluate now prints a warning naming the evaluator and its
applies_to (suppressed by quiet, like the module's other warnings); partial
scoping stays silent. The missing-traces.jsonl error now names the family
that needs it, trace_invariants (traces: imported) as well as agent_trace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
invariant_violations rows in report.json gain a `blocking` field from the
record, and the "Trace rules broken" panel tags rows from a
`blocking: false` entry with a muted "advisory" badge, so a reader can tell
which breaks gate the run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The class now also covers a rule both sides break, so "though the source
broke nothing" no longer describes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y-scope warning

max_invariant_violations counts distinct (prompt_id, example_id) examples
over a denominator of distinct examples, and its row appears only where a
blocking trace_invariants row was scored. Documents the evaluate warning for
an entry whose applies_to matched nothing, the family-naming missing-traces
error, and the `blocking` field / advisory tag on invariant_violations rows.
CHANGELOG: the capture sync bullet moves to Added, and the init bullet warns
that older CLIs reject the scaffolded field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@babaliauskas
babaliauskas merged commit 39fe562 into main Oct 7, 2026
4 checks passed
@babaliauskas
babaliauskas deleted the feat/trace-invariants branch October 9, 2026 19:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant