Skip to content

rl-trainer-start-stop: sample buffer, trainer lifecycle and scheduler, sampler/trainer islands, convertible nodes - #265

Draft
michaellchung wants to merge 46 commits into
s24-pr4-identityfrom
s24-pr3-start-stop
Draft

michaellchung wants to merge 46 commits into
s24-pr4-identityfrom
s24-pr3-start-stop

Conversation

@michaellchung

Copy link
Copy Markdown
Contributor

Stacked on PR-4. Implements openspec change rl-trainer-start-stop (spec as the last commit).

  • L3/L4: trainer lifecycle ports with fakes, SCHEDULED, elastic JOIN/LEAVE, durable sample buffer with arrival polling, real Miles path wiring through --rollout-function-path.
  • L5: merge metrics, island sync timing and syncer tape clock (L5.5–L5.7); head scheduler in the main loop; remote trainer island start/stop (L5.3c); L5.4 sampler/trainer pair in one launch.
  • L4.6: sampler-only and trainer islands over the sample buffer; no-sync buffer islands may train every parameter.
  • L7: conversion between sampler and trainer roles (L7.1–L7.3), convertible node selection by five levels, convertible pools, node shard indexes merged by the head.
  • Sky islands: trainer island binding, one-GPU colocated LoRA, sample buffer and trainer commands over the store's object API (no bucket mount).
  • yeto/rl/engine/sample_buffer.py keeps the object/JSONL index layouts and threads the convertible shard index through JsonlIndex (as in integration merge 5d75197b).

GPU evidence: L5.4 (Modal and sky), L7.4, LoRA sampler runs.

🤖 Generated with Claude Code

chowhsu and others added 30 commits October 15, 2026 15:44
RoundCutCheckpoint.save takes a final-cut trigger and LocalOnlySync passes
its final_trigger through; resume treats SCHEDULED like MANUAL
(rl-trainer-start-stop L3.2, design D3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 198bf71)
TrainerLifecycle, DurableSampleBuffer and TrainerScheduler are neutral
ports (rl-trainer-start-stop D1). DriverLifecycle builds a fresh driver per
incarnation, JOINs before restoring the last cut, holds the driver at round
boundaries, drains with the cut trigger of the stop reason (STOPPED ->
SCHEDULED, PREEMPTED -> PREEMPT) and LEAVEs with the P5 code (L3.1, L3.3,
L3.4). CPU fakes record JOIN/LEAVE and cut the fake trainer through
save_cut/restore_cut; tests run three start/stop cycles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit eeaa187)
FileSampleBuffer keeps an append-only index next to the sample payloads in
the cut storage; reopen after process exit, torn-line recovery, select,
commit, backlog statistics and polled arrival callbacks (rl-trainer-start-
stop L4.1, L4.3). The consumed watermark goes into the cut record and a
restart resets the buffer to it (L4.2). CPU fakes: a buffer-fed rollout pool
and a sampler that loads each published version (L4.4). Tests cover the
crash-after-consuming scenario and samplers following the trainer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 81b49f8)
ConsistentCut carries an optional buffer_watermark (rl-trainer-start-stop
D4); RoundCutCheckpoint takes it from a buffer_watermark callable. The
lifecycle now holds the driver after each publication, so samplers see the
newest version while the trainer waits, and commits a round's samples
through the rollout pool's optional commit_round verb.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit a51fb3a)
- elastic syncer: outer_step lists the deltas rejected since the previous
  step; every tape line gets time_unix (transfer lines too); accepted and
  carried deltas get arrival_offset_s; outer_step gets step_seconds and
  last_arrival_island. The coordinator stays clock-free: TapeClock stamps
  lines when the server flushes them, so ledger golden cases are unchanged.
- yeto/rl/engine/merge_metrics.py folds the elastic tape into per-step
  weight shares, carried share/discount and rejected share by reason;
  wandb_tape logs these once per outer step instead of per raw record.
- island side: rl_sync_timing (submit_ts, global_ready_ts, recv_seconds,
  sync_wait_seconds) for strict and elastic; full-parameter round events
  carry the same fields; decoupled remote_quorum_wait_seconds is now the
  measured blocked time instead of 0.0.
