[OCaml] High Intensity Training Online
feat validate efforts in the core, not only at the form
Evidence.Stimulus.Effort.make now returns a result and is the only constructor. It refuses a load that is not finite or is negative, a rep count below one, and an extension that counts zero. Zero load stays valid: it is a bodyweight movement. The rule lived only in the web form decoder, so an import, a stored record, or a direct construction could mint an effort the doctrine cannot read. Holding the invariant in the core makes that state unrepresentable everywhere. Each boundary now answers the core's refusal in its own terms. The form decoder maps it onto the field it concerns. The stored-record codecs report a malformed record. Restoring an import snapshot yields a typed Invalid_effort. The Hevy adapter applies the same rule at the trust boundary, so a row with zero reps or a negative weight becomes a warning rather than a rejected effort later.
Changed files
- lib/app/workout_import.ml
- lib/app/workout_import.mli
- lib/core/evidence.ml
- lib/core/evidence.mli
- lib/import/hevy_csv.ml
- lib/persistence/codec.ml
- lib/persistence/import_codec.ml
- lib/web/decode.ml
- test/test_codec.ml
- test/test_evidence.ml
- test/test_hevy_csv.ml
- test/test_import_codec.ml
- test/test_import_repo.ml
- test/test_import_sqlite.ml
- test/test_progression.ml
- test/test_service.ml
- test/test_sqlite_repo.ml
- test/web/logbook.ml