Skip to content

feat: keep training arrays with the env servers - #4196

Merged
mikasenghaas merged 18 commits into
mainfrom
feat/payload-refs
Oct 28, 2026
Merged

mikasenghaas merged 18 commits into
mainfrom
feat/payload-refs

Conversation

@mikasenghaas

@mikasenghaas mikasenghaas commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

Summary

Router-replay rows, the score-centering top-k head and sampling masks travel today from the env servers to the orchestrator inline, and the orchestrator packs them into the trainer batch. At high concurrency with long contexts, the orchestrator node then holds terabytes of per-token arrays that only the trainer reads. Now they never leave the env servers inline: the train env servers keep them, and references are the only way they reach the trainer. There is no config and no inline path:

  • Env servers keep the arrays (transports/payload.py). vLLM still returns them inline, so the env server keeps full control over the response. When a train episode finishes, each env-server worker (verifiers' set_episode_hook, PayloadHook.keep) encodes every trainable branch's arrays (encode_branch_arrays), keeps the blob, serves it over HTTP (GET <url>/<id>, a stdlib threaded server per worker on the env server's bind host, loopback by default, DELETE to free). It leaves the URL and, per branch, the blob id, the kept fields and the routing shape in trace.info["prime_rl_payload"]. Env-server workers import no torch through this path. Episode streams never carry the arrays (verifiers#2934).
  • The orchestrator packs references, never arrays. A sample carries a PayloadSlice (url, id, kept token count, fields); truncation shortens the count. A micro batch lists one reference per sequence, so samples with and without arrays pack together; samples with and without routing do not (orchestrator/trajectories.py, orchestrator/batch.py). Padding dummies reference only routing rows.
  • Trainer ranks fetch and assemble. Before each step, every rank fetches its micro batches' blobs in parallel and assembles them (pack_token_arrays, pad_token_arrays: cut each to its sample, concatenate in packing order, pad; a sample without arrays gets empty rows), so the tensor conversion is unchanged (trainer/rl/data.py, transports/payload.py).
  • Strict accounting, no sweep. Every kept array is deleted exactly once (DELETE <url>/<id>), by whoever holds it last: the trainer, once every rank fetched a step's arrays (after the world all-reduce that agrees on failed fetches, one rank per data rank deletes); the orchestrator, for whatever it receives but never ships: the train sink for filtered and dropped traces and groups, untrained branches, pruned samples, samples that staleness masking voids, and batches and queues discarded at shutdown; the eval sink for every eval episode; the dispatcher for episodes that returned after their group was dropped; the env server, for episodes it kept that never reached the orchestrator, including runs the client abandoned after the reply (PayloadHook.discard, called by verifiers' delivery contract).
  • Missing arrays fail loudly. Each fetch retries before giving up, and bypasses HTTP proxies. A step where no micro batch's arrays can be fetched anywhere stops the run. A micro batch whose arrays still cannot be fetched is dropped from the loss on every rank that trains it (one all-reduce), never trained on zeros, and counted in payload/dropped_micro_batches (summed over data ranks). Its routing keeps the model's shape and repeats real rows, so the forward pass still routes like real data.
  • Every env server keeps its arrays; eval deletes them. prime-rl's env-server entrypoint always installs the hook, with no config. Eval never trains, so EvalSink.add deletes the arrays of every eval episode it receives, in the orchestrator and in the standalone eval entrypoint (orchestrator/eval_sink.py).
  • Env-server replicas on every inference node. On multi-node SLURM runs with inference nodes, every inference node runs a replica of every launcher-managed env server, bound to the node address. Each replica publishes its address to envs/<split>/<name>.<node>.address. The orchestrator connects to all replicas and sends each run to the replica with the fewest runs in flight (orchestrator/envs.py). So env-server memory and CPU spread over all inference nodes, even for a single env. The launcher sets orchestrator.env_server_replicas to the number of inference nodes.

Builds on PrimeIntellect-ai/verifiers#2934 (merged): episode streams without training arrays, and set_episode_hook with its keep/discard contract. deps/verifiers points at its merge commit on verifiers main (60290d436).

Breaking

  • Env server placement. On multi-node SLURM runs with inference nodes, launcher-managed env servers now run as one replica per inference node instead of one server next to the orchestrator. Env-server logs move to logs/envs/<split>/<name>.<node>.log. No option restores the old placement. To pin a source's env server elsewhere, run it yourself and point the source at it with serve.address.
  • Train env servers must run prime-rl's env-server entrypoint. Episode streams no longer carry training arrays, so a train source served any other way (serve.address pointing at a plain verifiers server) delivers none. Runs that need sampling masks or top-k heads stop with an error saying so.
  • Per-server limits apply per replica. serve.max_concurrent, the worker pool and sandbox limits of a launcher-managed env server now apply to each replica, so a source's total rises with the number of inference nodes. Migration: divide a limit that must hold run-wide by the number of inference nodes.
  • Persisted batches can't be replayed. With the filesystem rollout transport, a step file now holds references, and the trainer deletes the arrays once it has fetched them. A trainer that re-reads an old step file after a restart finds the arrays gone and drops every micro batch (payload/dropped_micro_batches).
  • The sampling_support/size metric is removed. Traces reach the orchestrator without sampling masks, so nothing feeds it.

Verification

Qwen3-30B-A3B-Thinking on i3_math with router replay (48 layers × 8 experts, uint8) and IcePop score centering (top-128 head), batch 128 × group 8, up to 1,024 rollouts in flight, 16k-token completions, H200.

Inline (main) vs by reference, 1 trainer + 1 inference node each, same config otherwise, 10 steps, measured on an earlier revision of this branch:

inline by reference
Reward (mean of steps 1–10) 0.427 0.388
Mismatch KL 4.0e-4 4.0e-4
Step time (orchestrator / trainer) 381 s / 380 s 378 s / 375 s
Orchestrator peak RSS 14.5 GiB 2.7 GiB
Dropped micro batches – 0

Per-step reward varies by ±0.07 at this batch size, so the reward gap is within about two standard errors; the trainer sees byte-identical arrays (unit test below) and the mismatch KL is unchanged.

Env-server replicas, 2 trainer + 4 inference nodes, 27 steps each. one env server vs one replica per inference node:

one env server replica per inference node
Env-server peak RSS 90.6 GiB on one node 25.6–25.9 GiB on each of 4 nodes
Peak memory used, inference nodes 220 GiB on the env-server node, 122–164 GiB elsewhere 144–158 GiB on every node
Orchestrator peak RSS 3.2 GiB 3.3 GiB
Payload fetch per trainer step (mean / max) 1.26 s / 2.30 s 0.74 s / 2.20 s
Reward (mean of steps 1–27) 0.398 0.415
Mismatch KL (mean) 3.9e-4 3.9e-4
Step time (orchestrator / trainer) 118 s / 117 s 124 s / 122 s
Dropped micro batches 0 0

The replica run continued to its 60-step limit and exited cleanly. Each replica's memory peaks early (≤ 25.9 GiB) and stays flat at ~22–23 GiB, so eager deletes keep up. Over 60 steps: no dropped micro batches, payload fetch 0.67 s mean, orchestrator peak RSS 3.3 GiB, reward mean 0.417. In both runs, most env-server memory is the math runtime's per-episode subprocesses and the pool workers; the kept arrays are about 10 GiB in total.

Transport, measured on an earlier revision of this branch: deltas into the orchestrator 8.8 GiB → 120 MiB per step, trainer batch 2.6 GiB → 78 MiB per step.

Tests: tests/unit/transports/test_payload.py: real verifiers traces go through the env-server hook, trace_to_samples, packing (truncation, padding, multi-sample bins) and the HTTP fetch, and each fetched micro batch holds exactly its branches' encoded arrays in packing order. Samples with and without routing never share a micro batch. Explicit heads, masks with sampler logprobs, plain masks and a sample without arrays survive truncation, mixed packing, padding and the trainer's tensor conversion. Also covered: the routing of a micro batch whose fetch failed (truncated sample included), a keep that fails midway, a branch whose arrays don't encode, the bind host (wildcard included), and the store and discard. tests/unit/orchestrator/test_train_sink_payloads.py: the train sink deletes exactly the arrays of filtered, dropped, stale, pruned and discarded samples and leaves only the shipped ones; the eval sink deletes those of eval episodes; the dispatcher deletes those of episodes that returned after their group was dropped. tests/unit/orchestrator/test_env_replicas.py: least-loaded routing over replicas. pytest -m "not gpu" tests/unit after merging main: 1003 passed; after the last fixes, the affected suites (orchestrator, transports, trainer, configs, eval) pass 498.

🤖 Generated with Claude Code


Note

High Risk
Changes the training data path (HTTP payload fetch, strict delete accounting, micro-batch drop on fetch failure) and multi-node env-server placement; misconfiguration or network issues can silently drop training signal or fail steps.

Overview
Training arrays (router replay, sampling masks, top-k heads) are kept on env servers and passed by reference instead of riding inline through the orchestrator and rollout transport. The env-server entrypoint installs PayloadHook, which encodes branch arrays over HTTP and stamps trace.info["prime_rl_payload"]; samples carry PayloadSlice, the packer threads payloads through micro batches, and trainer ranks fetch blobs before each step then release them after a DP-wide sync. Dropped or never-shipped arrays are deleted from train/eval sinks, the dispatcher on cancel, and when fetches fail micro batches are zeroed out of the loss (with payload/dropped_micro_batches).

Multi-node runs spread env-server load: orchestrator.env_server_replicas (auto-set to inference node count) launches one env server per inference node; the orchestrator connects to all replicas and routes episodes least-loaded first. SLURM templates move env-server startup off the orchestrator node; address files use <name>.<replica>.address.

Orchestrator batching no longer inlines slice/pack logic for routed experts and masks—that moves to transports/payload.py. Live monitor deltas keep full delta dicts; the sampling_support/size metric is removed.

Reviewed by Cursor Bugbot for commit d65bfc0. Bugbot is set up for automated code reviews on this repo. 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.

Stale Bugbot comment from a previous run.

@mikasenghaas mikasenghaas changed the title feat: pass training arrays by reference from the env servers feat: keep training arrays with the env servers Oct 21, 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.

Stale Bugbot comment from a previous run.

Comment thread src/prime_rl/transports/payload.py Resolved
Comment thread src/prime_rl/transports/payload.py Resolved
Comment thread src/prime_rl/transports/payload.py Resolved
Comment thread src/prime_rl/transports/payload.py Resolved

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

Stale Bugbot comment from a previous run.

Comment thread src/prime_rl/orchestrator/batch.py Resolved

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

Stale Bugbot comment from a previous run.

Comment thread src/prime_rl/orchestrator/envs.py Resolved
mikasenghaas added a commit that referenced this pull request Oct 22, 2026
…#4176, squashed)

Squash of #4176 (feat/icepop-sc-sampling-replay @ daff5a2) onto
rl-v1/prl, merged with the payload references of #4196 and the batch-load perf commit:
- SamplingMask keeps memoryview ids/counts (zero-copy decode) and gains memoryview logprobs.
- The trainer's head assembly merges the top-logprob and sampling-mask heads, keeping the
  row-copy fast path.
- The train sink accepts sampler logprobs inline or kept by an env server (payload reference).
- The env-server payload hook encodes the mask and head with _encode_sampler_evidence, and the
  trainer's payload assembly carries the mask's logprobs column.

Co-Authored-By: Mika Senghaas <mika@primeintellect.ai>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mikasenghaas and others added 7 commits October 23, 2026 14:00
Router-replay rows, the score-centering top-k head and sampling masks
travel today from the env servers to the orchestrator inline, and the
orchestrator packs them into the trainer batch, so the orchestrator node
holds every buffered episode's arrays.

With `[payloads]`, each env-server worker keeps a finished train
episode's per-branch arrays and serves them over HTTP; the orchestrator
packs only references, and each trainer rank fetches its micro batches'
arrays before the step and assembles them exactly as the packer does
inline. Arrays are freed by the policy version stamped on each dispatch,
and capped per worker. Micro batches whose arrays cannot be fetched are
dropped from the loss on every rank that trains them.

On multi-node runs the env servers now always run on the inference
nodes, round robin over sources, instead of next to the orchestrator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Once every rank has fetched a step's arrays (after the world all-reduce
that agrees on failed fetches), one rank per data rank deletes them from
the env servers. Drop the per-worker size cap, which threw away arrays a
pending batch still needed; the version sweep stays only for arrays no
batch ever fetches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every array an env server keeps is deleted exactly once, by whoever
holds it last: the trainer after every rank fetched it, the train sink
when it drops a sample or trace without shipping it (filtered traces
and groups, untrained branches, pruned and stale samples, shutdown),
and the env server when the episode never reached the orchestrator.
This replaces the version sweep, so dispatches no longer stamp policy
versions for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spreading sources round robin left a single env's memory on one node.
Every inference node now runs a replica of every launcher-managed env
server, each publishing its address to <name>.<node>.address, and the
orchestrator routes each run to the replica with the fewest runs in
flight. The rl launcher sets env_server_replicas to the number of
inference nodes on multi-node runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop the [payloads] block: train env servers launched by rl always
keep their episodes' training arrays, and the trainer always fetches
and frees them. The launcher marks train sources with the internal
keep_training_arrays env-server field; eval servers and servers run
by hand still send arrays inline.

