One prompt group is one prompt with 2 rollouts. Training consumes 3 groups a step. The checkpoint
lands mid-run, so the buffer holds a mix: some groups trained already, some finished and waiting,
some still generating. The rule every case is checked against:
a group the loader has handed out and the trainer has not used must come back — it
sits behind the saved loader position, so nothing will ever produce it again.
Where the two failure lines come from
FAIL — in_order and weight_fifo say
supports_buffer_checkpoint = False, so no index file is written
(single_controller.py:981)
and the restore returns straight away
(:322).
Before this change the buffer was saved every time with no such check
(merge base :802)
and restored for any sampler whose name matched
(:270).
XFAIL — a group that has not committed is never written down.
reserve() marks the slot not-ready
(replay_buffer.py:910)
and the save skips anything not ready
(:1093).
A group is either reserved or committed with nothing in between, so 1/2 and
0/2 are the same thing to a checkpoint. The old code did this too, so it is a gap, not
a step backwards.
Does it work?
These were run, on CPU, in about 12 seconds. Real output:
10 failed, 19 passed, 4 xfailed in 11.69s
AssertionError: in_order/lag1-next-step-complete: ['g12', 'g13', 'g14'] came back
before this change and do not now. These groups finished generating and committed,
so the old save wrote them and the old restore returned them on a same-sampler
resume. recovered=[]
That is 33 tests: the 18 badges above, plus 15 smaller checks that pin why each line fails.
Zero XPASS, which is the number that matters: every case marked "expected to fail" really does
fail, so none of them is quietly passing and hiding a gap. The tests drive the real
TQReplayBuffer, the real samplers and the real save and load calls — only the tensor
converter is swapped out, so a case can use empty prompt records.
What to take from it. The two PASS badges on the bottom row are the check proving
itself: there, the old code recovered nothing either, so there is nothing to lose and no failure is
reported. Everywhere else a gated sampler is used, groups that finished generating no longer come
back. The rollout tensors are still in TransferQueue after the restore — only the note of which rows
form which group is missing — so closing this needs an index, not a re-run of the work.