- decoupling golden: strict tape gains one rl_sync_timing event.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 766c927)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 619016a)
L5.1-L5.3 (rl-trainer-start-stop): Q_on = A_hard + 1 by default, or
min(Q_s1, A_hard + 1) with the optional alpha; stop when the backlog is
empty and idle for T_s; T_s from the last trainer_start_summary with a
configured fallback; the idle start time and the decision inputs go to the
controller journal so a restarted head continues the idle time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 2352deb)
TrainerLoop ticks S1Scheduler on the buffer backlog and the lifecycle state,
starts a new incarnation from the newest cut, trains at round boundaries,
and stops with a SCHEDULED cut and the P5 LEAVE code. Starts and stops are
journaled next to the scheduler state; after a head restart the loop
continues, numbers the next incarnation after the last one and releases an
orphaned trainer. The launcher runs it beside the fleet controller only
with --rl-trainer-scheduler s1 (default off) and refuses the switch until a
cloud trainer binding exists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 6b444d9)
Sampler islands write each finished group (whole samples with per-token
behavior log-probs and weight versions) to the sample buffer through a
rollout-function plugin; trainer islands select their rounds from the buffer
with the max_policy_age gate instead of generating. Round cuts commit and
record the consumed watermark of the rounds below the cut, and a restore
continues at it. No Miles fork change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit c34b0da)
…uler

rl-trainer-start-stop L7.1-L7.3. Neutral core in yeto/rl/engine/convertible.py:
node classes with the class rule (no spot or low-precision convertible node),
a conversion port with a fixed call order in both directions, the P0-S cut rule
for in-flight trajectories, pool rollout capacity with the last-rollout-node
guard, and a scheduler that turns S1 start/stop into conversions. Miles/SGLang
port uses the existing colocate offload/onload calls. Launcher flag
--rl-node-class refuses bad classes before launch. CPU tests with fakes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit c8a437e)
RemoteTrainerLifecycle runs each trainer incarnation on its own island:
the cloud layer launches it with the incarnation and the cut to restore
in its environment, commands (train N rounds, drain with the stop
reason's cut trigger and LEAVE code) go through a per-incarnation file in
the run's checkpoint store, and readiness, trained rounds, the consumed
watermark and the final cut come back as rl_trainer_lifecycle events on
the island's event stream. The island side (IslandTrainerAgent) wraps
the built driver in DriverLifecycle; the Miles entry runs it when
YETO_TRAINER_CONTROL=1.

The Modal binding is the default binding_factory: the launcher holds
back --rl-trainer-island, spawns one call per incarnation, cancels it at
release and checks the call ended; call ids are kept in the run dir so a
restarted head releases an orphan. The head reads the modal-volume store
through the SDK and counts the backlog from the consumed watermark the
trainer committed (running incarnation's report, else the newest cut).
Sky trainer islands are refused for now.

Scheduler S1 now stops with FINISHED when the data ended and less than
one batch is left, in any startable or training state, and treats a
partial batch as idle; the loop ends and journals it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 5ff3b06)
…ght requests, admission

MilesPublisher.load_version loads a published version into the engines and
returns the digest of their common check_weights(checksum) body;
published_checksum gives the digest of the last full publication.
MilesRolloutPool.set_admission closes/opens harness admission.
engine_requests_inflight counts running and queued engine requests.
resume_rollout onloads the weights tag directly: onload_weights is a no-op
without 'weight' in --offload-rollout-level while the release frees weights.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit d944da3)
…rsion

load_version now stamps the version under the update lock (shared helper
with _publish) and, in LoRA mode, loads the adapter into each engine's LoRA
pool before the read-back, so the checksum covers the adapter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 2a21396)
- --rl-sample-role both|sampler|trainer (default both, unchanged): neutral
  SampleBufferConfig on RLRunConfig; launcher checks and forwards the flags
  (single-island no-sync launches sharing one --rl-checkpoint-store).
- Sampler island: Miles --debug-rollout-only (no trainer model, no trainer
  GPU), BufferWriterRolloutFn writes groups; SamplerLoop loads each policy
  release and waits at a backlog cap, keeping the last version while the
  trainer is stopped; marks the data end after the last round.
- Policy releases through the store: the trainer's publisher also exports an
  HF checkpoint (actor.export_hf) per published version with its buffer read
  position; LATEST is the commit point; republish is a no-op, a rewound
  lineage is a new release. The sampler loads it under the controller update
  lock (update_weights_from_disk, stamp + read back the version).
- Groups read from the buffer are renumbered by buffer position, so group and
  sample indexes stay unique across sampler rounds and restarts.
