
Verified pacing, not vibes
Photograph a race course map — get a km-by-km pacing plan checked against five deterministic rules before it's ever shown to you, plus a locally fine-tuned model that produces the same plans for free.
Ask a chatbot for a marathon pacing plan and you're trusting it to sum 42 split times correctly, reason about hills it can't see numerically, and not quietly hand you a negative-split strategy that almost nobody actually achieves. Race Day Copilot never trusts a plan on vibes: every plan is checked in plain code against five rules before you see it — coverage, arithmetic sum, per-km plausibility against the real course profile, fade direction, and a heat-adjustment formula derived from a real Boston Marathon finding.
Local OCR + Claude reads the course-map photo into a structured profile — refuses non-course photos.
Coverage, sum, plausibility, fade, and heat — every violation a plain-English sentence, checked before you ever see the plan.
Heat adjustment derived from this portfolio's own Boston Marathon finding: ~1 min slower per °F on the field median.
A LoRA-tuned Qwen2.5-3B distills the same plans — runs on a laptop, $0 marginal cost.
Held out by scenario id (70 scenarios), zero-shot/no-retry, identical prompt for all three systems:
| System | Parse success | Verifier pass rate | Latency |
|---|---|---|---|
| Base Qwen2.5-3B (no fine-tune) | 55.7% | 0.0% | 17.5s |
| + LoRA (this project) | 95.7% | 0.0%* | 53.7s |
| Claude (teacher, zero-shot) | 90.0% | 75.7% | 157.7s |
mlx_lm.lora's CLI has none — which still didn't fully eliminate the instability. The published model is the best checkpoint from ~400 iterations of confirmed-clean training, published deliberately rather than chasing a fifth attempt. The full story is in the writeup — it's the most interesting part of this whole project.
claude -p calls for course extraction and generation — no metered API, no vision API key. See the full writeup for the complete training saga, including four real incidents and the two bugs found building the benchmark itself.