Question
Implement an iterator over a copied list that can serialize its position and later resume at the next not-yet-yielded item. Then compose the same behavior across a sequence of lists.
```python
class ResumableListIterator:
def __init__(self, items, index: int = 0): ...
def __iter__(self): ...
def __next__(self): ...
def save_state(self) -> dict: ...
@classmethod
def from_state(cls, items, state): ...
class ResumableChain:
def __init__(self, groups: list): ...
def __iter__(self): ...
def __next__(self): ...
def save_state(self) -> dict: ...
@classmethod
def from_state(cls, groups, state): ...
```
Saved state points to the next item, never the last item returned. A completed iterator restores completed. State must be serializable and restoration must not repeat or skip values. The chain state records both the current group and the position within it.
Use exactly `{"index": integer}` for list state and `{"outer": integer, "inner": {"index": integer}}` for chain state. Reject malformed keys, booleans/nonintegers and negative or out-of-range active indices with `ValueError`. The list index may equal its length; the chain outer index may equal its group count to represent completion. A completed chain retains a nonnegative inner index but does not read that group. Copy the list containers at construction (not a deep copy of contained objects); returned state dictionaries are independent snapshots. The caller must restore against the same logical dataset: these positional checkpoints do not authenticate dataset identity. The provided `run_case` handles fixture orchestration and is not candidate work.
## Staged follow-ups
1. Add dataset identity/version checks to reject checkpoints from a different dataset.
2. Support a reopenable file by storing an offset instead of live process state.
3. Discuss one-shot generators and why a live generator cannot be a durable checkpoint.
4. Version the checkpoint format as the iterator implementation evolves.