- connect_island_ray forwards the buffer environment to Ray workers.
- Full-parameter mode only; LoRA buffer islands are refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 5fd3f3b)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit adc2417)
G-L7.4 first GPU run: the LoRA warm-up generate inside load_version ran with
the KV cache still released and crashed the SGLang scheduler. The port now
loads (load_version), onloads the KV cache, then reads the checksum
(MilesPublisher.served_checksum, which warms the LoRA adapter first).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit faef230)
- --rl-island-sample-role ISLAND:ROLE (repeatable): one launch of a sampler
  island and a trainer island over one --rl-checkpoint-store. Needs
  --rl-single-island-no-sync (no syncer); each island runs as a one-learner
  no-sync island (learner 0 of 1) with its own --rl-sample-role and only its
  own role option, while the cluster / Modal function name, tape subdir and
  volumes keep the island index. The held-back trainer island of
  --rl-trainer-scheduler is therefore never counted as a syncer learner.
- --tuning full is accepted for sampler/trainer islands only; the island
  command carries --parameter-mode full without the full-parameter sync, and
  the Miles island entry accepts full mode on a no-sync buffer island.
- run_with_trainer_loop: when the fleet (the samplers) ends, the loop sees
  data_ended and keeps ticking up to --rl-trainer-drain-seconds (default
  3600) so the trainer consumes the rest of the buffer and finishes.
- dry-run plan: per-island sample_role, held_by_trainer_scheduler, and the
  trainer_scheduler record; it runs the scheduler launch checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit e12c6f7)
--rl-trainer-start-seconds-fixed keeps the configured T_s after a start was
measured (the measured value is still journaled), and
--rl-sampler-min-round-seconds throttles the sampler island so the trainer
becomes idle and scheduler S1 can stop it more than once in a short run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 4649cc4)
The island parser accepted --parameter-mode full on a sampler/trainer
island, but run_miles refused it (expert-full tuning needs the sync) and
bound the full-parameter identity only inside the policy-sync block. Skip
the refusal for buffer islands and bind the identity for them as well.
Found by the L5.4 GPU run s24-l54-pair-20261014c.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit bd81194)
A sampler island asks Miles for --debug-rollout-only, so Miles places the
rollout engines only; the placement check refused that as a rewritten
fixed-partition placement. Skip the check when the launch asked for it.
Found by the L5.4 GPU run s24-l54-pair-20261014e.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 7174b9f)
The rollout metadata hook pushes group records to the named Ray actor the
trainer driver creates; a sampler island has no driver, so its first round
failed to look up yeto_rollout_meta. Create the sink on the sampler island
and drop the records after each round. Found by the L5.4 GPU run
s24-l54-pair-20261014f.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 3e6eca4)
Miles post-processes every round for the trainer's data-parallel layout,
which a trainer actor normally sets on the rollout executor. A sampler
island has no trainer actor, so RolloutExecutor.get raised KeyError(None).
Set DP=1 (the buffer trainer's full-parameter layout) at sampler start.
Found by the L5.4 GPU run s24-l54-pair-20261014g.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 183b724)
3e6eca4 added the method to DirMetadataSource by mistake; the sampler
island failed with AttributeError in run s24-l54-pair-20261014h.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 79280be)
created_at is int(time.time()); a run crossing a second boundary made
the stock and CompactionRL raw outputs differ (flaky in a full run).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit dce70b3)
A sampler/trainer (no outer sync) island in --parameter-mode full got the
LoRA policy state, so its first publish exported LoRA tensors and failed
with 'Megatron produced an empty or duplicate LoRA mapping' (L5.4 GPU run
s24-l54-pair-20261014i, all four incarnations). Such an island now gets
MilesFullParameterLocalPolicyState: each trainer rank hashes its model
shard (export_full_digest plugin), the head combines the shard hashes into
a trainer-resident state; the weights reach the engines through Miles
update_weights as before. No LoRA export, no apply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit db1315b)
The loop started a new trainer incarnation about 15 s after each failure,
without a limit (L5.4 run i). After --rl-trainer-max-failures (default 3)
failed incarnations in a row with no trained round between them, the loop
releases the trainer, journals gave_up and raises TrainerLoopGaveUp; the
fleet controller is aborted and the launcher exits 8. A trained round
resets the count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 387d05b)
A trainer island that reads its rounds from the sample buffer never called
--rollout-sample-filter-path / --rollout-all-samples-process-path, so the yeto
rollout metadata never reached the driver and every incarnation failed with
'no rollout metadata for rollout 0'. Found by the L5.4 GPU run
s24-l54-pair-20261014j.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 212a9be)
The held-back Modal function keeps the environment built at fleet launch,
so every incarnation reported phase 'submitted' at the run's launch time.
Each spawn now sets YETO_START_SUBMITTED_UNIX itself, so the island's
trainer_start_summary.total_s (the scheduler's T_s) measures this start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 6160480)
A round cut refused every full-parameter trainer ('no LoRA adapter
parameters', L5.4 GPU run k), so a trainer the scheduler stopped could not
continue. A trainer without LoRA adapters now cuts every trainable
parameter: the model copies, the named optimizer state (FP32 main and
moments, same fork-M5 helpers), the LR scheduler, RNG and counters, as for
LoRA. A mix of LoRA adapters and other trainable parameters is still
refused. The shard summary records the tuning ('lora' or 'full').

CPU test: a full-parameter trainer stops at a SCHEDULED cut, a fresh
incarnation restores it, continues the policy version and the buffer
watermark, trains no group twice and ends bitwise equal to a run that
never stopped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 2748b9b)
The scheduler could only start and stop a Modal trainer island; check()
refused a sky one. A sky trainer island is now one cluster per
incarnation (<island>-i<n>), launched from the held-back task with the
incarnation environment and torn down (verified) at release.

- SkyTrainerIslands: launch submits sky.launch and provisions on a
  daemon thread (alive while provisioning), tails the job log for the
  island events; release cancels a launch still provisioning; a
  restarted head releases an orphan by its recorded cluster.
- S3StoreFiles: the head's command channel and cut/buffer reads over an
  s3:// checkpoint store (the island mounts the bucket).
- Each incarnation's events go to <events dir>/<island>-i<n>.jsonl
  (Modal too), where the launcher reads trainer_start for the manifest;
  a record replayed by a reconnected log tail is skipped.
- check(): a sky trainer island needs an s3:// store; Verda refused.
- --rl-trainer-ready-seconds (default 1800): a sky start includes
  provisioning.
- The sky island submit (model-store config) moves to
  sky_ops.submit_island_launch, shared by the fleet and the binding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit f05e0053961e4176ce911e52fd9833a66f104a37)
