Skip to content

fix(v1): enforce trainable standing against the trainable model context#2101

Draft
hallerite wants to merge 1 commit into
mainfrom
fix/assert-trainable-seat-is-run-ctx
Draft

fix(v1): enforce trainable standing against the trainable model context#2101
hallerite wants to merge 1 commit into
mainfrom
fix/assert-trainable-seat-is-run-ctx

Conversation

@hallerite

@hallerite hallerite commented Jul 22, 2026

Copy link
Copy Markdown
Member

What

Env.run_episode now resolves trainable standing right after setup() and before any agent runs. One model context is trainable — the run's ctx — and every trainable agent must point to it (the same model, no pinned client). The run's client decides what divergence means:

  • Training run (ctx.client is a TrainClient — its tokens exist to be trained on): a divergent trainable agent is a config error; the episode fails loudly.
  • Any other run (eval): the agent is demoted to untrainable, with a warning fired 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 message names the one-line fix: agents.<name>.trainable = False in the env's setup().

Why

AgentInfo.trainable means "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 stamped trainable: 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 marked trainable: true from another model would poison later SFT-from-traces.

Design notes:

  • The client type is the run's purpose, not a new knob: trainable tokens only exist when sampled through a TrainClient (that's what carries renderer tokens), so "refuse when training, demote when measuring" needs no wire or config surface.
  • After setup() — the only site that declares trainable (AgentConfig deliberately has no such field), so standing is final; before run() — no tokens burn on a doomed training episode, and eval traces are stamped with corrected standing from the first token.
  • Compared on the resolved config: an agent explicitly pinning the run's own model string stays trainable; client is checked as "pinned at all", since _episode_agents hands 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-v1 fixture (no model calls; standing reported by a sentinel raised at the top of run()), both run types:

  • eval ctx: unpinned agent stays trainable; model/client pins demote before run() (one warning across 3 episodes); an env that already declares the agent untrainable is untouched, no warning
  • train ctx: the same pins fail the episode with ValueError: trainable agent 'a' doesn't use the trainable model ('run-model') …; unpinned and declared-untrainable envs reach run()

(ruff check / format on verifiers/v1/env.py: the import-sort and reformat complaints are pre-existing on main with the locked ruff 0.15.21; this diff's lines are clean and no unrelated churn is included.)

🤖 Generated with Claude Code

@hallerite
hallerite force-pushed the fix/assert-trainable-seat-is-run-ctx branch from d82c0b8 to c19cf2e Compare July 22, 2026 13:10
@hallerite hallerite changed the title fix(v1): refuse a trainable seat that isn't the run's model context fix(v1): refuse a trainable agent that isn't the run's model context Jul 22, 2026
@hallerite
hallerite force-pushed the fix/assert-trainable-seat-is-run-ctx branch from c19cf2e to cae6562 Compare July 22, 2026 13:12
@hallerite
hallerite marked this pull request as ready for review July 22, 2026 13:25
@macroscopeapp

macroscopeapp Bot commented Jul 22, 2026

Copy link
Copy Markdown

Approvability

Verdict: 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.

@hallerite
hallerite force-pushed the fix/assert-trainable-seat-is-run-ctx branch from cae6562 to e605132 Compare July 22, 2026 13:38
@hallerite hallerite changed the title fix(v1): refuse a trainable agent that isn't the run's model context fix(v1): demote a trainable agent that doesn't use the trainable model Jul 22, 2026

@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.

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 e605132ac5583fd3037f9148df90bac389cab97f. Configure here.

Comment thread verifiers/v1/env.py
ctx.model,
name,
type(self).__name__,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e605132ac5583fd3037f9148df90bac389cab97f. Configure here.

@hallerite
hallerite marked this pull request as draft July 22, 2026 13:44
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>
@hallerite
hallerite force-pushed the fix/assert-trainable-seat-is-run-ctx branch from e605132 to 4527f61 Compare July 22, 2026 13:47
@hallerite hallerite changed the title fix(v1): demote a trainable agent that doesn't use the trainable model fix(v1): enforce trainable standing against the trainable model context Jul 22, 2026
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