fix(orchestrator): pin eval epochs to one policy version#3034
Open
vineetjain96 wants to merge 1 commit into
Open
fix(orchestrator): pin eval epochs to one policy version#3034vineetjain96 wants to merge 1 commit into
vineetjain96 wants to merge 1 commit into
Conversation
vineetjain96
marked this pull request as ready for review
July 15, 2026 03:51
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.
Summary
Problem
Eval groups record the policy version and cache salt present when they are scheduled, but the live inference pool is not pinned to that version. The watcher can pause, update, and resume inference between requests or turns of the same eval step. A trajectory can therefore execute against multiple policies while being labeled as one version, and an eval cohort can contain mixed weights or hit provider failures while its model or adapter is replaced.
Holding the existing watcher update lock across eval exposes a related race. The watcher can select checkpoint N and then wait behind the eval lease while the trainer publishes newer checkpoints and deletes N. Loading the path selected before that wait then fails because it no longer exists.
Fix
Queue each due eval step and acquire the watcher lock in a supervised side task before adding its work to the dispatcher. Keeping lease acquisition off the bounded rollout consumer lets completed train and eval rollouts continue draining while a watcher already ahead of the trigger finishes its update. The lease remains held until every eval environment due at that step has returned; startup eval uses the same path, and failure and shutdown paths release it explicitly.
The dispatcher finishes any train group already being scheduled and drains the pre-existing train tail before starting eval. Once the complete eval cohort has been dispatched, it returns spare permits to train while the eval tail finishes under the pinned policy. Empty eval environments and group-scoring cohorts larger than
max_inflight_rolloutsare rejected up front because they could never complete the epoch and release the lease.The watcher waits for checkpoint publication without holding the live-policy lock. If the originally requested checkpoint has already disappeared, it first retargets to a newer stable checkpoint. After acquiring the lock, it resolves the latest stable checkpoint again and uses that version consistently for the weight path, pending and completed observer callbacks, inference, and policy state.
Interval eligibility is unchanged. If another eval step becomes due while an epoch is active, it is queued and starts after the current epoch releases the lease. Eval sampling, scoring, and trainer data are unchanged.
Verification
pytest -q tests/unit/orchestrator/test_orchestrator_setup.py— 10 passedpytest -q tests/unit/orchestrator -m 'not gpu'— 102 passed, 1 skippedruff check --config=pyproject.toml .ruff format --check --config=pyproject.toml .git diff --checkNote
High Risk
Changes orchestrator scheduling, live inference weight updates, and eval correctness guarantees on a critical training path; mistakes could deadlock the pipeline or mis-label eval policy versions.
Overview
Pins each eval step to a single live policy version by holding the watcher’s
update_lockfrom a backgroundeval_triggertask until every env due at that step finishes. Due steps are queued (non-blocking on the rollout consumer); startup eval runs and completes its lease before the dispatcher and watcher start.Dispatcher
PREFER_EVALnow finishes open train groups, drains in-flight train work, dispatches the full eval cohort, then flips back toPREFER_TRAINso spare capacity can run train while the pinned eval tail completes. Train scheduling stays off whentrain_scheduling_disabled.WeightWatcher waits for checkpoint
STABLEwithout the update lock, retargets if the requested step was deleted, and coalesces to the latest stable checkpoint after acquiring the lock.Fail-fast on eval envs with no examples or group-scoring
group_sizeabovemax_inflight_rollouts.EvalSource.eligible_envspeeks due envs without consuming startup trigger state. Docs and monitor-run skill describe the pinned-eval log line and overlapping train/eval inflight.Reviewed by Cursor Bugbot for commit cbdc11c. Bugbot is set up for automated code reviews on this repo. Configure here.