chowhsu and others added 16 commits October 15, 2026 15:44
…store

A LoRA trainer island now releases only its adapter (run_plugin
write_lora_adapter over the snapshot publisher) with a file sha256 and the
trainer engines' lora:* checksum digest. The sampler island hot-swaps the
adapter (unload + load_lora_adapter under the update lock), waits for the
first release before generating, checks the file digest before loading and
reads the engines' adapter checksum back against the trainer's. The launch
refusal of LoRA buffer islands is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 658b7cc3d20b07509575a3395d88898ff68d3135)
After a LoRA load the sampler sends one fixed greedy request under the
adapter and records the answer digest and its token log-probability sum
in rl_sampler_policy_loaded, so a run shows the served policy change with
the version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 4b1be2aaab47a6c1e22af4be09724c8f321bfad1)
The LoRA release export gathers over the trainer's NCCL groups, which a
colocated trainer has released when it publishes (CUDA invalid argument in
all_gather_object on the first GPU run). Require a partitioned placement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 9a16d57a232d642559192d7d6525eca95e1167f2)
SGLang keeps the adapter files open, and open files on a Modal Volume make
every later volume reload fail, so the sampler never saw a new release.
The engines now load a local copy; the previous copy is removed after its
adapter is unloaded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit be5ef64f6bf5a0203ac848c65804b7164b21a951)
A colocated trainer publishes asleep (process groups destroyed, memory
paused). The LoRA release export now reloads the groups and disables the
memory saver around the adapter write, as the fork's update_weights does
for an asleep actor. Colocated LoRA trainer islands with more than one GPU
stay refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 71df2b3efb135ac9537e4cebe1714b0ff4d81403)
A LoRA sampler serves no adapter before the first policy release, so
the sample buffer stays empty and S1 never starts the trainer: the
pair waits forever (seen on Nebius, s24-tsky-l54-20261015a). The loop
now starts a trainer while the store has no release (journal record
first_policy) and keeps that incarnation until it trained a round, so
it is not idle-stopped right after its warm-up publication. The cloud
binding probes policy-releases/LATEST.json for LoRA runs only; a
full-parameter sampler serves the base model.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit b26e7e2fdb84e6d25dcc30aa0b06e6da59d22474)
A bucket mount (goofys) cannot append to or rewrite an object, so the sample
buffer's index.jsonl stalled on an s3:// store (L5.4 b on Nebius). Shared state
that one host rewrites while another reads it now goes through an object API:

- yeto/rl/engine/object_store.py: neutral ObjectStore port (read, write,
  create-if-absent, exists, list by prefix after a key, delete) and a
  directory backend.
- yeto/cloud/s3_store.py: S3 backend (boto3, configurable endpoint for MinIO,
  conditional put for create) and S3Store, the bucket mount plus `objects`.
  Islands use only explicit store credentials (YETO_RL_STORE_S3_*), never the
  default AWS chain; the launcher refuses a sample-role s3 launch without them.
- Sample buffer: one object per index record (sample-buffer/index/<pos>.json),
  positions taken with create-if-absent; the jsonl index stays for local
  directories and Modal Volumes. Watermark and dedup semantics are unchanged.
- Trainer commands and the cut pointer are read through the object API on the
  island; the policy release pointer is read by key and release weights are
  downloaded to a local cache on the sampler.
- The head reads the buffer by object key on sky instead of mirroring the index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit bd598c9e3df5c2930e4ca960dd6edccf829e6d3e)
A bucket mount (goofys) writes an empty "<dir>/" object for each directory it
creates; the sampler's release download read it as a key and failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 193fef17749fc1847d11bb1fcebc34c54b870bce)
The cut shards went through the goofys mount at 112-133 s per cut. A one-node
island with an s3:// store now keeps the store root on local disk and mirrors it
by object (TreeMirror): commit uploads new and changed files with the pointer
files (LATEST, LATEST.json) last and deletes pruned ones; reload downloads the
newest committed cut. Snapshot, cut and release code is unchanged. Multi-node
islands keep the mount (ranks on other nodes write to their own disks).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 65d93d53ebd7752e679c7e342bfae85e8ca29dbf)
… the store

Neutral core for the convertible pool (rl-trainer-start-stop L7.5):
- rank_nodes/select_group: hard conditions, network domain, layout fit and
  GPU type, in-flight trajectories, rollout capacity (GOAL 7.1 item 14);
  ConversionScheduler uses it and journals the ranking.
- Sample buffer shards: one index per appender host, merged by the head.
- convertible_remote: node agent (rollout rounds, conversion, one trainer
  incarnation per conversion), head TrainerIslands port and S1 gate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 75e1edfa3dace2a5b14f621a7db008b06283a944)
- Islands declared --rl-node-class islandM=convertible run as one pool: no
  held trainer island, S1 starts at the conversion time (60 s until one is
  measured), ConvertibleGate holds a start no node may take.
- Head binding (yeto/launch/convertible_pool.py): node profiles from --gpu,
  memory estimate as the level-1 limit, shard merge before each backlog,
  only samples within A_hard count.
- Miles node: ConvertibleRolloutFn switches between writing its shard and
  reading the buffer; run_convertible_node boots the engines, generates,
  and runs one fresh driver per conversion (DriverHandoffPort).
- A node reader never creates the shared index (store commit race).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit f79848abc9bf7e39677e6ccf20a194e578a59e83)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 717d43f26b75ed66130078310eb25d1ca5c38d7d)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 9851510f6a55d91570b67e4b315f1703581565e2)
GPU memory per node from memory_estimate.gpu_capacity_gib (GiB, as the
estimate); trainer_loop passes the node classes and the trainer precision,
so convertible_pool imports neither the launcher nor trainer_loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit a8808b5a441061821354400f57ebd533951acd2e)
…writes

GPU run s24-conv-20261015b: the driver's first publication ran onload_kv on
engines whose KV cache was still resident (SGLang KeyError 'kv_cache').
The handoff port now releases the KV cache and CUDA graphs (weights stay)
and wakes the trainer, the state of a fresh boot. Node store writes are
retried on OS errors (a Modal mount refused one rename); status writes are
best effort.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 389ea2c6f863e31704a483e2b52b4c8178467c67)
Spec for openspec change rl-trainer-start-stop, taken from s24-sampler-spec 7bd416e2 (PR #258).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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