Also drop deployment.orchestrator_on_inference. Env servers now run
on the inference nodes and the orchestrator holds only references, so
moving the orchestrator off the trainer node no longer frees memory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adapt references to main's micro-batch format: micro batches now carry
raw buffers, a padding count and sampling masks with sampler logprobs.

- The env-server hook and trace_to_samples share one branch encoder
  (encode_branch_arrays), so kept arrays match what main packs inline.
- The trainer assembles fetched arrays with the packer's own helpers
  (pack_token_arrays, pad_token_arrays), so mask logprobs and padding
  follow the inline path.
- The train sink deletes the arrays of samples that staleness masking
  voids, and accepts kept mask logprobs as sampler heads.

Also fix review findings in the same code:

- A micro batch whose fetch failed keeps the model's routing shape
  (carried on the reference) and repeats real rows, not zeros.
- Samples with and without routing never share a micro batch.
- The payload server is created once and advertises the bind host.
- A failing keep frees what it stored; any fetch error drops the
  micro batch instead of crashing the step.
- Env-server workers no longer import torch through this module.
- Replica address files are indexed on every multi-node run, so a run
  with one inference node starts; the dead orchestrator-side env-server
  block is gone.

Co-Authored-By: Claude Opus 5.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.

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

Comment thread src/prime_rl/orchestrator/batch.py Resolved
mikasenghaas and others added 4 commits October 25, 2026 14:27
- Truncation copies the whole reference, so a truncated sample's
  failed fetch keeps the routing shape instead of crashing the step.
