Skip to content

feat(v1): episode-level derived aggregates and typed EnvInfo - #2187

Merged
mikasenghaas merged 7 commits into
mainfrom
feat/episode-aggregates
Jul 31, 2026
Merged

feat(v1): episode-level derived aggregates and typed EnvInfo#2187
mikasenghaas merged 7 commits into
mainfrom
feat/episode-aggregates

Conversation

@mikasenghaas

@mikasenghaas mikasenghaas commented Jul 30, 2026

Copy link
Copy Markdown
Member

Summary

  • Episode gains derived, non-serialized aggregates mirroring Trace's property pattern: usage (agent-call usage summed via Usage.aggregate; judge/off-graph usage stays on trace.extra_usage), num_input_tokens / num_output_tokens / num_total_tokens, num_turns, and by_agent (traces grouped by agent name, e.g. n solvers). Consumers holding a validated WireEpisode (the serve wire, prime-rl) get them for free — properties stay off the wire and disk.

  • Episode.env is now a typed EnvInfo carrying the env id, instead of a bare string.

  • Episode.error is renamed to last_error, matching Trace.last_error; short docstrings added to all Episode fields and properties.

  • StrictBaseModel is removed: the whole record tree (Trace, Episode, messages, usage, task data, timing, calls, nodes, judge/rubric types) parses non-strict — extras are ignored instead of rejected, so additive schema evolution no longer breaks older readers. WireTaskData keeps extra="allow" to round-trip unknown task fields.

  • The dashboard's _tokens helper is deleted in favor of the Trace properties it re-implemented (num_input_tokens / num_output_tokens / num_branches / usage).

  • uv.lock syncs the per-package exclude-newer cutoffs to the UTC timestamps pyproject pinned in chore: timezone-stable uv.lock and trace record cleanups #2172 (the lock was never re-locked, so every uv run --locked hook failed).

  • The Timing.generation span is renamed Timing.agent (GenerationSpanAgentSpan, still split into model and harness time), and Trace.split_generation becomes split_agent_time.

Breaking

  • Timing.generationTiming.agent (GenerationSpanAgentSpan, Trace.split_generationsplit_agent_time): persisted traces carry timing.agent; older records with timing.generation lose that span on re-read (extras are ignored).
  • Episode.env is EnvInfo instead of str: episodes persist as "env": {"id": ...}; existing traces.jsonl files with string env no longer parse for resume/replay. Read episode.env.id for the id; Episode.of(trace, env=...) still takes the id string.
  • Episode.errorEpisode.last_error.
  • vf.StrictBaseModel is gone (removed from the public API); subclass pydantic.BaseModel instead. No models reject unknown fields anymore: extras are ignored on validation (and dropped on rewrite paths like resume) rather than raising.

Verification

  • uv run pytest tests/ green; ruff, pre-commit run --all-files, and the ty pre-push hook pass (re-run after the non-strict sweep).
  • Live smoke: uv run eval reverse_text_v1 -n 2 --rich renders the usage/time rows and per-trace tokens correctly, pushes traces, and the persisted episode round-trips through read_episodes with env.id and working aggregates.

🤖 Generated with Claude Code


Note

High Risk
Persisted episodes and traces change shape (env object, timing.agent), and relaxing validation breaks strict readers and old traces.jsonl resume unless migrated; timing rename drops legacy generation spans on re-read.

Overview
Episode now stores env as EnvInfo ({"id": ...} on disk) instead of a plain string, exposes last_error (was error), and adds derived properties—usage, token/turn totals, and by_agent—summed from traces without extra wire fields.

Trace timing renames generationagent (GenerationSpanAgentSpan, split_generationsplit_agent_time). Rollout, debug, legacy v0 mapping, and the eval dashboard follow the new span name and read token counts from Trace properties instead of a local _tokens helper.

StrictBaseModel is removed across traces, episodes, messages, tasks, judges, and artifacts; models use plain pydantic.BaseModel so unknown fields are ignored rather than rejected. Public exports drop StrictBaseModel and export AgentSpan instead of GenerationSpan.

Replay and e2e expectations use episode.env.id and the new serialized env shape.

Reviewed by Cursor Bugbot for commit c9ee978. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add episode-level derived aggregates and structured EnvInfo to Episode

  • Episode now stores a structured EnvInfo object (with an id field) instead of a plain string for env, and exposes new aggregate properties: num_input_tokens, num_output_tokens, num_total_tokens, num_turns, usage, and by_agent.
  • GenerationSpan is renamed to AgentSpan and Timing.generation becomes Timing.agent throughout traces, the dashboard, debug/replay CLI, and the v0-to-v1 legacy bridge.
  • StrictBaseModel is removed across all v1 models; all Pydantic models now use BaseModel with extra='ignore' semantics, accepting unknown fields instead of raising validation errors.
  • The error property on Episode is replaced by last_error; the dashboard and replay CLI are updated accordingly.
  • Risk: serialized trace and episode JSON now uses agent instead of generation for timing fields and a nested env object instead of a string — existing consumers of these fields will break.

Macroscope summarized c9ee978.

Episode gains derived, non-serialized aggregates mirroring Trace's
property pattern (usage, num_input/output/total_tokens, num_turns),
env becomes a typed EnvInfo carrying the env id, error is renamed to
last_error to match Trace, and Trace/Episode relax from StrictBaseModel
to plain BaseModel so additive schema evolution no longer breaks older
readers. The dashboard's duplicated _tokens helper is replaced by the
Trace properties it re-implemented.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread verifiers/v1/episode.py
@macroscopeapp

macroscopeapp Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR changes the serialization schema (Episode.env from string to typed object) and removes strict validation (extra="forbid") from all models, relaxing field constraints system-wide. These schema and validation changes warrant human review for backward compatibility implications.

You can customize Macroscope's approvability policy. Learn more.

Extras are ignored everywhere instead of rejected: the whole record tree
(messages, usage, task data, timing, calls, nodes, judge/rubric types)
tolerates additive schema evolution. WireTaskData keeps extra="allow" to
round-trip unknown task fields.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread verifiers/v1/types.py
Timing.generation -> Timing.agent (AgentSpan, still split into model and
harness), Trace.split_generation -> split_agent_time.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 515b820. Configure here.

Comment thread verifiers/v1/trace.py
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mikasenghaas
mikasenghaas requested a review from hallerite July 31, 2026 00:17
mikasenghaas and others added 2 commits July 31, 2026 00:22
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
hallerite
hallerite previously approved these changes Jul 31, 2026
Migrates #2144's new records (Artifact, CollectHook) off the removed
StrictBaseModel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mikasenghaas
mikasenghaas merged commit 2719276 into main Jul 31, 2026
12 of 13 checks passed
@hallerite
hallerite deleted the feat/episode-aggregates branch July 31, 2026 00:45
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.

2 participants