Skip to content

feat(orchestrator): add trace support on algorithm abstraction#2857

Draft
willccbb wants to merge 228 commits into
mainfrom
feat/trace-algorithm-abstraction-base
Draft

feat(orchestrator): add trace support on algorithm abstraction#2857
willccbb wants to merge 228 commits into
mainfrom
feat/trace-algorithm-abstraction-base

Conversation

@willccbb

@willccbb willccbb commented Jun 23, 2026

Copy link
Copy Markdown
Member

Summary

Adds the trace-native env/orchestrator/training base layer on top of feat/algorithm-abstraction, while intentionally preserving the existing algorithm runtime shape. This is the base branch for #2842.

Current scope:

  • carries verifiers v1 traces through the orchestrator as typed rollout objects
  • makes EnvConfig inherit the verifiers v1 env-server config surface while prime-rl keeps orchestration-only fields such as env name, address, ratio, and retry defaults
  • keeps v0 envs behind the narrow legacy bridge through id + args
  • converts traces into trainer samples from branch token/logprob/mask data
  • updates sinks, filters, metrics, monitors, and batching to read trace-native rollout fields
  • keeps the old algorithm runtime in place; the registry/advantage rewrite is isolated to feat(orchestrator): use trace algorithm registry #2842
  • pins verifiers v1 and installs the bundled harnesses / tasksets plugin packages in the root prime-rl environment; the slim prime-rl-configs package depends only on verifiers config types

Deliberately out of scope here:

  • no new demo config tree
  • no deps/prime submodule path
  • no inference-server monkeypatch or template rewrites
  • no trace algorithm registry rewrite

Stack

This is PR 1 of 2:

  1. feat(orchestrator): add trace support on algorithm abstraction #2857: trace support into feat/algorithm-abstraction
  2. feat(orchestrator): use trace algorithm registry #2842: trace algorithm registry rewrite into this branch

The branch should stay focused on trace/v1 support. Algorithm API replacement belongs in #2842.

Validation

  • UV_CACHE_DIR=/tmp/uv-cache uv run pytest 'tests/unit/test_configs.py::test_load_configs[configs/rlm_swe/qwen35_4b.toml]' -q -> pass
  • UV_CACHE_DIR=/tmp/uv-cache uv run pytest 'tests/unit/test_configs.py::test_load_configs[examples/glm5_pd_disag/rl.toml]' -q -> pass
  • UV_CACHE_DIR=/tmp/uv-cache uv run pytest 'tests/unit/test_configs.py::test_load_configs[configs/general_agent/rl_qwen3_30b_a3b.toml]' -q -> pass
  • UV_CACHE_DIR=/tmp/uv-cache uv lock --check -> pass
  • commit hooks: ruff check and ruff format passed on cleanup commits
  • Earlier on this stack, before the cleanup/narrowing pass: targeted unit suites passed (254 passed, 1 skipped on the base branch; 69 passed for the focused algorithm/batch subset)

Note

High Risk
Large orchestrator/env-server boundary refactor touching rollout lifecycle, config migration, and launcher process topology; incorrect trace→sample conversion or legacy bridge behavior would silently break training data.

Overview
Replaces v0 RolloutOutput / trajectory-step plumbing with verifiers v1 Trace objects end-to-end, while keeping the existing algorithm hook shape (RolloutView, scoring stages) for the follow-up registry PR.

EnvConfig now subclasses verifiers.v1.EnvServerConfig (taskset, pool, timeouts) with prime-rl fields (address, ratio, retries) and a v0 legacy bridge via id + args; num_workers migrates to pool. Training/eval sampling uses task_idx over server-reported num_tasks instead of loading datasets in the orchestrator. Env servers run through serve_env / EnvClient; the RL launcher can spawn one env-server per env on fixed ports (env_server_base_port) and writes per-env TOMLs before subconfigs.

Rollouts unify as Rollout(vf.Trace) with train/eval metadata; the dispatcher, sinks, filters, and metrics read trace fields (has_error, num_turns, completion_len, metrics) instead of raw["trajectory"]. trace_to_samples builds TrainingSamples per trace branch (mask/logprobs on flat tokens), replacing tokenizer backfill, interleaving, and image offload in the train sink. Echo and OPSD scoring paths are updated for branch/node traces; echo custom filters are rejected on v1.

Deps: root pins verifiers>=0.1.15.dev357 plus harnesses / tasksets; slim prime-rl-configs adds verifiers as a pydantic-only dep (CI no longer treats verifiers as a forbidden heavy import). Logging/docs use flatter logs/envs/{train,eval}/{name}.log. Trainer packing gains multimodal-aware truncation when sequences exceed seq_len.

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