- References record fields per branch, so the sink's sampling-mask
  and top-logprob guards hold for each branch.
- The payload server binds the env server's own host and defaults to
  loopback, so local runs need no resolvable hostname and expose
  nothing beyond what the env server does.
- References live under a namespaced info key, so an env's own
  info["payload"] cannot collide with them.
- A batch held back at max_steps releases its arrays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Env servers no longer stream training arrays (verifiers drops them from
every delta), so references are the only path to the trainer:

- TrainingSample carries a reference, never arrays, and the packer
  stops slicing and concatenating them. Micro batches list one
  reference per sequence, so samples with and without arrays share
  micro batches; the trainer gives the ones without empty rows.
- The encoders and array helpers move to transports/payload.py, shared
  by the env-server hook and the trainer's assembly and free of torch.
- References name the fields they hold; padding dummies keep only
  routing, so the trainer stops refilling their masks and heads.
- The sink's mask and head guards check references only and tell a
  user-run train env server to set keep_training_arrays.
- The sampling-support metric, which read streamed masks, is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread docs/inference.md Resolved
mikasenghaas and others added 3 commits October 26, 2026 16:49
Drop orchestrator_on_inference from main's new GLM-5.3 Flash SWE
example: this branch removes the field, and the orchestrator no longer
holds training arrays, so it fits next to the trainer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address review: drop keep_training_arrays, so every env server
launched with prime-rl's env-server entrypoint keeps its episodes'
arrays, and the eval sink deletes those of eval episodes since eval
never trains on them. Keep deployment.orchestrator_on_inference as on
main, and leave docs/inference.md unchanged. Pin verifiers to the
late-cancel fixes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The dispatcher deletes the arrays of an episode that returned but is
  never emitted: a claimed task that had already finished, or a run
  cancelled during session cleanup.
