[OCaml] High Intensity Training Online
refactor Rename movement to effort
Name recorded all-out exercise performances Effort to separate them from catalog exercise movements and preserve the Heavy Duty focus on failure-bound effort rather than set counting.
Changed files
ARCHITECTURE.md
@@ -14,7 +14,7 @@
14
14
prescribes one set per exercise, so there is no set count to record, and no "set
15
15
group" wrapping anything. How a drive was delivered is a property of the stimulus.
16
16
17
Removed:
**Reaching failure is an invariant, not data.** A recorded movement went to
17
Added:
**Reaching failure is an invariant, not data.** A recorded effort went to
18
18
failure by construction; that is what separates a stimulus from mere exercise.
19
19
What varies is load, the reps it happened to yield, and whether anything carried
20
20
the effort past failure.
@@ -49,7 +49,7 @@
49
49
50
50
| Principle | Enforced at |
51
51
|-------------------------------------------|-------------------------------------------------------|
52
Removed:
| 6-12 stimulus window | `Prescription.rep_limits` |
52
Added:
| 6-12 stimulus window | `Rep_range.limits` |
53
53
| one drive to failure per slot | `Prescription`'s shape — no set count exists to raise |
54
54
| isolation into compound, sharing a muscle | `Exercise.may_pre_exhaust` |
55
55
| every other day, then two days off | `Routine.recovery_after` |
@@ -100,14 +100,14 @@
100
100
101
101
### Factual — the record
102
102
103
Removed:
- **Stimulus** — one drive to failure: how it was delivered, what each movement
103
Added:
- **Stimulus** — one drive to failure: how it was delivered, what each effort
104
104
lifted, and whether extensions carried it past failure. Warm-ups sit alongside
105
105
and carry no outcome, so a warm-up cannot reach failure by type.
106
106
- **Entry** — a workout performed against its prescription. Starting one requires
107
107
a clearance; adding a stimulus requires that the prescription actually calls
108
108
for it.
109
109
- **Logbook** — every entry, plus the questions history answers: what was
110
Removed:
performed last, how long since the last finished workout, and what a movement
110
Added:
performed last, how long since the last finished workout, and what an effort
111
111
has done over time. Evidence is dated, because a stall is defined by two weeks
112
112
of nothing.
113
113
lib/core/evidence.ml
@@ -37,7 +37,7 @@
37
37
let to_int value = value
38
38
end
39
39
40
Removed:
module Movement = struct
40
Added:
module Effort = struct
41
41
type t = {
42
42
exercise : Exercise.t;
43
43
load : Load.t;
@@ -66,28 +66,28 @@
66
66
end
67
67
68
68
type delivery =
69
Removed:
| Single of Movement.t
70
Removed:
| Pair of { first : Movement.t; second : Movement.t }
69
Added:
| Single of Effort.t
70
Added:
| Pair of { first : Effort.t; second : Effort.t }
71
71
72
72
type t = { delivery : delivery }
73
73
74
74
let make delivery = { delivery }
75
75
let delivery t = t.delivery
76
76
77
Removed:
let movements t =
77
Added:
let efforts t =
78
78
match t.delivery with
79
Removed:
| Single m -> [ m ]
79
Added:
| Single effort -> [ effort ]
80
80
| Pair { first; second } -> [ first; second ]
81
81
82
Removed:
let exercises t = List.map Movement.exercise (movements t)
83
Removed:
let extensions t = List.concat_map Movement.extensions (movements t)
82
Added:
let exercises t = List.map Effort.exercise (efforts t)
83
Added:
let extensions t = List.concat_map Effort.extensions (efforts t)
84
84
let is_extended t = extensions t <> []
85
85
86
86
let pp ppf t =
87
87
match t.delivery with
88
Removed:
| Single m -> Movement.pp ppf m
88
Added:
| Single effort -> Effort.pp ppf effort
89
89
| Pair { first; second } ->
90
Removed:
Format.fprintf ppf "%a then %a" Movement.pp first Movement.pp second
90
Added:
Format.fprintf ppf "%a then %a" Effort.pp first Effort.pp second
91
91
end
92
92
93
93
module Feedback = struct
@@ -260,7 +260,7 @@
260
260
261
261
type observation = {
262
262
exercise : Exercise.t;
263
Removed:
movement : Stimulus.Movement.t;
263
Added:
effort : Stimulus.Effort.t;
264
264
performed_at : Recovery.timestamp;
265
265
}
266
266
@@ -283,11 +283,11 @@
283
283
284
284
let observations_of workout =
285
285
Workout.stimuli workout
286
Removed:
|> List.concat_map Stimulus.movements
287
Removed:
|> List.map (fun movement ->
286
Added:
|> List.concat_map Stimulus.efforts
287
Added:
|> List.map (fun effort ->
288
288
{
289
Removed:
exercise = Stimulus.Movement.exercise movement;
290
Removed:
movement;
289
Added:
exercise = Stimulus.Effort.exercise effort;
290
Added:
effort;
291
291
performed_at = Workout.started_at workout;
292
292
})
293
293
lib/core/evidence.mli
@@ -28,7 +28,7 @@
28
28
val to_int : t -> int
29
29
end
30
30
31
Removed:
module Movement : sig
31
Added:
module Effort : sig
32
32
type t
33
33
34
34
val make :
@@ -43,8 +43,8 @@
43
43
end
44
44
45
45
type delivery =
46
Removed:
| Single of Movement.t
47
Removed:
| Pair of { first : Movement.t; second : Movement.t }
46
Added:
| Single of Effort.t
47
Added:
| Pair of { first : Effort.t; second : Effort.t }
48
48
49
49
type t
50
50
@@ -53,7 +53,7 @@
53
53
54
54
val delivery : t -> delivery
55
55
56
Removed:
val movements : t -> Movement.t list
56
Added:
val efforts : t -> Effort.t list
57
57
(** Performance order. *)
58
58
59
59
val exercises : t -> Exercise.t list
@@ -142,7 +142,7 @@
142
142
143
143
type observation = {
144
144
exercise : Exercise.t;
145
Removed:
movement : Stimulus.Movement.t;
145
Added:
effort : Stimulus.Effort.t;
146
146
performed_at : Recovery.timestamp;
147
147
}
148
148
lib/core/progression.ml
@@ -1,15 +1,15 @@
1
Removed:
module Movement = Evidence.Stimulus.Movement
1
Added:
module Effort = Evidence.Stimulus.Effort
2
2
3
3
let beats ~previous ~current =
4
4
let load =
5
5
Float.compare
6
Removed:
(Movement.load current |> Evidence.Stimulus.Load.to_kg)
7
Removed:
(Movement.load previous |> Evidence.Stimulus.Load.to_kg)
6
Added:
(Effort.load current |> Evidence.Stimulus.Load.to_kg)
7
Added:
(Effort.load previous |> Evidence.Stimulus.Load.to_kg)
8
8
in
9
9
let reps =
10
10
Int.compare
11
Removed:
(Movement.reps current |> Evidence.Stimulus.Reps.to_int)
12
Removed:
(Movement.reps previous |> Evidence.Stimulus.Reps.to_int)
11
Added:
(Effort.reps current |> Evidence.Stimulus.Reps.to_int)
12
Added:
(Effort.reps previous |> Evidence.Stimulus.Reps.to_int)
13
13
in
14
14
load > 0 || (load = 0 && reps > 0)
15
15
@@ -31,7 +31,7 @@
31
31
let rec scan found = function
32
32
| (previous : Evidence.Log.observation) :: (current :: _ as rest) ->
33
33
let found =
34
Removed:
if beats ~previous:previous.movement ~current:current.movement then
34
Added:
if beats ~previous:previous.effort ~current:current.effort then
35
35
Some current
36
36
else found
37
37
in
@@ -99,11 +99,11 @@
99
99
100
100
(* The trigger is absolute, so the window's ceiling never gates the verdict —
101
101
only its floor does. *)
102
Removed:
let judge_load ~rep_range movement =
103
Removed:
let reps = Movement.reps movement |> Evidence.Stimulus.Reps.to_int in
102
Added:
let judge_load ~rep_range effort =
103
Added:
let reps = Effort.reps effort |> Evidence.Stimulus.Reps.to_int in
104
104
let min_reps, _ = Prescription.Rep_range.bounds rep_range in
105
105
if reps >= load_increase_trigger then
106
Removed:
let low, high = load_increase ~current:(Movement.load movement) in
106
Added:
let low, high = load_increase ~current:(Effort.load effort) in
107
107
Increase (low, high)
108
108
else if reps < min_reps then Too_heavy
109
109
else Hold
lib/core/progression.mli
@@ -39,7 +39,7 @@
39
39
40
40
val judge_load :
41
41
rep_range:Prescription.Rep_range.t ->
42
Removed:
Evidence.Stimulus.Movement.t ->
42
Added:
Evidence.Stimulus.Effort.t ->
43
43
load_verdict
44
44
(** Only the range floor matters: below it is [Too_heavy]; at twelve reps the
45
45
verdict is [Increase]. *)
lib/web/decode.ml
@@ -69,7 +69,7 @@
69
69
ok
70
70
(Evidence.Stimulus.make
71
71
(Evidence.Stimulus.Single
72
Removed:
(Evidence.Stimulus.Movement.make ~exercise ~load ~reps ~outcome)))
72
Added:
(Evidence.Stimulus.Effort.make ~exercise ~load ~reps ~outcome)))
73
73
| ( Prescription.Stimulus.Pre_exhaust { isolation; compound },
74
74
Pair { iso_load; iso_reps; comp_load; comp_reps; extension } ) ->
75
75
let second_outcome =
@@ -78,11 +78,11 @@
78
78
| Some extension -> Evidence.Stimulus.Beyond_failure (extension, [])
79
79
in
80
80
let first =
81
Removed:
Evidence.Stimulus.Movement.make ~exercise:isolation ~load:iso_load
81
Added:
Evidence.Stimulus.Effort.make ~exercise:isolation ~load:iso_load
82
82
~reps:iso_reps ~outcome:Evidence.Stimulus.Positive_failure
83
83
in
84
84
let second =
85
Removed:
Evidence.Stimulus.Movement.make ~exercise:compound ~load:comp_load
85
Added:
Evidence.Stimulus.Effort.make ~exercise:compound ~load:comp_load
86
86
~reps:comp_reps ~outcome:second_outcome
87
87
in
88
88
ok (Evidence.Stimulus.make (Evidence.Stimulus.Pair { first; second }))
lib/web/pages.ml
@@ -285,15 +285,15 @@
285
285
]
286
286
287
287
let describe_stimulus stimulus =
288
Removed:
let movement movement =
288
Added:
let effort effort =
289
289
Format.asprintf "%s %g kg x %d"
290
Removed:
(Exercise.name (Evidence.Stimulus.Movement.exercise movement))
291
Removed:
(Evidence.Stimulus.Movement.load movement |> Evidence.Stimulus.Load.to_kg)
292
Removed:
(Evidence.Stimulus.Movement.reps movement |> Evidence.Stimulus.Reps.to_int)
290
Added:
(Exercise.name (Evidence.Stimulus.Effort.exercise effort))
291
Added:
(Evidence.Stimulus.Effort.load effort |> Evidence.Stimulus.Load.to_kg)
292
Added:
(Evidence.Stimulus.Effort.reps effort |> Evidence.Stimulus.Reps.to_int)
293
293
in
294
294
let body =
295
295
String.concat " into "
296
Removed:
(List.map movement (Evidence.Stimulus.movements stimulus))
296
Added:
(List.map effort (Evidence.Stimulus.efforts stimulus))
297
297
in
298
298
match Evidence.Stimulus.extensions stimulus with
299
299
| [] -> body
test/test_evidence.ml
@@ -18,7 +18,7 @@
18
18
let day n = at (n * 86_400)
19
19
20
20
let move ?(outcome = Stimulus.Positive_failure) id load_kg rep_count =
21
Removed:
Stimulus.Movement.make ~exercise:(get id) ~load:(load load_kg)
21
Added:
Stimulus.Effort.make ~exercise:(get id) ~load:(load load_kg)
22
22
~reps:(reps rep_count) ~outcome
23
23
24
24
let single id load r = Stimulus.make (Stimulus.Single (move id load r))
@@ -78,9 +78,7 @@
78
78
`Quick,
79
79
fun () ->
80
80
let s = single "curls" 40. 8 in
81
Removed:
Alcotest.(check int)
82
Removed:
"one movement" 1
83
Removed:
(List.length (Stimulus.movements s));
81
Added:
Alcotest.(check int) "one effort" 1 (List.length (Stimulus.efforts s));
84
82
Alcotest.(check bool) "not extended" false (Stimulus.is_extended s) );
85
83
( "a pre-exhaust pair is one stimulus, isolation first",
86
84
`Quick,
@@ -90,7 +88,7 @@
90
88
in
91
89
Alcotest.(check int)
92
90
"two movements" 2
93
Removed:
(List.length (Stimulus.movements s));
91
Added:
(List.length (Stimulus.efforts s));
94
92
Alcotest.(check (list string))
95
93
"isolation leads"
96
94
[ "Dumbbell Flyes"; "Incline Presses" ]
@@ -384,7 +382,7 @@
384
382
"12kg then 14kg" [ 12.; 14. ]
385
383
(List.map
386
384
(fun (o : Log.observation) ->
387
Removed:
Stimulus.Movement.load o.movement |> Stimulus.Load.to_kg)
385
Added:
Stimulus.Effort.load o.effort |> Stimulus.Load.to_kg)
388
386
history) );
389
387
( "observations are dated, so a stall can be measured",
390
388
`Quick,
test/test_progression.ml
@@ -20,12 +20,12 @@
20
20
21
21
let move ?(outcome = Stimulus.Positive_failure) ?(id = "laterals") load_kg
22
22
rep_count =
23
Removed:
Stimulus.Movement.make ~exercise:(get id) ~load:(load load_kg)
23
Added:
Stimulus.Effort.make ~exercise:(get id) ~load:(load load_kg)
24
24
~reps:(reps rep_count) ~outcome
25
25
26
26
(* An observation is a public record, so evidence can be written directly. *)
27
27
let seen ~on load r : Evidence.Log.observation =
28
Removed:
{ exercise = get "laterals"; movement = move load r; performed_at = day on }
28
Added:
{ exercise = get "laterals"; effort = move load r; performed_at = day on }
29
29
30
30
let assess_tests =
31
31
[
test/test_service.ml
@@ -20,7 +20,7 @@
20
20
let service () = S.make ~repo:(Memory_repo.create ())
21
21
22
22
let move id load_kg rep_count =
23
Removed:
Stimulus.Movement.make ~exercise:(get id) ~load:(load load_kg)
23
Added:
Stimulus.Effort.make ~exercise:(get id) ~load:(load load_kg)
24
24
~reps:(reps rep_count) ~outcome:Stimulus.Positive_failure
25
25
26
26
let single id load r = Stimulus.make (Stimulus.Single (move id load r))