mikasenghaas and others added 30 commits June 8, 2026 17:05
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Points the submodule at the vf-nano EnvServer branch so the orchestrator can
build on the env-server abstraction.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switch prime-rl's env path to vf-nano: the orchestrator spawns a vf-nano
EnvServer per env (it never loads an environment), dispatches rollouts by task
index, and trains on the returned Trace dicts (branches + renderer tokens).

- pyproject: dep verifiers -> vf-nano; drop v1/research env packages; only the
  vf-nano reverse-text example; override out the transitive v1 verifiers (pulled
  by the prime CLI) so it can't shadow vf-nano's `verifiers` package; add orjson
  /pandas/msgspec (were transitive via verifiers).
- EnvConfig inherits vf-nano's swappable agent/runtime (+ max_turns).
- envs.py: spawn EnvServer child + EnvClient, info() for num_tasks/group-scoring,
  dispatch by task_idx, adapt Trace -> RolloutOutput-shaped dict.
- trajectories.py: trace_to_samples (one sample per Trace branch) + trace_to_output.
- train_source: index sampling; client pool builds vf-nano ClientConfig; lag
  monitor vendored; env-server entrypoint repointed; ~14 files retyped off
  vf.RolloutOutput / vf.ClientConfig.
- configs/debug/vf_nano_reverse_text.toml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…er config)

- trace_to_samples stitches each Trace branch's tokens into one TrainingSample
  (prompt = branch start, then each turn's new context [masked] + generated
  tokens [trained]); drop the RolloutOutput adapter — read the Trace's native
  fields directly (reward, error{type,message}, timing generation/scoring,
  num_turns, branches).
- envs returns the raw Trace; eval_sink / train_sink / dispatcher / metrics /
  orchestrator read native Trace fields (no token_usage/completion/timing.total).
- client pool forwards the shared renderers.RendererConfig to the env server's
  renderer client (so it uses qwen3, not the tool-less default fallback).
- debug config: tool_call_parser=hermes (vLLM accepts the agent's tools),
  max_steps=20.
- bump deps/vf-nano.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o timeout)

