
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.