Multi-step agentic pipeline for aitrainingplan.app: generate a training plan, verify it against real coaching constraints, repair or regenerate what fails, publish. Plan, kept on rails.
athlete profile (JSON)
-> GENERATE model proposes a structured weekly plan
-> VALIDATE deterministic rule check, in code, never the model grading itself
-> REPAIR bounded local surgery from structured violations (no LLM call)
-> REGENERATE only for what repair cannot fix, prompted with exact numbers
-> FLOOR conservative template, valid by construction, if all else fails
-> PUBLISH markdown
The model proposes. Code decides whether the proposal is allowed to ship.
No API key needed. naive is an offline generator that reproduces the failure
modes real models show on this task, so the pipeline is demonstrable and
testable offline.
python3 -m planrail.cli fixtures/tight_timeline.json --generator naive
python3 -m planrail.cli fixtures/tight_timeline.json --generator naive --no-repair # ablation
OPENROUTER_API_KEY=... python3 -m planrail.cli fixtures/tight_timeline.json --generator llm
python3 -m pytest tests/ -qThe 10% rule is not in here as stated. It is folk wisdom with weak evidence, and it is mathematically broken at low volume: an athlete running 12 km/week gets a 1.2 km ceiling and can never build. The ramp gate is 10% or a +3 km absolute floor, whichever is more permissive, backed by ACWR (7-day acute over 28-day chronic, held in 0.8-1.3) used as a governor, not a predictor. ACWR's injury-prediction claim has been substantially dismantled (Impellizzeri et al.), and it destabilises at low chronic load, so it is only applied above a 20 km/week chronic floor. Below that the ramp gate governs alone.
Repair before regenerate. Handing a whole plan back to the model to fix week 3 discards five good weeks and usually breaks a different one. The validator emits structured repair hints, code does the surgery, and the model is only paid for what code genuinely cannot describe. On both fixtures this resolves every violation at zero LLM calls.
Rules are checked across week boundaries. A Sunday long run followed by a Monday tempo is two hard days back to back. A per-week loop never sees it, and it is the most common way a plan sneaks a hard pair past a reviewer.
| path | what |
|---|---|
planrail/validate.py |
the rules. no LLM in this file, on purpose |
planrail/repair.py |
structural fixes + the forward volume sweep |
planrail/generate.py |
llm / naive / template generators, one signature |
planrail/pipeline.py |
the loop, the retry budget, the floor |
docs/AUTOPSY.md |
the repair regression, root cause, fix |
docs/ARCHITECTURE.md |
adaptive replanning (MONITOR) |