- Env.run_rollout/run_group pass the vf-nano ClientConfig object and a
  SamplingConfig (built from the env's sampling args) directly — no model_dump,
  no per-rollout timeout forwarded to the server.
- debug config: max_steps=20.
- bump deps/vf-nano (typed env-server RPC).
The env server returns a Trace minus its derived fields; the orchestrator resolves
the env's Task subclass (from config.id) and validates the wire dict into a strict
Trace[EnvTask], so the whole orchestrator works with a real, typed vf.Trace —
typed task fields included (e.g. task.answer), nothing subscriptable.

- envs.py: resolve_task_type(env_id); run_rollout/run_group validate -> Trace[EnvTask].
- trajectories/types/dispatcher/train_sink/eval_sink/metrics/filters/advantage/utils
  /orchestrator: attribute access on the typed Trace (reward, error{type,message},
  branches, timing.<span>.duration, num_turns, ...); derived fields recompute on the
  consumer.
- Task/Trace/TimeSpan stay strict (StrictBaseModel) — no extra=ignore anywhere.
- bump deps/vf-nano.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The orchestrator spawns the env server, so request the serve extra
(zmq/msgpack) explicitly now that vf-nano keeps them out of core.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`from __future__ import annotations` already defers all annotations to strings,
so the quotes + `# noqa: F821` on the TYPE_CHECKING-only `vf.Trace` / `TrainRollout`
annotations are unnecessary (no import cycle — verifiers.nano never imports prime_rl).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The field holds a typed vf.Trace, so `trace` reads truer than `raw` (which
suggested an unparsed dict). Renames the field + every `.raw` access, the
`emit_rollout(trace=...)` param/kwarg, the to_dict field filter, and the
dispatcher cancel-path locals.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Drop the FinishedRollout proxy properties (error/reward/is_truncated and the
  example_id field); consumers now read r.trace.{reward,is_truncated,task.idx,...}
  directly. The trace is the single source of truth.
- Use vf.Trace.has_error for existence checks instead of `.error is not None`.
- Replace the prime-rl trace_* token-length utils with vf.Trace.{completion_len,
  total_tokens,has_response} (now on the trace); keep trace_to_samples.
- Carry task_idx end-to-end (GroupState.task_idx, env.run_rollout/run_group(task_idx),
  source dict key) instead of the example/example_id dict carrier; identity comes
  off trace.task.idx.
- Mark the local-package env arrangement as a temporary/experimental TODO.
- Move the debug config to configs/debug/nano/reverse_text.toml.
- Bump deps/vf-nano (Trace/Turn accessors).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- The env server binds tcp://127.0.0.1:0 and reports its concrete address back
  over a queue; the orchestrator connects to that. Removes _get_free_port and its
  TOCTOU race (the OS assigns the port atomically).
- A spawned server has already bound + loaded by the time it reports its address,
  so the untimed info() is enough — only poll wait_for_server_startup for an
  external (config.address) server, which has no spawn handshake.
- Bump deps/vf-nano (port report + Trace/Branch token-length accessors).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Task-subclass introspection now lives in vf-nano (vf.task_type); drop the
prime-rl copy and build the typed Trace via vf.Trace[vf.task_type(env_id)]. Bump
deps/vf-nano.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SFT trains on a teacher served over the chat client, which returns no token ids,
so the trace's turns have tokens=None and trace_to_samples yields nothing. Restore
backfill: for each tokenless turn, render its prompt + assistant response with the
student chat template and split on the longest common prefix to fill TurnTokens
(masks/logprobs come from trace_to_samples). train_sink.process_rollout backfills
when any turn lacks tokens, before building samples.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
drop_group's error_rollout_output calls omitted the required task_idx, so an
off-policy cancel (on_new_version) raised TypeError. Use the group's task_idx
(or -1 when the group is already gone), mirroring handle_completed_rollout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- envs.py: EnvClient now returns Trace[WireTask]; upgrade to this env's real Task
  subclass via self.trace_type.model_validate(wire.to_wire()).
- dispatcher.py: drop the error_rollout_output helper — inline the synthetic error
  Trace at each call site using vf.Error's field names (type/message/traceback); the
  task-exception path carries a real traceback, cancels/empty-trajectory carry none.
- Bump deps/vf-nano.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nical

- Spawned env servers now route their output (logging + subprocess-runtime output)
  to <output_dir>/logs/envs/<name>.log via a _run_env_server wrapper that redirects
  stdout/stderr and sets up logging in the child. Previously the orchestrator-spawned
  server logged nowhere.
- Debug config: batch_size 16->128, group_size 8->16, eval num_examples 8->128
  (interval=1), matching configs/debug/training_modes/rl.toml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The orchestrator already passes a train/eval-split log_dir (.../logs/envs/train,
.../logs/envs/eval), so _spawn must drop the file directly under it
(<log_dir>/<name>.log) rather than re-adding an envs/ subdir — which had buried the
train/eval split under logs/envs/<kind>/envs/<name>.log.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Instead of the orchestrator sidecar-spawning each env server as an mp child, the
rl launcher now spawns one `env-server` process per env (train + eval), each on a
free port, with output to logs/envs/{kind}/{name}.log and a crash monitor — same
model as inference/trainer. It sets env.address in the orchestrator config so the
orchestrator attaches (its existing external path) instead of spawning. Envs that
already set address (user-managed external server) are left alone; the orchestrator's
mp sidecar stays as the fallback for running `orchestrator` directly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add RLConfig.env_server_base_port (default 5000); the i-th launcher-managed env binds
base_port + i. Drops the get_free_port dependency in the launcher.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Train envs bind base_port + i; eval envs bind base_port + ENV_SERVER_KIND_STRIDE + i
(stride 1000), so each kind has headroom for many envs without the blocks colliding
(was a single running index — train and eval sat adjacent).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- env_server entrypoint: intercept vf-nano stdlib logging so the server's own logs
  (EnvServer up, request failures) land in logs/envs/<kind>/<name>.log — previously
  only loguru output was captured, swallowing them.
- envs.py: close the address-handoff mp.Queue after use (no resource_tracker
  leaked-semaphore warning on the sidecar path).
- configs/debug/nano/reverse_text.toml: drop the eval block, mirroring
  examples/reverse_text/rl.toml (train-only smoke; eval path validated separately).
- bump deps/vf-nano (serve/types docstring trim).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…irectly

The I/O boundary (save_rollouts + monitor sample tables) now dumps the typed
vf.Trace itself (r.trace.model_dump(mode="json")) instead of a Trace+metadata
merge — the on-disk rollout is just the trace.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
vf-nano renamed its rollout-driver abstraction Agent -> Harness. Update the
integration: EnvConfig.agent -> harness (HarnessConfig/DefaultHarnessConfig);
env.run_rollout/run_group spawn forwards harness_config; the env-server entrypoint
passes harness_config/harness_timeout; debug config uses `harness = {...}`. Bump
deps/vf-nano to the renamed branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
hallerite and others added 16 commits June 18, 2026 21:52
"advantage" was too general for a component that also fixes loss routing
and the model role, so flatten the `algo` bundle into a discriminated union
keyed on `type`: `[orchestrator.algo] type = "grpo"` instead of
`[orchestrator.algo.advantage] type = ...`.

- AlgorithmConfig is the union; each *AlgorithmConfig variant carries `type`,
  its params, and the shared sampling/teacher fields (BaseAlgorithmConfig).
- `algo.teacher` shorthand preserved (folds into `model` for opd/opsd,
  `sampling.source` for sft); `algo.model` is opd/opsd's direct field.
- Deleted the unused `advantage = {}` env/top-level shorthand + fold validator.
- Runtime: Algorithm takes the algo config (self.config); build_algorithm
  dispatches on algo.type. Migrated 12 TOMLs, docs, and unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump deps/verifiers 7bb546c2 -> b8fad841: PrimeIntellect-ai/verifiers#1753 shows per-reward/
metric running means under the eval progress bar. Eval CLI dashboard + format helper only -
no training/wire surface, no prime-rl edits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…debug logs)

