Repository navigation
S26 E1: single-island overhead (background round cuts, publish fast path, profilers) - #287
Draft
michaellchung wants to merge 55 commits into
Draft
michaellchung wants to merge 55 commits into
michaellchung wants to merge 55 commits into
Conversation
Eight arms per model preset ({bare,yeto} x {verl,miles} x {sync,async})
generated from one shared spec with a control fingerprint. Bare arms run
the frameworks' own entry points (verl main_ppo / fully_async_main,
Miles train.py / train_async.py) in the same images yeto uses; yeto arms
run verl island_entry and the yeto launcher. Parsers normalize verl
FileLogger, Miles perf lines and yeto tapes into per-round records
(missing fields are marked, never estimated); the report gives
yeto-vs-bare deltas with min-max range verdicts. Presets: Qwen3-0.6B
(E0.1) and Qwen3-4B-Instruct-2507 (E0.2). CLI: eff-plan, eff-run,
eff-parse, eff-report.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ric recipe; TE flash fails on sm86) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…spec Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…LoRA and /dev/shm NCCL host buffers The verl image applies yeto's patch_verl, whose sender hook imports yeto, so the bare arms now build the image without it. The fork's disaggregated checkpoint engine drops the PEFT config of unmerged LoRA params; the bare async arm sends merged weights instead. On Modal the trainer allocated NCCL host buffers with cuMem while the receiver fell back to /dev/shm and failed to attach (NCCL_DEBUG=INFO run); NCCL_CUMEM_HOST_ENABLE=0 keeps both on /dev/shm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…over rounds that report the value The fork logs a step's training metrics one parameter version late and never logs the last step's, so rows are keyed by training/global_step and the step without metrics keeps its trained batch from the config. Throughput rates use the steady rounds that report the value; the report lists partial counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… does not) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y Ray worker On Modal one weight-sync process could allocate NCCL host buffers with cuMem while its peer fell back to /dev/shm and then failed to attach (eff01-yeto-verl-async-e; NCCL_DEBUG=INFO in eff01-bare-verl-async-d). NCCL_CUMEM_HOST_ENABLE=0 unless the caller sets it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ains without dropout --rl-gradient-accumulation-fusion lets a run keep Megatron's fused wgrad (the Miles recipes' default); off keeps the historical argv. The generic recipe passed no dropout, so Megatron's 0.1 applied during RL training while the Miles recipes pass 0.0; it now passes 0.0 as the Qwen3.5 recipe does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No deterministic inference, constant LR, full recompute, dynamic batching, gradient-accumulation fusion, and an algorithm spec with the recipe's clip range (0.2 / 0.28); S26 GOAL §6 makes the bare framework's settings the reference. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… store) miles_local.sh pulls a copy of the repo whose test fixtures are tapes of other runs; their rl_local_round rows replaced the run's rewards (all four yeto+Miles runs showed the same fixture curve with reward 0 in round 0). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… missing The trainer's timing_s/gen and yeto's driver generate phase only pull finished samples from the rollouter queue. Bare verl logs the rollouter's active time per parameter version; yeto logs none per round, so its value stays missing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…from the rollouter E0.1 found yeto+Miles 77 % (sync) and 252 % (async) slower than bare Miles. The tape shows the gap is the per-round round cut: the arm sets a checkpoint store, so a cut runs after every publish (5.7-12.9 s: trainer wake, export, sleep, Modal volume commit), and in fully_async mode the cut also stops Miles' background worker, so generation never runs ahead of training. The bare arms keep no checkpoint; cut only at the end (--rl-cut-every rounds). yeto+verl async generation time was missing: the tape's rl_publication_check.rollouter_timing carries the rollouter's active time per policy version, the same metric the bare arm reports. Read it (version v closes round v - 1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… path VerlPolicyState.export_digest returns a trainer-resident handle: the workers validate the LoRA like canonical_state, compute the policy digests and the bf16 send checksum, and only those cross Ray. Both verl publishers take the send checksum from the handle; other states are hashed on the driver as before. materialize falls back to the full export; YETO_RL_PUBLISH_FASTPATH=0 keeps the old path. Tests pin hash and checksum equality with the full export. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… background E0.1 measured a periodic round cut at 5.7-12.9 s per round on the critical path of a colocated Miles island: wake the offloaded trainer (0.7 s), export (1.8 s), sleep it again (1.0 s), then a synchronous store commit (1.5-8 s). - prepare(): when a cut is due at the next safe point, the driver writes the trainer part right before it offloads a colocated trainer for the publication (phase "checkpoint"), for the state it is about to publish. The safe point keeps it only if its own context names the same cut (id, progress incl. policy hash, data cursor, ledger, outer token, in-flight); otherwise the unpointed files are dropped and the cut is taken as before. - Background commit: a periodic cut writes the snapshot and LATEST, then a thread commits and prunes (ResumeController.commit_checkpoint_store). The next cut joins it first and reports rl_cut_committed. Pruning only follows a successful commit, so a failed or lost commit leaves the previous cut as the committed one. The final cut, and a run with a sample buffer that commits the same store, stay inline. YETO_RL_CUT_BACKGROUND_COMMIT=0 turns it off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A round cut on a Miles fully_async island stopped the background worker and requeued every untrained prompt into the live data source (P0-S decision 3). With a cut every round the worker restarted each round, so generation never ran ahead of training (E0.1: 8 worker starts in 8 rounds, policy age 0, 2.15 s of serial generation per round). snapshot_untrained() reads the data source buffer, every drawn untrained group (reset copies) and the data cursor in one step on the worker's event loop, where the worker draws; the cut carries them and the cursor read with them, and the live run is untouched. A resume still regenerates exactly the drawn, untrained prompts (decision 3 unchanged); the resume manifest's data watermark is that cursor. requeue_untrained() stays for its other callers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
e0f833d made the generic Miles recipe pass --attention-dropout 0.0 and --hidden-dropout 0.0 (the bare Miles recipe's values); the seven decoupling golden samples were not regenerated and failed on miles_argv. The only difference is those four argv items (and the argv hash); no algorithm or contract hash changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
E0.1: the yeto+Miles sync generate span was 0.46 s longer than Miles' perf/rollout_time. 0.44 s of it is --offload-rollout (release_memory_occupation), which Miles times outside rollout_time but MilesRolloutPool.generate ran inside the driver's generate phase. The driver now asks the pool to defer it and runs it as phase "rollout_offload"; callers that use the pool directly keep the old in-generate offload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
final (default) cuts once at the end like the bare arms keep no checkpoint; every-round is the product default. Kept out of the control fingerprint so the bare runs stay comparable. The yeto+verl arm has no checkpoint store in either mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GPU canary e1a-yeto-miles-async-1 (every-round cuts, after 74016eb and 3b00fe5): overlap is back (one fully_async worker start), but each cut still held the round for 2.1 s writing and hashing the 283 MB shard, so a round took 5.3 s against 4.15 s bare (78 %). MilesTrainerGroup.start_cut captures every rank's state at the step boundary (CPU copies) and the ranks write, fsync and hash their shards in a thread (cut_plugin.save_cut_shard background=True); finish_cut collects the summaries and commits the manifest. A periodic cut now only captures at its safe point; the shard write, manifest, pointer, store snapshot and commit complete before the next cut (join_commit, rl_cut_committed). Until then the previous cut stays the pointed and committed one; a failed completion drops the unpointed files. The store's first cut, the final cut, runs with a sample buffer or a critic, and YETO_RL_CUT_BACKGROUND_COMMIT=0 stay inline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he sample filter hook GPU e1a2-yeto-miles-async-1: with the shards written in the background a cut held the safe point 0.2 s, but joining the previous Modal volume commit (1.8-9.9 s) at the next cut still held the round. A periodic cut is now skipped (rl_cut_skipped) while that commit runs; the final cut waits for it. The same run spent 0.9 s per round between Miles' 'Finish rollout' and the train data (0.04 s in E0.1): the yeto sample filter hook now prints its per-step timing to find it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
state_digest copied every tensor into a bytes object before hashing, holding the GIL for the copy. Hashing the buffer gives the same digest (checked for float32, bfloat16, empty tensors and the numpy RNG arrays) and lets hashlib release the GIL, so a background cut writer does not stall the trainer's own thread (E1 GPU: a colocated offload took 4.0 s instead of 1.3 s while the shard was written). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
e1a3-yeto-miles-async-1 still held each round 5-6 s: join_commit completed the pending cut, started its store commit and then joined that same commit. A periodic cut now completes the pending cut and starts its commit without waiting (the commit still running at the next safe point skips that cut); only the final cut waits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fully_async round metadata took 1.1 s per round on GPU (0.09 s in round 0). Print the seconds of round id, group records, carry-over fields, submitted groups and the sink put with the hook timing line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l path e1a4-yeto-miles-async-1: with overlap restored every trained token is one version old, so each round's metadata hook re-scored all 32 samples by an SGLang prefill (carry_over_fields: 0.7-1.2 s) before Miles returned the batch, while the trainer waited. Its only reader is the policy-age governor, after the train step. Under fully_async the estimate now runs in a background thread of the rollout process and reaches the sink as a late record of its rollout id; MilesRolloutPool keeps it aside and the driver reads it after the train step (late_cross_version_fraction). Other modes estimate inline as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…yword Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The completion of a periodic cut (manifest commit with shard re-hash, pointer, state-dir copy into the store) ran on the driver thread at the next safe point (0.33-0.36 s per cut on the Modal A10G run e1a5). Only the trainer call that collects the rank summaries stays on the driver thread (collect_cut); the rest runs in the commit thread. The store prune no longer deletes a round cut newer than every kept one: such a cut may be being captured while the previous cut commits in the background. A reconfiguration at a safe point first joins a running cut commit. The Miles publisher logs a per-step timing line (E1 diagnosis). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The terminal rl_load_sample probe after generate ran synchronously (~0.1 s per round on the Modal A10G fully_async run e1a6). It is observation only: it now runs in a thread; the next round and the end of the run wait for it (bounded), so its event still lands before the tape closes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A periodic cut prepared before the trainer offload and then skipped because the previous cut was still committing waited for its trainer-side shard writer at the safe point (~0.9 s every other round, Modal A10G sync run e1a6). The writer is now collected, and the files dropped, at the next cut. The Miles cut capture logs a per-step timing line (E1 diagnosis). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the prepare of a periodic cut met a running commit, and the commit was done by the safe point, the cut was taken there by waking the offloaded colocated trainer (~2 s, Modal A10G sync run e1a7). Such a cut is now skipped (rl_cut_skipped) when a committed cut exists; the next safe point cuts again. Cuts whose prepare was not declined, the first cut and the final cut are taken as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The yeto+Miles arm ran in one 8-CPU / 64 GiB container whatever its card count; the bare arms (modal_eff) get 8 CPU and 48 GiB per card, i.e. 16 CPU for the two-card fully_async arm. The yeto fully_async trainer took 6-11 % more time per token than the bare one on the Modal A10G E1 runs while the one-card sync arms matched, with and without periodic cuts. The arm now asks for 8 CPU and 48 GiB per card. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A periodic cut whose store sync is deferred copied the state dir into the store
only at the next safe point, after one more round had changed it: the snapshot
held the ledger of round N+1 with the pointer of the cut at round N, and a
resume from it was refused ('the restored ledger's consumed groups (64, ...)
differ from the pointer's (56, ...)', Modal A10G resume run e1c, cut written by
e1a7). The deferred cut now stages a copy of the state dir at the cut (a few KB:
ledger, journal) and the background sync snapshots that copy, with the same
pointer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A background cut took its adapter and optimizer copies to the host at the step boundary (0.12-0.14 s per round on A10, one wait per tensor). It now clones them device-to-device there and the writer thread moves them to the host, then computes the N7.6 resume state from the lrs and scheduler progress read at the boundary. The shard is byte-identical to the inline host path. The device path needs CUDA parameters and free memory for twice the copies plus 1 GiB; otherwise, or on a device OOM, the capture takes the host path. Each rank reports its mode and reason; rl_round_cut carries them as cut_capture. YETO_RL_CUT_DEVICE_COPY=0 turns the device path off. The Miles fork takes export_named_optimizer_state(device=None) on branch yeto/s26-e1-device-export (466b89b92, not pushed); for the pinned image the same entries are built from the fork's optimizer_slots. Tests compare both with the fork's host export. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The trainer-side policy digest copied each LoRA tensor to the host on its own (about 400 waits for Qwen3-0.6B). stage_f32_on_host converts and checks the tensors on the GPU and copies them into one reusable pinned buffer with one synchronization. The values, and so both digests, are bit for bit those of the per-tensor copies; a non-finite tensor is named as before. verl: the worker digest stages the same way, validates its own copies without a second clone, and computes the bf16 send checksum in a thread beside the two digest streams. Only the digest path stages; full exports keep their own tensors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under max_policy_age > 0 the hook read the policy-age limit from the sink actor once per group and twice per trajectory record, each time with a ray.get_actor lookup: about 70 actor calls a round, 0.03 s of the 0.045 s hook on A10. one_sink_view memoizes sink reads and the actor handle within one run of record_trained_groups, the all-samples hook and the buffer filter. A write from the same thread drops the value from the view; other writers are seen from the next run on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Modal A10G containers report NVIDIA A10 or, sometimes, NVIDIA A10G; the A10G trained about 12 % slower (E1 run e1a9). The bare and yeto+verl containers now refuse an A10G when the spec asks for the A10 the other runs had, parsed runs record the GPU names from NVML, and the report refuses to compare runs on different GPU models. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hash_seconds and export_seconds are wall times and may differ between two calls on the same tensors (seen on yeto-cpu-test). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With --offload-train the colocated trainer sleeps right after the cut is prepared, and the writer thread then failed to read the device copies (CUDA error: invalid argument; 5 of 6 cuts refused in GPU run e1g-yeto-miles-sync-1, the run itself went on). Such a trainer now takes the host path, reported as reason offload_train. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The background cut writer ran torch.save and four sha256 passes in a thread of the trainer process while the next step trained; each trained token was about 8 % slower in those rounds (GPU runs e1g/e1h). The trainer thread now copies the device snapshot into shared host memory and a per-rank spawn writer process (nice 10, one intra-op thread) serializes, hashes and summarizes it. A writer-process failure removes its partial file and the thread writes the same shard; the summary's writer field and rl_cut_committed record the mode and the fallback. YETO_RL_CUT_WRITER_PROCESS=0 keeps the thread. Strings are interned before torch.save so the shard bytes depend on values only: a shard written in the writer process is byte-identical to the inline one (tests compare file bytes, digests and the N7.6 state). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Modal A10G containers are an NVIDIA A10 or an A10G, and the A10G trains
about 12 % slower (E1 run e1a9). --modal-gpu-name MODEL fails an island
at container start unless every GPU nvidia-smi reports is that model
("NVIDIA A10" refuses "NVIDIA A10G"). The efficiency eval passes it
for the yeto+Miles arm with the model the bare arms require, so every
arm runs on the same card.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In sync colocated mode the trainer hashed the staged policy (two sha256 streams, about 0.1 s) before it could offload. export_digest now takes deferred=True: the trainer returns once the tensors are staged in its pinned buffer and hashes them in a thread; the state's DeferredDigest collects the result (collect_digest plugin) on first read, at the publication. The values are those of the eager digest; a new export waits for an uncollected hash before it reuses the buffer. The cut context reads the policy hash last. The driver defers only for a colocated trainer that offloads to publish (YETO_RL_DEFERRED_DIGEST=0 turns it off). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Observation only, for E1: around each train_one_step the trainer process prints the step's wall time, main-thread and process CPU time, garbage collector pauses, CUDA caching-allocator counters and stack samples of every Python thread. The launcher forwards the flag to the island, the island sets it on the Miles args, and eff-run --profile-trainer passes it for the yeto+Miles arms (not in the control fingerprint). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… step The writer thread copied the device snapshot to the host tensor by tensor while the next round trained: about 400 synchronous device-to-host copies interleaved with the training kernels, the suspected cause of the slower training (profile run e1j-yeto-miles-async-a: the step grew while GC, allocator and main-thread CPU stayed as in the final-only run). The capture now copies the device snapshot into one reused pinned host buffer with a single synchronization, drops the device copies, and the writer thread copies the staged views into shared memory (the buffer is reused only after that). Since nothing reads device memory after the capture returns, a trainer that sleeps right after the cut (--offload-train) takes the device path too. Tests compare the shard bytes and summaries of staged, unstaged and inline cuts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t at the step" This reverts commit fe5e612. GPU runs e1k-yeto-miles-async-2 and e1k-yeto-miles-sync-1: the trained tokens were not faster (0.553 ms per token, as before) and the step-boundary capture got slower (async 0.033 -> 0.08 s, sync host path 0.16-0.28 -> 0.30 s). The interleaved device-to-host copies were not the cause of the slower training. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moving each tensor to shared memory passed one file descriptor per tensor (about 400 per cut) through the trainer process's resource sharer, and its queue feeder and server threads ran while the next round trained (profile run e1j-yeto-miles-async-a: about 15 % of the step's stack samples; none in the final-only run). The writer thread now copies every contiguous tensor that owns its whole storage into one shared uint8 buffer and sends offsets; the writer process rebuilds each tensor as its own copy. Other tensors are shared one by one as before. The shard bytes and summaries are unchanged (tests compare them with the inline cut). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Observation only, for E1: a daemon thread samples the driver thread's stack every 5 ms and, at each driver phase change, prints the finished phase's wall time and its most frequent yeto/miles/ray frames. Without the flag the phase hook does nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GPU run e1n-yeto-miles-async-p: the island enables the sampler from the thread that starts asyncio, but the driver runs its phases on another thread, so every sample showed the event loop. The sampler now binds to the thread of the first phase report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A sample is labelled by its innermost four yeto/miles/ray frames, so a wait inside Ray shows which driver call waits (GPU run e1o-yeto-miles-sync-p only showed the Ray frame). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Untested work in progress (E1 phase 8): verify_publication, the unverified identity of a skipped round, --rl-verify-every-round. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under --offload-train the cut capture took host copies tensor by tensor (0.16-0.28 s at the step boundary, GPU runs e1i/e1l sync). It now copies the state device-to-device and stages the copies into one reused pinned host buffer with a single synchronization before the capture returns, so nothing reads device memory after the trainer sleeps. The writer thread copies the staged views into the shared hand-over buffer and frees the staging buffer. Shard bytes and summaries equal the inline cut's (tests). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The product default round-cut interval is now 4 (--rl-cut-every 1 cuts every round). A publication hashes the policy and reads the engine weight checksums back only on the first publication, on every round that is a multiple of the cut interval (every round that keeps a cut) and, with --rl-verify-every-round, on every round. Other publications skip both but still check, by weights version, that the trainer holds the policy; their identity is a 64-hex value derived from the trainer weights version (never a content hash), and the publication event says sync/publication_verified false. A cut refuses an unverified publication, so resume always sees a content hash. Help texts and the rl-resume-from-checkpoint spec and design say so; the efficiency eval gains --checkpoint default (no --rl-cut-every). Tests: which rounds verify, the switch, the skipped read-back, the weights-version check on an unverified round, and the cut refusal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Verified publications keep the event as it was, so recorded golden tapes stay byte-identical (test_decoupling_golden, test_rl_driver_profiles). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A test enabled the process-wide driver phase sampler and left it on; it then printed on every later driver phase and broke the JSON output that test_rl_reconfig_e1::test_command_inbox_and_cli parses (failed in the full suite only). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A first periodic cut was written inline because a store that holds cuts but no LATEST refuses to start. With --rl-cut-every 4 that cut falls in the steady rounds (Modal A10 e1q: 2.3-9.8 s on the critical path). The first cut is now deferred like the others. Before its shards start, a fsynced mark round-cuts/<cut_id>.first-cut-in-progress is written; it is cleared once a LATEST is committed. At start, a store without LATEST whose every cut carries the mark (never committed) is cleaned and the run starts at rollout 0 (the local state dir is moved aside, journaled). Any other store with cuts and no LATEST is still refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
E1 single-island overhead work: the yeto path against the bare framework on one island.
--rl-profile-trainer(trainer step profile, driver sampled per phase). Launcher flag--modal-gpu-namepins a Modal island to one GPU model.Defaults
Branch dependencies
Contains s26-e01-effeval (#281). Merge or rebase after it.
Tests
GPU evidence (Qwen3-0.6B, Modal A10G; SESSION26/E0.1-REPORT.md sections 7-10)
Not verified
🤖 Generated with Claude Code