Reproducibility with seeds
Pseudo-random numbers come from a generator that starts at a seed. The same seed gives the same sequence, so a run can be repeated exactly.
import random
a = random.Random(42)
b = random.Random(42)
print(a.random() == b.random()) # TrueOwn your generator
random.seed(42) sets one shared global generator. Any other code calling random.random() will shift your sequence, and your code will shift theirs. Creating random.Random(seed) keeps the stream private.
NumPy has the same idea: np.random.default_rng(seed).
Notebooks
Notebook cells can run in any order, and that is a common reason for results that cannot be reproduced. Before you trust a result, restart and run every cell from the top.
Limits
A seed reproduces a sequence for a given version of the library. Do not hard-code expected random values across Python versions. Save the seed and the results together.
Your task
Write shuffled_copy(items, seed) returning a new list shuffled by random.Random(seed).shuffle. Write split_rows(rows, test_fraction, seed) shuffling a copy the same way and returning (train, test), where test is the first int(len(rows) * test_fraction) shuffled rows. Neither may change the input or the global generator.
The explanation opens here once every check passes.