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.

Commit
d60bee69aaa3c8a668f387f00fba4467aa302310
Author
Marius Peter <dev@marius-peter.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
Changed files
ARCHITECTURE.md
index 85cd7d7e..8a26895b 100644..100644
@@ -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
index 5eda035f..a1bb02df 100644..100644
@@ -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
index 1062af44..950a7428 100644..100644
@@ -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
index aec5ee66..7dcdd424 100644..100644
@@ -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
index c456cb82..f51f50e5 100644..100644
@@ -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
index 4f4fb3ea..c6e008cd 100644..100644
@@ -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
index 0cc1938a..36eb09cb 100644..100644
@@ -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
index 73c0865c..8c5b78e8 100644..100644
@@ -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
index d92b28fb..28b6755d 100644..100644
@@ -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
index f4f8c577..72220304 100644..100644
@@ -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))