What happens, step by step, when RL saves and restores a turn snapshot

NVIDIA-NeMo/RL #4500 (Gym turn-level rollout recovery). Drawn from the code at head 341b9ad, not from the PR description. Every step links to its line.

Before
A rollout snapshot saved only RL's own data. RL never paused Gym, and an unfinished rollout always restarted from its prompt.
This PR
With target_level: turn, each snapshot also pauses Gym and saves where every unfinished run is. A restart picks those runs up mid-conversation.
Status
Proposed in this PR, with open review findings. Red numbered badges mark the step each finding hits. The review is still pending, so the badge links work only for its author until it is published.

Four pages on this PR. This one: the exact order of a save and a restore, read from the code.

Background. Gym runs each rollout as a run. r7#0 means rollout r7, Gym attempt 0. gA and gB are two copies of the Gym servers. The recovery ledger is RL's list of which runs each copy holds. TQ holds finished rollout data until training uses it. Dotted words have a short explanation: hover or tap them.

The PR description has its own 15-step version of this diagram. I rebuilt it from the code to check it. The order matches. But six steps are missing or drawn differently (marked â—†), and most of them are where a snapshot can be lost. The example: copy gA has two runs in flight. r7 is mid-turn, and r9's last reply is already on its way back. Copy gB still runs r4, which RL already dropped.

Save: one snapshot

Snapshot task RL controller Dispatch gate new Gym /run calls Recovery ledger who owns which run Gym servers copies gA, gB TransferQueue rollout data Snapshot dir tmp → final 1 close; wait for calls already sent SC L4727 review comment #4: nothing measures how long each snapshot pauses rollouts#4 2 differs from the PR description's diagram pause rollout time limits SC L4731 3 dropped runs? → gB: [r4#0] SC L4586 4 retire gB: [r4#0] (skipped if none) SC L4589 review comment #11: no test notices if retired episodes are never marked retired#11 5 differs from the PR description's diagram prepare: park every run (≤ 300 s) coordinator L356 review comment #1: a restart-only Gym agent never parks, so prepare times out; 3 timeouts stop the run#1 review comment #8: the stall watchdog keeps counting while Gym is parked#8 6 all parked 7 differs from the PR description's diagram renew lease now, then every ≤ 10 s coordinator L613 8 runs RL owns → gA: [r7#0, r9#0] SC L4740 9 commit gA: [r7#0, r9#0] SC L4750 10 saved gA: [r7#0] (r9 already answered) 11 differs from the PR description's diagram write gym_checkpoint.json now coordinator L469 12 drain: wait until r9#0 is done (≤ 60 s) SC L4593 review comment #2: one failed row ends the whole stream for its group#2 review comment #3: a re-sent row waits behind the closed gate but still counts as running, so the drain times out#3 13 take the exclusive barrier SC L4902 14 differs from the PR description's diagram train step moved? → throw snapshot away SC L4903 review comment #6: a train step that ends during the Gym phase throws the snapshot away after the full pause#6 15 Gym saved == ledger? its rows in TQ? SC L4639 16 save TQ + ledger SC L4549 17 resume (once, no retry) coordinator L647 review comment #7: if one resume is lost, Gym stays closed for the 600 s lease#7 18 reopen; restart time limits SC L4762 19 differs from the PR description's diagram write sidecars, rename tmp → final SC L4926 no new Gym runs start Gym parked TQ writes blocked differs from the PR description's diagram not in the PR's diagram review comment #4: nothing measures how long each snapshot pauses rollouts#4 review comment (tap)

Where the PR's diagram differs from the code â—†

How a save fails: a prepare timeout (step 5) or a drain timeout (step 12) counts as one failed snapshot, and three in a row stop the run. A parked run that Gym did not save stops the run at once.

Restore: at startup

Restore (startup) RL controller Recovery ledger loaded from disk Gym servers copies gA, gB TransferQueue rollout data Snapshot dir final 1 block TQ writes for the whole restore SC L1255 2 load: r7 on gA, Gym attempt 0 SC L1258 3 differs from the PR description's diagram gym_checkpoint.json there? (no → r7 starts over) SC L1273 4 saved runs == ledger's runs? SC L1304 review comment #28: the mismatch error does not say which episodes differ#28 5 restore gA: [r7#0] → Gym sets up r7#1 SC L1312 review comment #19: a failed Gym restore calls resume twice (cleanup is written in two places)#19 6 r7: Gym attempt 0 → 1, stays on gA recovery L537 7 check staging rows; drop unused ones SC L1349 8 differs from the PR description's diagram a later step fails? → retire r7#1, resume SC L1358 9 resume SC L1381 10 send r7#1 to gA → goes on from its turn manager L2586 review comment #12: the functional test passes even if the episode started over#12

This matches the PR's flowchart on the main points. The saved runs must equal the ledger's runs. Each restored run moves to Gym attempt 1 and goes back to the same Gym copy. Two paths are missing from the flowchart â—†:

Review findings on this path

StepCommentWhat goes wrong
save 1#4 · single_controller.py:4727nothing measures how long each snapshot pauses rollouts
save 4#11 · test_checkpoint_dispatch_races.py:1244no test notices if retired episodes are never marked retired
save 5#1 · gym_checkpoint_adapter.py:208a restart-only Gym agent never parks, so prepare times out; 3 timeouts stop the run
save 5#8 · config.py:719the stall watchdog keeps counting while Gym is parked
save 12#2 · nemo_gym.py:1182one failed row ends the whole stream for its group
save 12#3 · rollout_manager.py:1864a re-sent row waits behind the closed gate but still counts as running, so the drain times out
save 14#6 · single_controller.py:4906a train step that ends during the Gym phase throws the snapshot away after the full pause
save 17#7 · nemo_gym.py:1961if one resume is lost, Gym stays closed for the 600 s lease
restore 4#28 · single_controller.py:1308the mismatch error does not say which episodes differ
restore 5#19 · gym_checkpoint_coordinator.py:537a failed Gym restore calls resume twice (cleanup is written in two places)
restore 10#12 · turn_recovery.sh:432the functional test passes even if the episode started over
So what. The PR's diagram gets the order right, but it leaves out the four places where a snapshot is lost or the run stops: the prepare timeout (step 5), the drain timeout (step 12), the train-step check (step 14) and a lost resume (step 17). Those are review comments #1, #2–3, #6 and #7. The PR's diagram should show them, so readers can see where a snapshot can fail.