fix(v1): enforce trainable standing against the trainable model context#2101
fix(v1): enforce trainable standing against the trainable model context#2101hallerite wants to merge 1 commit into
Conversation
d82c0b8 to
c19cf2e
Compare
c19cf2e to
cae6562
Compare
ApprovabilityVerdict: Needs human review Unable to check for correctness in e605132. This PR changes runtime training behavior by auto-demoting mismatched trainable agents. An unresolved high-severity comment questions whether this approach is correct, suggesting it could cause silent training failures instead of explicit errors. You can customize Macroscope's approvability policy. Learn more. |
cae6562 to
e605132
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e605132ac5583fd3037f9148df90bac389cab97f. Configure here.
| ctx.model, | ||
| name, | ||
| type(self).__name__, | ||
| ) |
There was a problem hiding this comment.
Demotes instead of refusing
High Severity
The PR is meant to refuse a mismatched trainable agent with a ValueError on the episode, but _resolve_trainable_standing auto-demotes it and only logs a warning. Training runs then stay alive with zero trainable traces—the silent failure the design notes explicitly reject.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit e605132ac5583fd3037f9148df90bac389cab97f. Configure here.
One model context is trainable per run — `ctx`. `trainable` on an agent that points anywhere else (a different `model`, or its own pinned `client`) is a false statement, but nothing corrected it: such traces shipped stamped `trainable: true`, and consumers degraded silently (group baselines fed by tokenless traces, SFT-from-traces on another model's tokens, judge traces polluting eval metric filtering). `run_episode` now resolves standing right after `setup()` (which declares it), before any tokens burn, and the run's client decides the teeth: on a training run (a `TrainClient` — its tokens exist to be trained on) a divergent trainable agent is a config error and the episode fails loudly; on any other run it is demoted to untrainable, warned once per agent — an eval must not fail over a training-only concern, but standing filters metrics and records, so it must stay honest either way. The fix is one line in the env (`agents.<name>.trainable = False` in `setup()`). Verified with a throwaway probe over the duet fixture (no model calls, both run types): eval ctx — unpinned stays trainable, `model`/`client` pins demote before `run()` with one warning across episodes, declared-untrainable envs are untouched; train ctx — the same pins fail the episode, unpinned and declared-untrainable envs reach `run()`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
e605132 to
4527f61
Compare


What
Env.run_episodenow resolves trainable standing right aftersetup()and before any agent runs. One model context is trainable — the run'sctx— and every trainable agent must point to it (the samemodel, no pinnedclient). The run's client decides what divergence means:ctx.clientis aTrainClient— its tokens exist to be trained on): a divergent trainable agent is a config error; the episode fails loudly.Either way the message names the one-line fix:
agents.<name>.trainable = Falsein the env'ssetup().Why
AgentInfo.trainablemeans "these tokens train the trainable model". An agent whose model context points anywhere else makes that a false statement, but nothing corrected it: its traces shipped stampedtrainable: true, and consumers degraded silently — in prime-rl the trace enters GRPO's group baseline while shipping zero renderer tokens (baseline skew with no error), eval metric filtering counts judge traces as policy traces, and saved records markedtrainable: truefrom another model would poison later SFT-from-traces.Design notes:
TrainClient(that's what carries renderer tokens), so "refuse when training, demote when measuring" needs no wire or config surface.setup()— the only site that declarestrainable(AgentConfigdeliberately has no such field), so standing is final; beforerun()— no tokens burn on a doomed training episode, and eval traces are stamped with corrected standing from the first token.clientis checked as "pinned at all", since_episode_agentshands the run's client to every unpinned agent.Companion prime-rl PR: PrimeIntellect-ai/prime-rl#3108 (gates group-relative advantages to a single trainable agent — the consumer half of the invariant).
Validation
Throwaway probe over the
duet-v1fixture (no model calls; standing reported by a sentinel raised at the top ofrun()), both run types:model/clientpins demote beforerun()(one warning across 3 episodes); an env that already declares the agent untrainable is untouched, no warningValueError: trainable agent 'a' doesn't use the trainable model ('run-model') …; unpinned and declared-untrainable envs reachrun()(
ruff check/formatonverifiers/v1/env.py: the import-sort and reformat complaints are pre-existing onmainwith the locked ruff 0.15.21; this diff's lines are clean and no unrelated churn is included.)🤖 Generated with Claude Code