feat: store raw score and weight on trace rewards - #2119
Merged
Conversation
Trace.rewards entries are now Reward(score, weight) instead of the pre-multiplied product, so records keep raw scores readable and the weighted sum stays a derived view (trace.reward). Bumps TRACE_VERSION to 4. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
ApprovabilityVerdict: Needs human review This PR introduces a new You can customize Macroscope's approvability policy. Learn more. |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
hallerite
reviewed
Jul 23, 2026
|
|
||
|
|
||
| TRACE_VERSION = 3 | ||
| TRACE_VERSION = 4 |
Member
There was a problem hiding this comment.
lol since when do we have this
Member
Author
There was a problem hiding this comment.
i will reset this to 1 and only use it once we have trace api lol
Member
Author
There was a problem hiding this comment.
but eventually it will be important and we have to think abt versioning
hallerite
approved these changes
Jul 23, 2026
mikasenghaas
added a commit
that referenced
this pull request
Jul 29, 2026
One to_v<n> util per historical schema bump, chained by the migrate hook: v1->v2 lifts node-level usage/finish_reason into synthesized ModelCalls (#2061), v2->v3 nests the flat agent identity and top-level runtime into AgentInfo (#2106), v3->v4 wraps float rewards as Reward(score, weight=1) preserving reward sums (#2119), v4->v5 as before. Steps copy what they mutate, so validating the same dict twice is stable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mikasenghaas
added a commit
that referenced
this pull request
Jul 30, 2026
* fix: pin exclude-newer-package cutoffs as UTC timestamps Bare dates resolve to midnight in the machine's local timezone, so every timezone relocks uv.lock with different exclude-newer timestamps and the --locked pre-commit hooks fail for anyone outside the tz that produced the lock. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: align trace mutators on the record_* verb stamp -> record_run, capture_error -> record_error, matching the existing record_metric/record_reward/record_judge family and the trace's own 'record' vocabulary (to_record, record schema). Also drop the override warnings on record_metric/record_reward: overriding is defined behavior, last write wins. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: rename trace dump exclusions to EXCLUDE_FIELDS Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: an expired agent timeout is an agent error, not a truncation The rollout deadline expiring now records a HarnessError (ok=False, no scoring) instead of the clean harness_timeout stop that scored the partial trajectory: a timeout is the agent breaking its time budget, not a healthy run cut short. The stop-condition name is gone from the vocabulary; the legacy v0 bridge's timeout_reached maps through the generic truncation fallback. The concept is renamed harness timeout -> agent timeout throughout: TaskTimeout.harness -> TaskTimeout.agent, the rollout plumbing, and cap_remote_harness_timeout -> cap_remote_agent_timeout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: slim the Trace surface Drop the agent_name/trainable/runtime passthroughs (read trace.agent directly) and the duplicate error property (last_error is the one reader); align tool_messages with assistant_messages (nodes-based, branch-independent); require an explicit stop condition (the 'done' default was never used); correct the stop_condition docstring to the real vocabulary; tighten field docstrings and ordering. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: require agent and verifiers on Trace, default tools to empty agent: every producer sets a seat — the rollout its resolved config, the debug CLI its synthetic seat, and now the v0 bridge and validate CLI theirs — so the None guards at every consumer were dead weight. verifiers: stamped by default_factory at construction; stored records keep their serialized build. tools: the empty state was unreachable (dialects normalize [] to None to avoid clearing a recording), so None carried no signal over []. Bumps TRACE_VERSION to 5: old records without an agent no longer validate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: hoist TRACE_VERSION to the module top Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: tighten trace docstrings, require RunInfo.id Every consumer stamps a run id (the eval CLI its uuid, trainers their own), so the None default was unreachable. Docstrings across the trace models trimmed to the constraint they actually add; kept_tokens aligned with routed_experts' shape-first style. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: last trace.runtime accesses missed in the property removal Agent.interaction's two borrowed-runtime stamps and the dashboard's boot-vs-build stage probe still read the removed Trace.runtime passthrough; all three now read trace.agent.runtime. Verified live: eval with the rich dashboard renders and pushes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: migrate pre-v5 trace records on read A version-gated before-validator upgrades v4 records to the v5 shape (drop the explicit nulls v5 defaults now fill, seat records that predate the required agent, drop an id-less run stamp) so eval resume and replay keep reading old outputs. Current-version records validate strictly: a v5 record with tools=null is rejected. Delete the validator when v4 support is dropped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: restructure trace migration as chained per-version utils The migrate hook sits at the bottom of Trace and chains one _to_v<n> util per schema bump; the next bump adds _to_v6 and a new chain link. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat: retroactive trace migrations back to v1 One to_v<n> util per historical schema bump, chained by the migrate hook: v1->v2 lifts node-level usage/finish_reason into synthesized ModelCalls (#2061), v2->v3 nests the flat agent identity and top-level runtime into AgentInfo (#2106), v3->v4 wraps float rewards as Reward(score, weight=1) preserving reward sums (#2119), v4->v5 as before. Steps copy what they mutate, so validating the same dict twice is stable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: drop trace record migrations, reset TRACE_VERSION to 1 The tightened schema restarts the version counter; pre-existing records are not loadable and old eval runs cannot be resumed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: migrate the standalone agent example and env docs off removed trace APIs * chore: drop the seat term from new comments, restore trimmed timing docstrings --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
eligotts
added a commit
that referenced
this pull request
Jul 31, 2026
…fload Picks up 35 main commits: the v1 structure cleanup (#2146) that retires StrictBaseModel and renames _NODE_DUMP_EXCLUDE -> EXCLUDE_FIELDS, the ruff 0.16 / ty tooling bump (#2147, #2148), runtime-on-agent + agent config stamped on the trace (#2106), Reward score/weight records (#2119), MCP tools for Codex (#2140), and assorted v1 fixes. Resolutions: - trace.py: main's restructure subsumes this branch's block wholesale — EXCLUDE_FIELDS already carries multi_modal_data, and TRACE_VERSION=1 is main's deliberate reset (this branch never touched it). Took main. - graph.py: kept the raw-mm sidecar validators and previous_multi_modal_data; adopted main's plain BaseModel now that StrictBaseModel is gone. - clients/train.py: kept the is_multimodal import — still used by the bridge path that threads previous_multi_modal_data. Ruff 0.16 flagged two spots this branch added that main's cleanup pass never saw: a constant getattr in the ingress offload walker, and the blind except at the prepare_request_body boundary (annotated noqa, per main's own convention at rollout boundaries).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Trace.rewardsvalues are now aRewardmodel carrying the rawscoreand itsweight, instead of the pre-multipliedweight * scorefloat — records keep raw scores readable and the weighted sum stays a derived view (Reward.value = score * weight,Trace.reward = sum of values).record_rewardstores the pair;TRACE_VERSIONbumped 3 → 4;Rewardexported fromverifiers.v1.reward/avg_rewardremain the weighted sum.task_weightrescale now scales each reward'sweightin place of mutating the product, so the solver's raw task scores stay visible on the trace.Breaking
rewardschanges fromdict[str, float]todict[str, {score, weight}];TRACE_VERSION3 → 4. Old v3 records no longer parse throughTrace/WireTrace, so--resume/replay of runs written before this change fails on the rewards field (no coercion shim by design). Migration: re-run the eval, or rewrite old records' reward values as{"score": <value>, "weight": 1.0}.trace.rewardsvalues as floats must switch to.score(raw) or.value(weighted contribution).Verification
Ran
uv run eval reverse_text_v1 --no-pushbefore and after:rewards: {"lcs": 1.0}(version 3)rewards: {"lcs": {"score": 1.0, "weight": 1.0}}(version 4), headlinereward=1.000unchanged;WireTrace.model_validateround-trips the new record.pytest tests/v1 -m "not e2e", ruff, and pre-commit all pass.🤖 Generated with Claude Code
Note
Medium Risk
Breaking trace schema and any code treating
trace.rewardsvalues as floats; headlinereward/avg_rewardbehavior is preserved but resume/replay of v3 records fails without migration.Overview
Trace rewards now persist as
Rewardobjects (score+weight) instead of pre-weighted floats.Trace.rewardis derived as the sum ofscore * weight;record_rewardwrites the pair unchanged.TRACE_VERSIONbumps 3 → 4 — serialized traces with the olddict[str, float]shape no longer validate without migration.Downstream readers are updated to use
.score(or.weightwhere composition matters): eval dashboard reward breakdowns, platform push flattening and per-name means, legacy v0 bridge, proposer-solver solve rate, and agentic-judgetask_weight(scales each reward’s weight in place instead of multiplying stored values). Tests assert the new shape, including separate raw score vs weight when env score weights apply.Reviewed by Cursor Bugbot for commit 18ea5a1. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Store raw score and weight on trace rewards as
RewardobjectsRewardmodel inverifiers/v1/trace.pywithscore,weight, and a computedvalue(score * weight) field, replacing the previousdict[str, float]type forTrace.rewards.TRACE_VERSIONfrom 3 to 4 to reflect the schema change.push.py,legacy.py,agentic_judge/env.py, dashboardeval.py, andproposer_solver_v1) to access.scoreor.weightrather than treating reward values as raw floats.Rewardfrom theverifiers.v1package.trace.rewards[name]as a float will break; callers must now use.score,.weight, or.value.Macroscope summarized 18ea5a1.