- An env server bound to a wildcard address binds the payload server to
  all interfaces and advertises a routable address, not the hostname's
  (often 127.0.1.1).
- A branch whose arrays fail to encode is skipped with a warning
  instead of failing the episode; a train run that needs them still
  stops at the train sink's guard.
- The payload server accepts a deep backlog, and fetches retry before a
  micro batch is dropped from the loss.
- Packing concatenates each array once (b"".join) instead of growing
  bytes per sample, and assembly checks each reference's token count.
- Pin verifiers to the pool late-cancel fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mikasenghaas added a commit that referenced this pull request Oct 27, 2026
Env.run takes the group's sampling, and the sink reads each env's sampling at the work's step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
deps/verifiers moves to verifiers#2934 merged with verifiers main (5e36059); deps/renderers back to
main's b6ab8c7 (the previous merge of main had set it to dc0b492).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mikasenghaas added a commit to PrimeIntellect-ai/verifiers that referenced this pull request Oct 28, 2026
## Summary

- **Training arrays never stream.** Nodes' per-token training arrays
(`TRAINING_ARRAY_FIELDS`: routing rows, sampling masks, top-k heads) are
left out of every delta, so they never cross the env server → client
hop. The routing-row repairs, which only patched rows already streamed,
are gone on both sides.
- **`set_episode_hook(hook)`** (`verifiers.v1.serve`) is the only way
the arrays leave a worker. It installs an `EpisodeHook` with two
methods:
- `keep(episode, sampling)` runs off the event loop on each finished
episode, before its final flush. A host keeps the arrays itself and
leaves references in `trace.info`; what it writes to the traces streams
with the final flush.
- `discard(episode)` frees what `keep` kept for an episode that was not
delivered.
- **Strict contract:** every episode passed to `keep` is either
delivered in its run's successful reply or handed to `discard`, exactly
once and never while `keep` still runs. This covers a `keep` that
raises, a cancel that lands during or after `keep` (shutdown included),
a reply that fails to encode or send, and a client that cancels the run
after the reply left, including while it assembles or validates the
reply: the client always sends a cancel, the pool broker forwards a
cancel to the worker that replied within `LATE_CANCEL_SECONDS`, and that
worker discards the kept episode. The host can then free what it keeps
without any sweep.

