Beat the baseline: recall@5
# grade this stage (from your course directory)
npx theinference test --stage beat-the-baseline --watchThe moment of truth. Train the encoder with your chapter-5 loop, then compute recall@5 on the same frozen eval set — and report it next to the baseline. A correct trainer crushes the baseline; a broken objective (no temperature, sign-flipped gradient, one-directional loss) doesn't separate and lands near the baseline.
What to build
Train, then measure recall@5
Make your_program.sh recall train from the frozen init, embed the eval set with the trained encoder and with the untrained baseline, and print both recalls as JSON:
$ ./your_program.sh recall
{"recall":0.96,"baselineRecall":0.44}
Process mode: stdout must be JSON matching fixtures/expected.json within atol 1e-4, rtol 1e-3. The trained recall sits far above the baseline — that gap is the proof your contrastive trainer learned meaning, not just word overlap. Because everything is seeded and single-thread, both numbers are reproducible to the reference's frozen values.
The usual culprits: the one-directional loss where symmetric was expected, a missing temperature scale (cosines never sharpen), or an L2-norm-backward sign error that lets the loss fall but not separate the embeddings. Re-run gradCheck and confirm the loss actually dropped below ln(B) in chapter 5.