Bump deps/verifiers b8fad841 -> cd205eee:
- PrimeIntellect-ai/verifiers#1751: type v1 rollout errors by boundary (RolloutError +
  Provider/Harness/Toolset/User/Sandbox/Taskset/Interception types, classified via boundary()).
  The trace Error record (type/message/traceback) and stop_condition values are unchanged, so
  prime-rl's consumed surface (vf.Error, errors/stop_condition, 'Cancelled'/'prompt_too_long')
  is intact.
- PrimeIntellect-ai/verifiers#1754: INFO interception up/down + debug-log intercepted routes.

Verified: reverse-text RL smoke (2 steps) trains clean (Error 0.0%, Training finished).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use the canonical v1 Trace.capture_error(exc) for the dispatcher's caught-exception path
instead of hand-building a vf.Error record + stop_condition - same result (type=exc class
name, message, traceback, stop_condition='error') but routed through the framework's recorder,
so it picks up the #1751 RolloutError boundary types when one escapes. The synthetic Cancelled/
EmptyTrajectory markers stay explicit vf.Error records (not exceptions; the sink/metrics branch
on those exact type strings).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…RM fix)

Bump deps/verifiers fedfa570 -> 2474b054, moving off the pre-merge fix/worker-sigterm-teardown
branch commit onto the feat/nano-as-v1 tip:
- PrimeIntellect-ai/verifiers#1755 (merged): swallow SIGTERM KeyboardInterrupt in spawned
  env-server workers.
- PrimeIntellect-ai/verifiers#1756: SDKs own retries; drop framework per-call model/runtime
  retries (removes RetryingClient/RetryingRuntime/CallRetryConfig). RetryConfig + the rollout
  retry (RolloutRetryConfig) are kept.

prime-rl imports none of the removed retry names and never references RetryConfig/.model/.runtime,
so no edits. Verified: config validates + reverse-text RL smoke (2 steps) trains clean
(Error 0.0%, Training finished).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…x channel retries)

Bump deps/verifiers 2474b054 -> e002e80d:
- PrimeIntellect-ai/verifiers#1758: keep the interception endpoint out of agent-inheritable env.
- PrimeIntellect-ai/verifiers#1759: retry in-sandbox /state + /task channel fetches; name us in
  the prime expose error.

Internal to harnesses/runtimes/mcp - no prime-rl serve/client/trace surface, no edits. Verified:
reverse-text RL smoke (2 steps, default harness + subprocess runtime) trains clean (Error 0.0%,
Training finished).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump deps/verifiers e002e80d -> 8c9f284b: PrimeIntellect-ai/verifiers#1761 makes
ModalRuntime.read resolve relative paths and stops retrying the tail tool logs. Modal-runtime
internal only - no prime-rl surface, no edits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
057f0087 (renderers-v0.1.8.dev40-2) fails a fresh hatch-vcs editable build
("0.1.8.dev40 can't be bumped"), so only grandfathered venvs built. dev49 is an
exact tag → builds cleanly, unblocking fresh venvs (e.g. running from /shared).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The SlurmConfig default partition "cluster" doesn't exist on this cluster (only
"all"), so generated sbatches failed to schedule. Set partition=all in the
scaleswe + hendrycks-sanity sources. Add scaleswe's prime-sandbox cleanup as a
pre_run_command (runs on the head node before launch) instead of editing the
sbatch template.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Source had num_train_nodes=1/num_infer_nodes=1 (the unconfirmed 30B-A3B starting
point); the run that reached step 36 used 2+2. A single train node OOMs the
35B-A3B trainer. Restore 2 train + 2 infer nodes (32 GPUs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switch the hendrycks-sanity train + eval envs from subprocess to modal (offloads
scoring to modal sandboxes), cap max_steps at 200 (was 5000), rename job to
hendrycks-modal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…malformed-json tool-args, container-test fix)