Used by PrimeIntellect-ai/prime-rl#4196: its train env-server workers
keep each trainable branch's arrays and serve them to the trainer, so
the orchestrator never holds them.

## Breaking

- **Deltas carry no training arrays.** A client that read
`routed_experts`, `sampling_mask` or `top_logprobs` from streamed nodes
now gets `None`. Migration: keep the arrays on the server with
`set_episode_hook` and fetch them from there.
- **`routing_repairs` deltas are gone.** `EpisodeAssembly` no longer
applies them, and no server sends them.

## Tests

`tests/unit/serve/test_delta.py` (an episode streams and reassembles
without its training arrays) and `tests/unit/serve/test_episode_hook.py`
(a failed, abandoned or finished `keep`, a cancel before or after the
reply leaves, and a cancel during the client's final decode each discard
exactly once; an expired delivery is never discarded) and
`tests/unit/serve/test_pool_cancel.py` (the broker routes a late cancel
to the worker that replied); full `tests/unit`: 239 passed.
prime-rl#4196 covers `keep` / `discard` end to end.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **High Risk**
> Breaking wire semantics for training arrays and cancel/discard
lifecycle; incorrect hook or cancel handling could leak GPU memory or
drop data trainers depend on.
> 
> **Overview**
> **Per-token training arrays no longer cross the env-server wire.**
Streamed node deltas now omit `routed_experts`, `sampling_mask`, and
`top_logprobs` (`TRAINING_ARRAY_FIELDS`), and the **`routing_repairs`**
mechanism is removed from both `DeltaStreamer` and `EpisodeAssembly`.
Clients that assemble `WireEpisode` from deltas should expect those
fields to be **`None`**.
> 
> **`set_episode_hook`** is the supported way for a host to retain
arrays on the worker: **`keep(episode, sampling)`** runs in a background
thread after rollout finishes (before the final delta flush);
**`discard(episode)`** runs when delivery fails or the client abandons
the run. The server tracks kept episodes through reply send and a
**`LATE_CANCEL_SECONDS`** window so a late cancel can still trigger
**`discard`**.
> 
> **Client and pool behavior** is tightened so abandonment is signaled
reliably: **`EnvClient.run`** reuses a stable **`request_id`** and fires
**`_cancel_run`** if decode/validation fails after the reply, or on
request cancellation; the pool broker forwards cancels to the worker
that still holds an in-flight run **or** recently replied within the
late-cancel window.
> 
> Tests now assert reassembly **without** training arrays instead of
routing-row repair/parametrized dtypes.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1cee4be. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- Macroscope's pull request summary starts here -->
<!-- Macroscope will only edit the content between these invisible
markers, and the markers themselves will not be visible in the GitHub
rendered markdown. -->
<!-- If you delete either of the start / end markers from your PR's
description, Macroscope will append its summary at the bottom of the
description. -->
> [!NOTE]
> ### Let host keep finished episodes' training arrays via episode hooks
> - Adds `EpisodeHook` with `keep(episode, sampling)` and
`discard(episode)`, plus `set_episode_hook` to configure it. When a run
finishes, the server runs `keep` in a worker thread before the final
delta flush and tracks the kept episode for cleanup.
> - Streamed node deltas now omit the `routed_experts`, `sampling_mask`,
and `top_logprobs` training arrays (see `TRAINING_ARRAY_FIELDS`), and
`routing_repairs` delta logic is removed from both streaming and
assembly ([delta.py](verifiers/v1/serve/delta.py)).
> - The client and pool support late cancellation: `EnvClient` can
cancel a run after its reply arrived, and `EnvServerPool` forwards such
cancels to the worker that handled the request within
`LATE_CANCEL_SECONDS` ([client.py](verifiers/v1/serve/client.py),
[pool.py](verifiers/v1/serve/pool.py)).
> - Cancellation and failed/unsendable replies now discard hook-kept
episode data.
> - Risk: reassembled streamed nodes no longer contain `routed_experts`
(the updated test asserts it is `None`); consumers relying on the old
streaming repair path in [test_delta.py](tests/unit/serve/test_delta.py)
behavior will need the hook to obtain training arrays.
>
> <!-- Macroscope's review summary starts here -->
>
> <sup><a href="https://app.macroscope.com">Macroscope</a> summarized
1cee4be.</sup>
> <!-- Macroscope's review summary ends here -->
>
<!-- Macroscope's pull request summary ends here -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Keep main's per-token staleness stream alongside the payload
references: samples and micro batches carry both, and padding extends
staleness while the trainer pads the fetched arrays. Point
deps/verifiers at the merged verifiers#2934 (60290d436).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hallerite
hallerite previously approved these changes Oct 28, 2026
- Stop the run when no micro batch's arrays could be fetched anywhere
  in a step, instead of training on nothing; log dropped micro batches
  across all data ranks, not only the logging rank's.
- Fetch and delete payloads without HTTP proxies: payload URLs are
  node-local, and a proxy set for downloads breaks them.
- Keep a branch's routing rows when its sampler evidence fails to
  encode; only the mask and head are dropped, with a warning.
- Check that assembled routing data matches its shape, so a short blob
  cannot shift later samples' rows.
- Delete payloads from one shared pool of daemon threads with a 10 s
  timeout, so DELETEs queued at exit never hold the process up.
- Start the payload server on first use, so pool brokers open none.
- Release only claimed tasks that are done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mikasenghaas
mikasenghaas merged commit 8acab25 into main Oct 28, 2026
11 checks passed
@mikasenghaas
mikasenghaas deleted the feat/payload-refs branch October 28, 2026 00:49
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