Brings in feat/nano-as-v1 #1762 (mark swebench_verified_v1 container-only),
#1763 (feed malformed tool-call JSON back to the model instead of crashing the
rollout), #1765 (pin swebench_verified_v1 task workdir to /testbed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tasks)

Switch the scaleswe train harness rlm -> mini-swe-agent (prime sandbox), and re-add a
SWE-Bench-Verified online eval: mini-swe-agent on the prime runtime, use_prime_registry=true,
first 100 tasks (num_examples=100, group_size=1), interval 20, rollout timeout 3600s.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/prime-rl-configs/src/prime_rl/configs/orchestrator.py
Comment thread packages/prime-rl-configs/src/prime_rl/configs/orchestrator.py
port = config.env_server_base_port + kind_index * ENV_SERVER_KIND_STRIDE + i
env.address = f"tcp://127.0.0.1:{port}"
env_dict = {
k: v for k, v in env.model_dump(mode="json", exclude_none=True).items() if k in EnvConfig.model_fields

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing EnvConfig import

High Severity

setup_env_servers filters env fields with EnvConfig.model_fields, but EnvConfig is never imported in this module, so a local uv run rl launch raises NameError when env servers are configured.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5925104. Configure here.

Comment thread packages/prime-rl-configs/src/prime_rl/configs/orchestrator.py Outdated
Comment thread packages/prime-rl-configs/src/prime_rl/configs/orchestrator.py
Comment thread src/prime_rl/inference/vllm/server.py
if self.renderer is None:
result["renderer"] = "None"
return result
serializes."""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MITO renderer lost on save

Medium Severity

Removing the wrap serializer means orchestrator.renderer = None (MITO) no longer round-trips when subconfigs are written with exclude_none=True. The renderer key disappears from TOML and reload applies the default AutoRendererConfig(), switching MITO runs back to renderer-backed tokenization after resume or control-dir dumps.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9cbbb53. Configure here.

env.num_workers = self.num_workers
if "max_retries" not in env.model_fields_set:
env.max_retries = self.max_retries
# Resolve auto num_workers now that num_examples and group_size are set

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Breaking config lacks changelog

Low Severity

This PR removes or renames orchestrator env worker settings (num_workerspool), drops default train/eval env entries, and requires env-server configs to specify env explicitly, but CHANGELOG.md is not updated with migration notes for those breaking config surfaces.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by project rule: BugBot Instructions

Reviewed by Cursor Bugbot for commit 9cbbb53. Configure here.


@property
def tool_response_len(self) -> int:
return sum(len(node.token_ids) for node in self._rollout.nodes if getattr(node.message, "role", None) == "tool")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Length penalty ignores harness metric

Medium Severity

Token length penalty now sums raw tool-message node tokens via RolloutView.tool_response_len, but the config still documents harness metrics like *_total_tool_response_tokens. Environments that only populate the deduped metric (not per-node tool spans) get zero tool cost and incorrect advantage shaping.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9cbbb53. Configure here.

Comment thread src/prime_rl/trainer/batch.py
"env": r.raw["timing"]["env"]["duration"],
"scoring": r.raw["timing"]["scoring"]["duration"],
"overhead": r.raw["timing"]["overhead"],
"total": r.timing.scoring.end - r.timing.start if r.timing.scoring.end else 0.0,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Rollout timing total zeroed

Low Severity

timing_df sets total to scoring.end - timing.start only when scoring.end is truthy; otherwise it records 0.0. Rollouts without a populated scoring end timestamp report zero wall time in dashboards despite nonzero setup or generation durations.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9cbbb53. Configure here.

@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 6 total unresolved issues (including 5 from previous reviews).

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

Comment thread src/prime_rl/orchestrator/orchestrator.py
@hallerite
hallerite force-pushed the feat/algorithm-abstraction branch from 87c38c7 to d33b3f4 Compare June 24, 2026 05:56
Base automatically changed from feat/algorithm-abstraction to main June 27, 2026 19:15
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.

5 participants