tthhanh commited on
Commit
7294c15
·
1 Parent(s): e7a202c

feat: ingest real exercise dataset into knowledge artifacts

Browse files
data/knowledge/exercises.json ADDED
@@ -0,0 +1,523 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "source_dataset": "DORTROX/Exercises-Data",
3
+ "generated_at": "2026-06-14T10:09:45.365188+00:00",
4
+ "exercise_count": 43,
5
+ "exercises": [
6
+ {
7
+ "source_dataset": "DORTROX/Exercises-Data",
8
+ "exercise_label": "advancedtransitionlunge",
9
+ "title": "Advancedtransitionlunge",
10
+ "aliases": [],
11
+ "category": null,
12
+ "equipment": [],
13
+ "primary_muscles": [],
14
+ "secondary_muscles": [],
15
+ "instructions": [],
16
+ "description": null
17
+ },
18
+ {
19
+ "source_dataset": "DORTROX/Exercises-Data",
20
+ "exercise_label": "alignedsquat",
21
+ "title": "Alignedsquat",
22
+ "aliases": [],
23
+ "category": null,
24
+ "equipment": [],
25
+ "primary_muscles": [],
26
+ "secondary_muscles": [],
27
+ "instructions": [],
28
+ "description": null
29
+ },
30
+ {
31
+ "source_dataset": "DORTROX/Exercises-Data",
32
+ "exercise_label": "benddownandruninplace",
33
+ "title": "Benddownandruninplace",
34
+ "aliases": [],
35
+ "category": null,
36
+ "equipment": [],
37
+ "primary_muscles": [],
38
+ "secondary_muscles": [],
39
+ "instructions": [],
40
+ "description": null
41
+ },
42
+ {
43
+ "source_dataset": "DORTROX/Exercises-Data",
44
+ "exercise_label": "bulgariansplitsquat",
45
+ "title": "Bulgariansplitsquat",
46
+ "aliases": [],
47
+ "category": null,
48
+ "equipment": [],
49
+ "primary_muscles": [],
50
+ "secondary_muscles": [],
51
+ "instructions": [],
52
+ "description": null
53
+ },
54
+ {
55
+ "source_dataset": "DORTROX/Exercises-Data",
56
+ "exercise_label": "bulgariansquat",
57
+ "title": "Bulgariansquat",
58
+ "aliases": [],
59
+ "category": null,
60
+ "equipment": [],
61
+ "primary_muscles": [],
62
+ "secondary_muscles": [],
63
+ "instructions": [],
64
+ "description": null
65
+ },
66
+ {
67
+ "source_dataset": "DORTROX/Exercises-Data",
68
+ "exercise_label": "calfraise",
69
+ "title": "Calfraise",
70
+ "aliases": [],
71
+ "category": null,
72
+ "equipment": [],
73
+ "primary_muscles": [],
74
+ "secondary_muscles": [],
75
+ "instructions": [],
76
+ "description": null
77
+ },
78
+ {
79
+ "source_dataset": "DORTROX/Exercises-Data",
80
+ "exercise_label": "chairplank",
81
+ "title": "Chairplank",
82
+ "aliases": [],
83
+ "category": null,
84
+ "equipment": [],
85
+ "primary_muscles": [],
86
+ "secondary_muscles": [],
87
+ "instructions": [],
88
+ "description": null
89
+ },
90
+ {
91
+ "source_dataset": "DORTROX/Exercises-Data",
92
+ "exercise_label": "extendedplank",
93
+ "title": "Extendedplank",
94
+ "aliases": [],
95
+ "category": null,
96
+ "equipment": [],
97
+ "primary_muscles": [],
98
+ "secondary_muscles": [],
99
+ "instructions": [],
100
+ "description": null
101
+ },
102
+ {
103
+ "source_dataset": "DORTROX/Exercises-Data",
104
+ "exercise_label": "frontplank",
105
+ "title": "Frontplank",
106
+ "aliases": [],
107
+ "category": null,
108
+ "equipment": [],
109
+ "primary_muscles": [],
110
+ "secondary_muscles": [],
111
+ "instructions": [],
112
+ "description": null
113
+ },
114
+ {
115
+ "source_dataset": "DORTROX/Exercises-Data",
116
+ "exercise_label": "inclinedlunge",
117
+ "title": "Inclinedlunge",
118
+ "aliases": [],
119
+ "category": null,
120
+ "equipment": [],
121
+ "primary_muscles": [],
122
+ "secondary_muscles": [],
123
+ "instructions": [],
124
+ "description": null
125
+ },
126
+ {
127
+ "source_dataset": "DORTROX/Exercises-Data",
128
+ "exercise_label": "inclinelunge",
129
+ "title": "Inclinelunge",
130
+ "aliases": [],
131
+ "category": null,
132
+ "equipment": [],
133
+ "primary_muscles": [],
134
+ "secondary_muscles": [],
135
+ "instructions": [],
136
+ "description": null
137
+ },
138
+ {
139
+ "source_dataset": "DORTROX/Exercises-Data",
140
+ "exercise_label": "isometricsquat",
141
+ "title": "Isometricsquat",
142
+ "aliases": [],
143
+ "category": null,
144
+ "equipment": [],
145
+ "primary_muscles": [],
146
+ "secondary_muscles": [],
147
+ "instructions": [],
148
+ "description": null
149
+ },
150
+ {
151
+ "source_dataset": "DORTROX/Exercises-Data",
152
+ "exercise_label": "jumpingjack",
153
+ "title": "Jumpingjack",
154
+ "aliases": [],
155
+ "category": null,
156
+ "equipment": [],
157
+ "primary_muscles": [],
158
+ "secondary_muscles": [],
159
+ "instructions": [],
160
+ "description": null
161
+ },
162
+ {
163
+ "source_dataset": "DORTROX/Exercises-Data",
164
+ "exercise_label": "kneeraisecombinedwitharmlift",
165
+ "title": "Kneeraisecombinedwitharmlift",
166
+ "aliases": [],
167
+ "category": null,
168
+ "equipment": [],
169
+ "primary_muscles": [],
170
+ "secondary_muscles": [],
171
+ "instructions": [],
172
+ "description": null
173
+ },
174
+ {
175
+ "source_dataset": "DORTROX/Exercises-Data",
176
+ "exercise_label": "low_impactjumpingjacks",
177
+ "title": "Low Impactjumpingjacks",
178
+ "aliases": [],
179
+ "category": null,
180
+ "equipment": [],
181
+ "primary_muscles": [],
182
+ "secondary_muscles": [],
183
+ "instructions": [],
184
+ "description": null
185
+ },
186
+ {
187
+ "source_dataset": "DORTROX/Exercises-Data",
188
+ "exercise_label": "lowimpactheeltobutt",
189
+ "title": "Lowimpactheeltobutt",
190
+ "aliases": [],
191
+ "category": null,
192
+ "equipment": [],
193
+ "primary_muscles": [],
194
+ "secondary_muscles": [],
195
+ "instructions": [],
196
+ "description": null
197
+ },
198
+ {
199
+ "source_dataset": "DORTROX/Exercises-Data",
200
+ "exercise_label": "lowimpactjumpingjack",
201
+ "title": "Lowimpactjumpingjack",
202
+ "aliases": [],
203
+ "category": null,
204
+ "equipment": [],
205
+ "primary_muscles": [],
206
+ "secondary_muscles": [],
207
+ "instructions": [],
208
+ "description": null
209
+ },
210
+ {
211
+ "source_dataset": "DORTROX/Exercises-Data",
212
+ "exercise_label": "lunge",
213
+ "title": "Lunge",
214
+ "aliases": [],
215
+ "category": null,
216
+ "equipment": [],
217
+ "primary_muscles": [],
218
+ "secondary_muscles": [],
219
+ "instructions": [],
220
+ "description": null
221
+ },
222
+ {
223
+ "source_dataset": "DORTROX/Exercises-Data",
224
+ "exercise_label": "lunge",
225
+ "title": "Lunge",
226
+ "aliases": [],
227
+ "category": null,
228
+ "equipment": [],
229
+ "primary_muscles": [],
230
+ "secondary_muscles": [],
231
+ "instructions": [],
232
+ "description": null
233
+ },
234
+ {
235
+ "source_dataset": "DORTROX/Exercises-Data",
236
+ "exercise_label": "lungewithbalance",
237
+ "title": "Lungewithbalance",
238
+ "aliases": [],
239
+ "category": null,
240
+ "equipment": [],
241
+ "primary_muscles": [],
242
+ "secondary_muscles": [],
243
+ "instructions": [],
244
+ "description": null
245
+ },
246
+ {
247
+ "source_dataset": "DORTROX/Exercises-Data",
248
+ "exercise_label": "lungewithkneelift",
249
+ "title": "Lungewithkneelift",
250
+ "aliases": [],
251
+ "category": null,
252
+ "equipment": [],
253
+ "primary_muscles": [],
254
+ "secondary_muscles": [],
255
+ "instructions": [],
256
+ "description": null
257
+ },
258
+ {
259
+ "source_dataset": "DORTROX/Exercises-Data",
260
+ "exercise_label": "lungewithkneestrike",
261
+ "title": "Lungewithkneestrike",
262
+ "aliases": [],
263
+ "category": null,
264
+ "equipment": [],
265
+ "primary_muscles": [],
266
+ "secondary_muscles": [],
267
+ "instructions": [],
268
+ "description": null
269
+ },
270
+ {
271
+ "source_dataset": "DORTROX/Exercises-Data",
272
+ "exercise_label": "neutralalignedsquat",
273
+ "title": "Neutralalignedsquat",
274
+ "aliases": [],
275
+ "category": null,
276
+ "equipment": [],
277
+ "primary_muscles": [],
278
+ "secondary_muscles": [],
279
+ "instructions": [],
280
+ "description": null
281
+ },
282
+ {
283
+ "source_dataset": "DORTROX/Exercises-Data",
284
+ "exercise_label": "plantarraisecalf",
285
+ "title": "Plantarraisecalf",
286
+ "aliases": [],
287
+ "category": null,
288
+ "equipment": [],
289
+ "primary_muscles": [],
290
+ "secondary_muscles": [],
291
+ "instructions": [],
292
+ "description": null
293
+ },
294
+ {
295
+ "source_dataset": "DORTROX/Exercises-Data",
296
+ "exercise_label": "polifootwear",
297
+ "title": "Polifootwear",
298
+ "aliases": [],
299
+ "category": null,
300
+ "equipment": [],
301
+ "primary_muscles": [],
302
+ "secondary_muscles": [],
303
+ "instructions": [],
304
+ "description": null
305
+ },
306
+ {
307
+ "source_dataset": "DORTROX/Exercises-Data",
308
+ "exercise_label": "poliopensandcloses",
309
+ "title": "Poliopensandcloses",
310
+ "aliases": [],
311
+ "category": null,
312
+ "equipment": [],
313
+ "primary_muscles": [],
314
+ "secondary_muscles": [],
315
+ "instructions": [],
316
+ "description": null
317
+ },
318
+ {
319
+ "source_dataset": "DORTROX/Exercises-Data",
320
+ "exercise_label": "polishoe",
321
+ "title": "Polishoe",
322
+ "aliases": [],
323
+ "category": null,
324
+ "equipment": [],
325
+ "primary_muscles": [],
326
+ "secondary_muscles": [],
327
+ "instructions": [],
328
+ "description": null
329
+ },
330
+ {
331
+ "source_dataset": "DORTROX/Exercises-Data",
332
+ "exercise_label": "runninginplace",
333
+ "title": "Runninginplace",
334
+ "aliases": [],
335
+ "category": null,
336
+ "equipment": [],
337
+ "primary_muscles": [],
338
+ "secondary_muscles": [],
339
+ "instructions": [],
340
+ "description": null
341
+ },
342
+ {
343
+ "source_dataset": "DORTROX/Exercises-Data",
344
+ "exercise_label": "sideshuffle",
345
+ "title": "Sideshuffle",
346
+ "aliases": [],
347
+ "category": null,
348
+ "equipment": [],
349
+ "primary_muscles": [],
350
+ "secondary_muscles": [],
351
+ "instructions": [],
352
+ "description": null
353
+ },
354
+ {
355
+ "source_dataset": "DORTROX/Exercises-Data",
356
+ "exercise_label": "sinkwithbalance",
357
+ "title": "Sinkwithbalance",
358
+ "aliases": [],
359
+ "category": null,
360
+ "equipment": [],
361
+ "primary_muscles": [],
362
+ "secondary_muscles": [],
363
+ "instructions": [],
364
+ "description": null
365
+ },
366
+ {
367
+ "source_dataset": "DORTROX/Exercises-Data",
368
+ "exercise_label": "sky",
369
+ "title": "Sky",
370
+ "aliases": [],
371
+ "category": null,
372
+ "equipment": [],
373
+ "primary_muscles": [],
374
+ "secondary_muscles": [],
375
+ "instructions": [],
376
+ "description": null
377
+ },
378
+ {
379
+ "source_dataset": "DORTROX/Exercises-Data",
380
+ "exercise_label": "squat",
381
+ "title": "Squat",
382
+ "aliases": [],
383
+ "category": null,
384
+ "equipment": [],
385
+ "primary_muscles": [],
386
+ "secondary_muscles": [],
387
+ "instructions": [],
388
+ "description": null
389
+ },
390
+ {
391
+ "source_dataset": "DORTROX/Exercises-Data",
392
+ "exercise_label": "squat",
393
+ "title": "Squat",
394
+ "aliases": [],
395
+ "category": null,
396
+ "equipment": [],
397
+ "primary_muscles": [],
398
+ "secondary_muscles": [],
399
+ "instructions": [],
400
+ "description": null
401
+ },
402
+ {
403
+ "source_dataset": "DORTROX/Exercises-Data",
404
+ "exercise_label": "squatandlateralshif",
405
+ "title": "Squatandlateralshif",
406
+ "aliases": [],
407
+ "category": null,
408
+ "equipment": [],
409
+ "primary_muscles": [],
410
+ "secondary_muscles": [],
411
+ "instructions": [],
412
+ "description": null
413
+ },
414
+ {
415
+ "source_dataset": "DORTROX/Exercises-Data",
416
+ "exercise_label": "squatandraisearms",
417
+ "title": "Squatandraisearms",
418
+ "aliases": [],
419
+ "category": null,
420
+ "equipment": [],
421
+ "primary_muscles": [],
422
+ "secondary_muscles": [],
423
+ "instructions": [],
424
+ "description": null
425
+ },
426
+ {
427
+ "source_dataset": "DORTROX/Exercises-Data",
428
+ "exercise_label": "squatandtouchthefootmp4",
429
+ "title": "Squatandtouchthefootmp4",
430
+ "aliases": [],
431
+ "category": null,
432
+ "equipment": [],
433
+ "primary_muscles": [],
434
+ "secondary_muscles": [],
435
+ "instructions": [],
436
+ "description": null
437
+ },
438
+ {
439
+ "source_dataset": "DORTROX/Exercises-Data",
440
+ "exercise_label": "squatwithbackwardlunge",
441
+ "title": "Squatwithbackwardlunge",
442
+ "aliases": [],
443
+ "category": null,
444
+ "equipment": [],
445
+ "primary_muscles": [],
446
+ "secondary_muscles": [],
447
+ "instructions": [],
448
+ "description": null
449
+ },
450
+ {
451
+ "source_dataset": "DORTROX/Exercises-Data",
452
+ "exercise_label": "squatwithinsistence2x",
453
+ "title": "Squatwithinsistence2x",
454
+ "aliases": [],
455
+ "category": null,
456
+ "equipment": [],
457
+ "primary_muscles": [],
458
+ "secondary_muscles": [],
459
+ "instructions": [],
460
+ "description": null
461
+ },
462
+ {
463
+ "source_dataset": "DORTROX/Exercises-Data",
464
+ "exercise_label": "stationaryrunning",
465
+ "title": "Stationaryrunning",
466
+ "aliases": [],
467
+ "category": null,
468
+ "equipment": [],
469
+ "primary_muscles": [],
470
+ "secondary_muscles": [],
471
+ "instructions": [],
472
+ "description": null
473
+ },
474
+ {
475
+ "source_dataset": "DORTROX/Exercises-Data",
476
+ "exercise_label": "stiffunilateral",
477
+ "title": "Stiffunilateral",
478
+ "aliases": [],
479
+ "category": null,
480
+ "equipment": [],
481
+ "primary_muscles": [],
482
+ "secondary_muscles": [],
483
+ "instructions": [],
484
+ "description": null
485
+ },
486
+ {
487
+ "source_dataset": "DORTROX/Exercises-Data",
488
+ "exercise_label": "sumosquat",
489
+ "title": "Sumosquat",
490
+ "aliases": [],
491
+ "category": null,
492
+ "equipment": [],
493
+ "primary_muscles": [],
494
+ "secondary_muscles": [],
495
+ "instructions": [],
496
+ "description": null
497
+ },
498
+ {
499
+ "source_dataset": "DORTROX/Exercises-Data",
500
+ "exercise_label": "sumosquat",
501
+ "title": "Sumosquat",
502
+ "aliases": [],
503
+ "category": null,
504
+ "equipment": [],
505
+ "primary_muscles": [],
506
+ "secondary_muscles": [],
507
+ "instructions": [],
508
+ "description": null
509
+ },
510
+ {
511
+ "source_dataset": "DORTROX/Exercises-Data",
512
+ "exercise_label": "unilateralsquat",
513
+ "title": "Unilateralsquat",
514
+ "aliases": [],
515
+ "category": null,
516
+ "equipment": [],
517
+ "primary_muscles": [],
518
+ "secondary_muscles": [],
519
+ "instructions": [],
520
+ "description": null
521
+ }
522
+ ]
523
+ }
data/knowledge_cards/external_exercise_dataset_pack.json ADDED
@@ -0,0 +1,782 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "source_dataset": "DORTROX/Exercises-Data",
3
+ "generated_at": "2026-06-14T10:09:45.368287+00:00",
4
+ "normalized_exercise_count": 43,
5
+ "card_count": 43,
6
+ "cards": [
7
+ {
8
+ "card_id": "exercise:advancedtransitionlunge",
9
+ "card_type": "exercise",
10
+ "labels": [
11
+ "advancedtransitionlunge"
12
+ ],
13
+ "title": "Advancedtransitionlunge",
14
+ "summary": "A Advancedtransitionlunge summary should stay grounded in structured rep evidence.",
15
+ "evidence_rules": [
16
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
17
+ "Treat valid detected variations as context unless issue markers show a separate problem."
18
+ ],
19
+ "coaching_points": [
20
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
21
+ ],
22
+ "allowed_interpretations": [],
23
+ "related_cards": []
24
+ },
25
+ {
26
+ "card_id": "exercise:alignedsquat",
27
+ "card_type": "exercise",
28
+ "labels": [
29
+ "alignedsquat"
30
+ ],
31
+ "title": "Alignedsquat",
32
+ "summary": "A Alignedsquat summary should stay grounded in structured rep evidence.",
33
+ "evidence_rules": [
34
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
35
+ "Treat valid detected variations as context unless issue markers show a separate problem."
36
+ ],
37
+ "coaching_points": [
38
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
39
+ ],
40
+ "allowed_interpretations": [],
41
+ "related_cards": []
42
+ },
43
+ {
44
+ "card_id": "exercise:benddownandruninplace",
45
+ "card_type": "exercise",
46
+ "labels": [
47
+ "benddownandruninplace"
48
+ ],
49
+ "title": "Benddownandruninplace",
50
+ "summary": "A Benddownandruninplace summary should stay grounded in structured rep evidence.",
51
+ "evidence_rules": [
52
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
53
+ "Treat valid detected variations as context unless issue markers show a separate problem."
54
+ ],
55
+ "coaching_points": [
56
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
57
+ ],
58
+ "allowed_interpretations": [],
59
+ "related_cards": []
60
+ },
61
+ {
62
+ "card_id": "exercise:bulgariansplitsquat",
63
+ "card_type": "exercise",
64
+ "labels": [
65
+ "bulgariansplitsquat"
66
+ ],
67
+ "title": "Bulgariansplitsquat",
68
+ "summary": "A Bulgariansplitsquat summary should stay grounded in structured rep evidence.",
69
+ "evidence_rules": [
70
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
71
+ "Treat valid detected variations as context unless issue markers show a separate problem."
72
+ ],
73
+ "coaching_points": [
74
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
75
+ ],
76
+ "allowed_interpretations": [],
77
+ "related_cards": []
78
+ },
79
+ {
80
+ "card_id": "exercise:bulgariansquat",
81
+ "card_type": "exercise",
82
+ "labels": [
83
+ "bulgariansquat"
84
+ ],
85
+ "title": "Bulgariansquat",
86
+ "summary": "A Bulgariansquat summary should stay grounded in structured rep evidence.",
87
+ "evidence_rules": [
88
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
89
+ "Treat valid detected variations as context unless issue markers show a separate problem."
90
+ ],
91
+ "coaching_points": [
92
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
93
+ ],
94
+ "allowed_interpretations": [],
95
+ "related_cards": []
96
+ },
97
+ {
98
+ "card_id": "exercise:calfraise",
99
+ "card_type": "exercise",
100
+ "labels": [
101
+ "calfraise"
102
+ ],
103
+ "title": "Calfraise",
104
+ "summary": "A Calfraise summary should stay grounded in structured rep evidence.",
105
+ "evidence_rules": [
106
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
107
+ "Treat valid detected variations as context unless issue markers show a separate problem."
108
+ ],
109
+ "coaching_points": [
110
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
111
+ ],
112
+ "allowed_interpretations": [],
113
+ "related_cards": []
114
+ },
115
+ {
116
+ "card_id": "exercise:chairplank",
117
+ "card_type": "exercise",
118
+ "labels": [
119
+ "chairplank"
120
+ ],
121
+ "title": "Chairplank",
122
+ "summary": "A Chairplank summary should stay grounded in structured rep evidence.",
123
+ "evidence_rules": [
124
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
125
+ "Treat valid detected variations as context unless issue markers show a separate problem."
126
+ ],
127
+ "coaching_points": [
128
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
129
+ ],
130
+ "allowed_interpretations": [],
131
+ "related_cards": []
132
+ },
133
+ {
134
+ "card_id": "exercise:extendedplank",
135
+ "card_type": "exercise",
136
+ "labels": [
137
+ "extendedplank"
138
+ ],
139
+ "title": "Extendedplank",
140
+ "summary": "A Extendedplank summary should stay grounded in structured rep evidence.",
141
+ "evidence_rules": [
142
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
143
+ "Treat valid detected variations as context unless issue markers show a separate problem."
144
+ ],
145
+ "coaching_points": [
146
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
147
+ ],
148
+ "allowed_interpretations": [],
149
+ "related_cards": []
150
+ },
151
+ {
152
+ "card_id": "exercise:frontplank",
153
+ "card_type": "exercise",
154
+ "labels": [
155
+ "frontplank"
156
+ ],
157
+ "title": "Frontplank",
158
+ "summary": "A Frontplank summary should stay grounded in structured rep evidence.",
159
+ "evidence_rules": [
160
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
161
+ "Treat valid detected variations as context unless issue markers show a separate problem."
162
+ ],
163
+ "coaching_points": [
164
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
165
+ ],
166
+ "allowed_interpretations": [],
167
+ "related_cards": []
168
+ },
169
+ {
170
+ "card_id": "exercise:inclinedlunge",
171
+ "card_type": "exercise",
172
+ "labels": [
173
+ "inclinedlunge"
174
+ ],
175
+ "title": "Inclinedlunge",
176
+ "summary": "A Inclinedlunge summary should stay grounded in structured rep evidence.",
177
+ "evidence_rules": [
178
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
179
+ "Treat valid detected variations as context unless issue markers show a separate problem."
180
+ ],
181
+ "coaching_points": [
182
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
183
+ ],
184
+ "allowed_interpretations": [],
185
+ "related_cards": []
186
+ },
187
+ {
188
+ "card_id": "exercise:inclinelunge",
189
+ "card_type": "exercise",
190
+ "labels": [
191
+ "inclinelunge"
192
+ ],
193
+ "title": "Inclinelunge",
194
+ "summary": "A Inclinelunge summary should stay grounded in structured rep evidence.",
195
+ "evidence_rules": [
196
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
197
+ "Treat valid detected variations as context unless issue markers show a separate problem."
198
+ ],
199
+ "coaching_points": [
200
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
201
+ ],
202
+ "allowed_interpretations": [],
203
+ "related_cards": []
204
+ },
205
+ {
206
+ "card_id": "exercise:isometricsquat",
207
+ "card_type": "exercise",
208
+ "labels": [
209
+ "isometricsquat"
210
+ ],
211
+ "title": "Isometricsquat",
212
+ "summary": "A Isometricsquat summary should stay grounded in structured rep evidence.",
213
+ "evidence_rules": [
214
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
215
+ "Treat valid detected variations as context unless issue markers show a separate problem."
216
+ ],
217
+ "coaching_points": [
218
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
219
+ ],
220
+ "allowed_interpretations": [],
221
+ "related_cards": []
222
+ },
223
+ {
224
+ "card_id": "exercise:jumpingjack",
225
+ "card_type": "exercise",
226
+ "labels": [
227
+ "jumpingjack"
228
+ ],
229
+ "title": "Jumpingjack",
230
+ "summary": "A Jumpingjack summary should stay grounded in structured rep evidence.",
231
+ "evidence_rules": [
232
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
233
+ "Treat valid detected variations as context unless issue markers show a separate problem."
234
+ ],
235
+ "coaching_points": [
236
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
237
+ ],
238
+ "allowed_interpretations": [],
239
+ "related_cards": []
240
+ },
241
+ {
242
+ "card_id": "exercise:kneeraisecombinedwitharmlift",
243
+ "card_type": "exercise",
244
+ "labels": [
245
+ "kneeraisecombinedwitharmlift"
246
+ ],
247
+ "title": "Kneeraisecombinedwitharmlift",
248
+ "summary": "A Kneeraisecombinedwitharmlift summary should stay grounded in structured rep evidence.",
249
+ "evidence_rules": [
250
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
251
+ "Treat valid detected variations as context unless issue markers show a separate problem."
252
+ ],
253
+ "coaching_points": [
254
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
255
+ ],
256
+ "allowed_interpretations": [],
257
+ "related_cards": []
258
+ },
259
+ {
260
+ "card_id": "exercise:low_impactjumpingjacks",
261
+ "card_type": "exercise",
262
+ "labels": [
263
+ "low_impactjumpingjacks"
264
+ ],
265
+ "title": "Low Impactjumpingjacks",
266
+ "summary": "A Low Impactjumpingjacks summary should stay grounded in structured rep evidence.",
267
+ "evidence_rules": [
268
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
269
+ "Treat valid detected variations as context unless issue markers show a separate problem."
270
+ ],
271
+ "coaching_points": [
272
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
273
+ ],
274
+ "allowed_interpretations": [],
275
+ "related_cards": []
276
+ },
277
+ {
278
+ "card_id": "exercise:lowimpactheeltobutt",
279
+ "card_type": "exercise",
280
+ "labels": [
281
+ "lowimpactheeltobutt"
282
+ ],
283
+ "title": "Lowimpactheeltobutt",
284
+ "summary": "A Lowimpactheeltobutt summary should stay grounded in structured rep evidence.",
285
+ "evidence_rules": [
286
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
287
+ "Treat valid detected variations as context unless issue markers show a separate problem."
288
+ ],
289
+ "coaching_points": [
290
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
291
+ ],
292
+ "allowed_interpretations": [],
293
+ "related_cards": []
294
+ },
295
+ {
296
+ "card_id": "exercise:lowimpactjumpingjack",
297
+ "card_type": "exercise",
298
+ "labels": [
299
+ "lowimpactjumpingjack"
300
+ ],
301
+ "title": "Lowimpactjumpingjack",
302
+ "summary": "A Lowimpactjumpingjack summary should stay grounded in structured rep evidence.",
303
+ "evidence_rules": [
304
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
305
+ "Treat valid detected variations as context unless issue markers show a separate problem."
306
+ ],
307
+ "coaching_points": [
308
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
309
+ ],
310
+ "allowed_interpretations": [],
311
+ "related_cards": []
312
+ },
313
+ {
314
+ "card_id": "exercise:lunge",
315
+ "card_type": "exercise",
316
+ "labels": [
317
+ "lunge"
318
+ ],
319
+ "title": "Lunge",
320
+ "summary": "A Lunge summary should stay grounded in structured rep evidence.",
321
+ "evidence_rules": [
322
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
323
+ "Treat valid detected variations as context unless issue markers show a separate problem."
324
+ ],
325
+ "coaching_points": [
326
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
327
+ ],
328
+ "allowed_interpretations": [],
329
+ "related_cards": []
330
+ },
331
+ {
332
+ "card_id": "exercise:lunge",
333
+ "card_type": "exercise",
334
+ "labels": [
335
+ "lunge"
336
+ ],
337
+ "title": "Lunge",
338
+ "summary": "A Lunge summary should stay grounded in structured rep evidence.",
339
+ "evidence_rules": [
340
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
341
+ "Treat valid detected variations as context unless issue markers show a separate problem."
342
+ ],
343
+ "coaching_points": [
344
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
345
+ ],
346
+ "allowed_interpretations": [],
347
+ "related_cards": []
348
+ },
349
+ {
350
+ "card_id": "exercise:lungewithbalance",
351
+ "card_type": "exercise",
352
+ "labels": [
353
+ "lungewithbalance"
354
+ ],
355
+ "title": "Lungewithbalance",
356
+ "summary": "A Lungewithbalance summary should stay grounded in structured rep evidence.",
357
+ "evidence_rules": [
358
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
359
+ "Treat valid detected variations as context unless issue markers show a separate problem."
360
+ ],
361
+ "coaching_points": [
362
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
363
+ ],
364
+ "allowed_interpretations": [],
365
+ "related_cards": []
366
+ },
367
+ {
368
+ "card_id": "exercise:lungewithkneelift",
369
+ "card_type": "exercise",
370
+ "labels": [
371
+ "lungewithkneelift"
372
+ ],
373
+ "title": "Lungewithkneelift",
374
+ "summary": "A Lungewithkneelift summary should stay grounded in structured rep evidence.",
375
+ "evidence_rules": [
376
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
377
+ "Treat valid detected variations as context unless issue markers show a separate problem."
378
+ ],
379
+ "coaching_points": [
380
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
381
+ ],
382
+ "allowed_interpretations": [],
383
+ "related_cards": []
384
+ },
385
+ {
386
+ "card_id": "exercise:lungewithkneestrike",
387
+ "card_type": "exercise",
388
+ "labels": [
389
+ "lungewithkneestrike"
390
+ ],
391
+ "title": "Lungewithkneestrike",
392
+ "summary": "A Lungewithkneestrike summary should stay grounded in structured rep evidence.",
393
+ "evidence_rules": [
394
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
395
+ "Treat valid detected variations as context unless issue markers show a separate problem."
396
+ ],
397
+ "coaching_points": [
398
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
399
+ ],
400
+ "allowed_interpretations": [],
401
+ "related_cards": []
402
+ },
403
+ {
404
+ "card_id": "exercise:neutralalignedsquat",
405
+ "card_type": "exercise",
406
+ "labels": [
407
+ "neutralalignedsquat"
408
+ ],
409
+ "title": "Neutralalignedsquat",
410
+ "summary": "A Neutralalignedsquat summary should stay grounded in structured rep evidence.",
411
+ "evidence_rules": [
412
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
413
+ "Treat valid detected variations as context unless issue markers show a separate problem."
414
+ ],
415
+ "coaching_points": [
416
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
417
+ ],
418
+ "allowed_interpretations": [],
419
+ "related_cards": []
420
+ },
421
+ {
422
+ "card_id": "exercise:plantarraisecalf",
423
+ "card_type": "exercise",
424
+ "labels": [
425
+ "plantarraisecalf"
426
+ ],
427
+ "title": "Plantarraisecalf",
428
+ "summary": "A Plantarraisecalf summary should stay grounded in structured rep evidence.",
429
+ "evidence_rules": [
430
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
431
+ "Treat valid detected variations as context unless issue markers show a separate problem."
432
+ ],
433
+ "coaching_points": [
434
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
435
+ ],
436
+ "allowed_interpretations": [],
437
+ "related_cards": []
438
+ },
439
+ {
440
+ "card_id": "exercise:polifootwear",
441
+ "card_type": "exercise",
442
+ "labels": [
443
+ "polifootwear"
444
+ ],
445
+ "title": "Polifootwear",
446
+ "summary": "A Polifootwear summary should stay grounded in structured rep evidence.",
447
+ "evidence_rules": [
448
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
449
+ "Treat valid detected variations as context unless issue markers show a separate problem."
450
+ ],
451
+ "coaching_points": [
452
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
453
+ ],
454
+ "allowed_interpretations": [],
455
+ "related_cards": []
456
+ },
457
+ {
458
+ "card_id": "exercise:poliopensandcloses",
459
+ "card_type": "exercise",
460
+ "labels": [
461
+ "poliopensandcloses"
462
+ ],
463
+ "title": "Poliopensandcloses",
464
+ "summary": "A Poliopensandcloses summary should stay grounded in structured rep evidence.",
465
+ "evidence_rules": [
466
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
467
+ "Treat valid detected variations as context unless issue markers show a separate problem."
468
+ ],
469
+ "coaching_points": [
470
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
471
+ ],
472
+ "allowed_interpretations": [],
473
+ "related_cards": []
474
+ },
475
+ {
476
+ "card_id": "exercise:polishoe",
477
+ "card_type": "exercise",
478
+ "labels": [
479
+ "polishoe"
480
+ ],
481
+ "title": "Polishoe",
482
+ "summary": "A Polishoe summary should stay grounded in structured rep evidence.",
483
+ "evidence_rules": [
484
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
485
+ "Treat valid detected variations as context unless issue markers show a separate problem."
486
+ ],
487
+ "coaching_points": [
488
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
489
+ ],
490
+ "allowed_interpretations": [],
491
+ "related_cards": []
492
+ },
493
+ {
494
+ "card_id": "exercise:runninginplace",
495
+ "card_type": "exercise",
496
+ "labels": [
497
+ "runninginplace"
498
+ ],
499
+ "title": "Runninginplace",
500
+ "summary": "A Runninginplace summary should stay grounded in structured rep evidence.",
501
+ "evidence_rules": [
502
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
503
+ "Treat valid detected variations as context unless issue markers show a separate problem."
504
+ ],
505
+ "coaching_points": [
506
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
507
+ ],
508
+ "allowed_interpretations": [],
509
+ "related_cards": []
510
+ },
511
+ {
512
+ "card_id": "exercise:sideshuffle",
513
+ "card_type": "exercise",
514
+ "labels": [
515
+ "sideshuffle"
516
+ ],
517
+ "title": "Sideshuffle",
518
+ "summary": "A Sideshuffle summary should stay grounded in structured rep evidence.",
519
+ "evidence_rules": [
520
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
521
+ "Treat valid detected variations as context unless issue markers show a separate problem."
522
+ ],
523
+ "coaching_points": [
524
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
525
+ ],
526
+ "allowed_interpretations": [],
527
+ "related_cards": []
528
+ },
529
+ {
530
+ "card_id": "exercise:sinkwithbalance",
531
+ "card_type": "exercise",
532
+ "labels": [
533
+ "sinkwithbalance"
534
+ ],
535
+ "title": "Sinkwithbalance",
536
+ "summary": "A Sinkwithbalance summary should stay grounded in structured rep evidence.",
537
+ "evidence_rules": [
538
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
539
+ "Treat valid detected variations as context unless issue markers show a separate problem."
540
+ ],
541
+ "coaching_points": [
542
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
543
+ ],
544
+ "allowed_interpretations": [],
545
+ "related_cards": []
546
+ },
547
+ {
548
+ "card_id": "exercise:sky",
549
+ "card_type": "exercise",
550
+ "labels": [
551
+ "sky"
552
+ ],
553
+ "title": "Sky",
554
+ "summary": "A Sky summary should stay grounded in structured rep evidence.",
555
+ "evidence_rules": [
556
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
557
+ "Treat valid detected variations as context unless issue markers show a separate problem."
558
+ ],
559
+ "coaching_points": [
560
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
561
+ ],
562
+ "allowed_interpretations": [],
563
+ "related_cards": []
564
+ },
565
+ {
566
+ "card_id": "exercise:squat",
567
+ "card_type": "exercise",
568
+ "labels": [
569
+ "squat"
570
+ ],
571
+ "title": "Squat",
572
+ "summary": "A Squat summary should stay grounded in structured rep evidence.",
573
+ "evidence_rules": [
574
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
575
+ "Treat valid detected variations as context unless issue markers show a separate problem."
576
+ ],
577
+ "coaching_points": [
578
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
579
+ ],
580
+ "allowed_interpretations": [],
581
+ "related_cards": []
582
+ },
583
+ {
584
+ "card_id": "exercise:squat",
585
+ "card_type": "exercise",
586
+ "labels": [
587
+ "squat"
588
+ ],
589
+ "title": "Squat",
590
+ "summary": "A Squat summary should stay grounded in structured rep evidence.",
591
+ "evidence_rules": [
592
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
593
+ "Treat valid detected variations as context unless issue markers show a separate problem."
594
+ ],
595
+ "coaching_points": [
596
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
597
+ ],
598
+ "allowed_interpretations": [],
599
+ "related_cards": []
600
+ },
601
+ {
602
+ "card_id": "exercise:squatandlateralshif",
603
+ "card_type": "exercise",
604
+ "labels": [
605
+ "squatandlateralshif"
606
+ ],
607
+ "title": "Squatandlateralshif",
608
+ "summary": "A Squatandlateralshif summary should stay grounded in structured rep evidence.",
609
+ "evidence_rules": [
610
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
611
+ "Treat valid detected variations as context unless issue markers show a separate problem."
612
+ ],
613
+ "coaching_points": [
614
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
615
+ ],
616
+ "allowed_interpretations": [],
617
+ "related_cards": []
618
+ },
619
+ {
620
+ "card_id": "exercise:squatandraisearms",
621
+ "card_type": "exercise",
622
+ "labels": [
623
+ "squatandraisearms"
624
+ ],
625
+ "title": "Squatandraisearms",
626
+ "summary": "A Squatandraisearms summary should stay grounded in structured rep evidence.",
627
+ "evidence_rules": [
628
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
629
+ "Treat valid detected variations as context unless issue markers show a separate problem."
630
+ ],
631
+ "coaching_points": [
632
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
633
+ ],
634
+ "allowed_interpretations": [],
635
+ "related_cards": []
636
+ },
637
+ {
638
+ "card_id": "exercise:squatandtouchthefootmp4",
639
+ "card_type": "exercise",
640
+ "labels": [
641
+ "squatandtouchthefootmp4"
642
+ ],
643
+ "title": "Squatandtouchthefootmp4",
644
+ "summary": "A Squatandtouchthefootmp4 summary should stay grounded in structured rep evidence.",
645
+ "evidence_rules": [
646
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
647
+ "Treat valid detected variations as context unless issue markers show a separate problem."
648
+ ],
649
+ "coaching_points": [
650
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
651
+ ],
652
+ "allowed_interpretations": [],
653
+ "related_cards": []
654
+ },
655
+ {
656
+ "card_id": "exercise:squatwithbackwardlunge",
657
+ "card_type": "exercise",
658
+ "labels": [
659
+ "squatwithbackwardlunge"
660
+ ],
661
+ "title": "Squatwithbackwardlunge",
662
+ "summary": "A Squatwithbackwardlunge summary should stay grounded in structured rep evidence.",
663
+ "evidence_rules": [
664
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
665
+ "Treat valid detected variations as context unless issue markers show a separate problem."
666
+ ],
667
+ "coaching_points": [
668
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
669
+ ],
670
+ "allowed_interpretations": [],
671
+ "related_cards": []
672
+ },
673
+ {
674
+ "card_id": "exercise:squatwithinsistence2x",
675
+ "card_type": "exercise",
676
+ "labels": [
677
+ "squatwithinsistence2x"
678
+ ],
679
+ "title": "Squatwithinsistence2x",
680
+ "summary": "A Squatwithinsistence2x summary should stay grounded in structured rep evidence.",
681
+ "evidence_rules": [
682
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
683
+ "Treat valid detected variations as context unless issue markers show a separate problem."
684
+ ],
685
+ "coaching_points": [
686
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
687
+ ],
688
+ "allowed_interpretations": [],
689
+ "related_cards": []
690
+ },
691
+ {
692
+ "card_id": "exercise:stationaryrunning",
693
+ "card_type": "exercise",
694
+ "labels": [
695
+ "stationaryrunning"
696
+ ],
697
+ "title": "Stationaryrunning",
698
+ "summary": "A Stationaryrunning summary should stay grounded in structured rep evidence.",
699
+ "evidence_rules": [
700
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
701
+ "Treat valid detected variations as context unless issue markers show a separate problem."
702
+ ],
703
+ "coaching_points": [
704
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
705
+ ],
706
+ "allowed_interpretations": [],
707
+ "related_cards": []
708
+ },
709
+ {
710
+ "card_id": "exercise:stiffunilateral",
711
+ "card_type": "exercise",
712
+ "labels": [
713
+ "stiffunilateral"
714
+ ],
715
+ "title": "Stiffunilateral",
716
+ "summary": "A Stiffunilateral summary should stay grounded in structured rep evidence.",
717
+ "evidence_rules": [
718
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
719
+ "Treat valid detected variations as context unless issue markers show a separate problem."
720
+ ],
721
+ "coaching_points": [
722
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
723
+ ],
724
+ "allowed_interpretations": [],
725
+ "related_cards": []
726
+ },
727
+ {
728
+ "card_id": "exercise:sumosquat",
729
+ "card_type": "exercise",
730
+ "labels": [
731
+ "sumosquat"
732
+ ],
733
+ "title": "Sumosquat",
734
+ "summary": "A Sumosquat summary should stay grounded in structured rep evidence.",
735
+ "evidence_rules": [
736
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
737
+ "Treat valid detected variations as context unless issue markers show a separate problem."
738
+ ],
739
+ "coaching_points": [
740
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
741
+ ],
742
+ "allowed_interpretations": [],
743
+ "related_cards": []
744
+ },
745
+ {
746
+ "card_id": "exercise:sumosquat",
747
+ "card_type": "exercise",
748
+ "labels": [
749
+ "sumosquat"
750
+ ],
751
+ "title": "Sumosquat",
752
+ "summary": "A Sumosquat summary should stay grounded in structured rep evidence.",
753
+ "evidence_rules": [
754
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
755
+ "Treat valid detected variations as context unless issue markers show a separate problem."
756
+ ],
757
+ "coaching_points": [
758
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
759
+ ],
760
+ "allowed_interpretations": [],
761
+ "related_cards": []
762
+ },
763
+ {
764
+ "card_id": "exercise:unilateralsquat",
765
+ "card_type": "exercise",
766
+ "labels": [
767
+ "unilateralsquat"
768
+ ],
769
+ "title": "Unilateralsquat",
770
+ "summary": "A Unilateralsquat summary should stay grounded in structured rep evidence.",
771
+ "evidence_rules": [
772
+ "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
773
+ "Treat valid detected variations as context unless issue markers show a separate problem."
774
+ ],
775
+ "coaching_points": [
776
+ "Keep the summary focused on repeatable setup and execution cues supported by the evidence."
777
+ ],
778
+ "allowed_interpretations": [],
779
+ "related_cards": []
780
+ }
781
+ ]
782
+ }
scripts/build_exercise_knowledge_base.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ from pathlib import Path
5
+ import sys
6
+
7
+
8
+ ROOT = Path(__file__).resolve().parents[1]
9
+ sys.path.insert(0, str(ROOT / "src"))
10
+
11
+ from pozify.knowledge_card_dataset_transformer import ( # noqa: E402
12
+ write_card_pack,
13
+ write_normalized_exercises,
14
+ )
15
+
16
+
17
+ DEFAULT_HF_DATASET = "DORTROX/Exercises-Data"
18
+ DEFAULT_HF_FILENAME = "dataset.json"
19
+
20
+
21
+ def _download_hf_dataset_file(repo_id: str, filename: str) -> Path:
22
+ try:
23
+ from huggingface_hub import hf_hub_download
24
+ except ImportError as exc: # pragma: no cover
25
+ raise RuntimeError("huggingface_hub is required to download Hugging Face datasets") from exc
26
+
27
+ return Path(hf_hub_download(repo_id=repo_id, repo_type="dataset", filename=filename))
28
+
29
+
30
+ def build_arg_parser() -> argparse.ArgumentParser:
31
+ parser = argparse.ArgumentParser(
32
+ description="Build a normalized exercise knowledge base and Pozify card pack from a real dataset export."
33
+ )
34
+ parser.add_argument(
35
+ "--input",
36
+ help="Local path to a JSON or JSONL exercise dataset export.",
37
+ )
38
+ parser.add_argument(
39
+ "--hf-dataset",
40
+ default=DEFAULT_HF_DATASET,
41
+ help="Hugging Face dataset repo id to download when --input is omitted.",
42
+ )
43
+ parser.add_argument(
44
+ "--hf-filename",
45
+ default=DEFAULT_HF_FILENAME,
46
+ help="Filename inside the Hugging Face dataset repo to download when --input is omitted.",
47
+ )
48
+ parser.add_argument(
49
+ "--normalized-output",
50
+ default=str(ROOT / "data/knowledge/exercises.json"),
51
+ help="Path for the normalized exercise-schema export.",
52
+ )
53
+ parser.add_argument(
54
+ "--card-pack-output",
55
+ default=str(ROOT / "data/knowledge_cards/external_exercise_dataset_pack.json"),
56
+ help="Path for the generated Pozify card-pack JSON.",
57
+ )
58
+ parser.add_argument(
59
+ "--source-dataset",
60
+ help="Optional source identifier to record in metadata. Defaults to the local file path or HF dataset id.",
61
+ )
62
+ return parser
63
+
64
+
65
+ def main(argv: list[str] | None = None) -> int:
66
+ parser = build_arg_parser()
67
+ args = parser.parse_args(argv)
68
+
69
+ if args.input:
70
+ input_path = Path(args.input).expanduser().resolve()
71
+ source_dataset = args.source_dataset or str(input_path)
72
+ else:
73
+ input_path = _download_hf_dataset_file(args.hf_dataset, args.hf_filename)
74
+ source_dataset = args.source_dataset or args.hf_dataset
75
+
76
+ normalized = write_normalized_exercises(
77
+ input_path=input_path,
78
+ output_path=Path(args.normalized_output),
79
+ source_dataset=source_dataset,
80
+ )
81
+ pack = write_card_pack(
82
+ input_path=input_path,
83
+ output_path=Path(args.card_pack_output),
84
+ source_dataset=source_dataset,
85
+ )
86
+
87
+ print(
88
+ "Built exercise knowledge base",
89
+ {
90
+ "source_dataset": source_dataset,
91
+ "input_path": str(input_path),
92
+ "normalized_output": args.normalized_output,
93
+ "card_pack_output": args.card_pack_output,
94
+ "exercise_count": normalized["exercise_count"],
95
+ "card_count": pack["card_count"],
96
+ },
97
+ )
98
+ return 0
99
+
100
+
101
+ if __name__ == "__main__":
102
+ raise SystemExit(main())
src/pozify/knowledge_card_dataset_transformer.py CHANGED
@@ -133,12 +133,12 @@ def _build_summary(
133
  return " ".join(parts)
134
 
135
 
136
- def transform_dataset_rows(
137
  rows: list[dict[str, Any]],
138
  *,
139
  source_dataset: str,
140
- ) -> dict[str, Any]:
141
- cards: list[dict[str, Any]] = []
142
  for row in rows:
143
  name_value = _first_present(row, FIELD_CANDIDATES["name"])
144
  if not isinstance(name_value, str) or not name_value.strip():
@@ -165,10 +165,45 @@ def transform_dataset_rows(
165
  description_value = _first_present(row, FIELD_CANDIDATES["description"])
166
  description = description_value.strip() if isinstance(description_value, str) else None
167
 
168
- labels = _dedupe_keep_order(
169
- [exercise_label, *(_slugify(alias) for alias in aliases if alias.strip())]
 
 
 
 
 
 
 
 
 
 
 
170
  )
171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  evidence_rules = [
173
  "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
174
  "Treat valid detected variations as context unless issue markers show a separate problem.",
@@ -219,6 +254,7 @@ def transform_dataset_rows(
219
  return {
220
  "source_dataset": source_dataset,
221
  "generated_at": datetime.now(timezone.utc).isoformat(),
 
222
  "card_count": len(cards),
223
  "cards": cards,
224
  }
@@ -271,6 +307,25 @@ def write_card_pack(
271
  return pack
272
 
273
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  def build_arg_parser() -> argparse.ArgumentParser:
275
  parser = argparse.ArgumentParser(
276
  description="Convert an exercise dataset export into a Pozify knowledge-card pack."
@@ -281,6 +336,10 @@ def build_arg_parser() -> argparse.ArgumentParser:
281
  required=True,
282
  help="Destination path for the generated Pozify card-pack JSON.",
283
  )
 
 
 
 
284
  parser.add_argument(
285
  "--source-dataset",
286
  default="unknown_dataset",
@@ -292,6 +351,12 @@ def build_arg_parser() -> argparse.ArgumentParser:
292
  def main(argv: list[str] | None = None) -> int:
293
  parser = build_arg_parser()
294
  args = parser.parse_args(argv)
 
 
 
 
 
 
295
  pack = write_card_pack(
296
  input_path=Path(args.input),
297
  output_path=Path(args.output),
 
133
  return " ".join(parts)
134
 
135
 
136
+ def normalize_dataset_rows(
137
  rows: list[dict[str, Any]],
138
  *,
139
  source_dataset: str,
140
+ ) -> list[dict[str, Any]]:
141
+ exercises: list[dict[str, Any]] = []
142
  for row in rows:
143
  name_value = _first_present(row, FIELD_CANDIDATES["name"])
144
  if not isinstance(name_value, str) or not name_value.strip():
 
165
  description_value = _first_present(row, FIELD_CANDIDATES["description"])
166
  description = description_value.strip() if isinstance(description_value, str) else None
167
 
168
+ exercises.append(
169
+ {
170
+ "source_dataset": source_dataset,
171
+ "exercise_label": exercise_label,
172
+ "title": title,
173
+ "aliases": aliases,
174
+ "category": category.strip() if isinstance(category, str) else None,
175
+ "equipment": equipment,
176
+ "primary_muscles": primary_muscles,
177
+ "secondary_muscles": secondary_muscles,
178
+ "instructions": instructions,
179
+ "description": description,
180
+ }
181
  )
182
 
183
+ exercises.sort(key=lambda item: item["exercise_label"])
184
+ return exercises
185
+
186
+
187
+ def transform_dataset_rows(
188
+ rows: list[dict[str, Any]],
189
+ *,
190
+ source_dataset: str,
191
+ ) -> dict[str, Any]:
192
+ exercises = normalize_dataset_rows(rows, source_dataset=source_dataset)
193
+ cards: list[dict[str, Any]] = []
194
+ for exercise in exercises:
195
+ exercise_label = str(exercise["exercise_label"])
196
+ title = str(exercise["title"])
197
+ aliases = [str(item) for item in exercise["aliases"]]
198
+ category = exercise["category"]
199
+ equipment = [str(item) for item in exercise["equipment"]]
200
+ primary_muscles = [str(item) for item in exercise["primary_muscles"]]
201
+ secondary_muscles = [str(item) for item in exercise["secondary_muscles"]]
202
+ instructions = [str(item) for item in exercise["instructions"]]
203
+ description = exercise["description"] if isinstance(exercise["description"], str) else None
204
+
205
+ labels = _dedupe_keep_order([exercise_label, *(_slugify(alias) for alias in aliases)])
206
+
207
  evidence_rules = [
208
  "Use structured rep analysis and issue markers instead of inferring directly from raw video.",
209
  "Treat valid detected variations as context unless issue markers show a separate problem.",
 
254
  return {
255
  "source_dataset": source_dataset,
256
  "generated_at": datetime.now(timezone.utc).isoformat(),
257
+ "normalized_exercise_count": len(exercises),
258
  "card_count": len(cards),
259
  "cards": cards,
260
  }
 
307
  return pack
308
 
309
 
310
+ def write_normalized_exercises(
311
+ *,
312
+ input_path: Path,
313
+ output_path: Path,
314
+ source_dataset: str,
315
+ ) -> dict[str, Any]:
316
+ rows = load_dataset_rows(input_path)
317
+ exercises = normalize_dataset_rows(rows, source_dataset=source_dataset)
318
+ payload = {
319
+ "source_dataset": source_dataset,
320
+ "generated_at": datetime.now(timezone.utc).isoformat(),
321
+ "exercise_count": len(exercises),
322
+ "exercises": exercises,
323
+ }
324
+ output_path.parent.mkdir(parents=True, exist_ok=True)
325
+ output_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
326
+ return payload
327
+
328
+
329
  def build_arg_parser() -> argparse.ArgumentParser:
330
  parser = argparse.ArgumentParser(
331
  description="Convert an exercise dataset export into a Pozify knowledge-card pack."
 
336
  required=True,
337
  help="Destination path for the generated Pozify card-pack JSON.",
338
  )
339
+ parser.add_argument(
340
+ "--normalized-output",
341
+ help="Optional destination path for a normalized exercise-schema JSON export.",
342
+ )
343
  parser.add_argument(
344
  "--source-dataset",
345
  default="unknown_dataset",
 
351
  def main(argv: list[str] | None = None) -> int:
352
  parser = build_arg_parser()
353
  args = parser.parse_args(argv)
354
+ if args.normalized_output:
355
+ write_normalized_exercises(
356
+ input_path=Path(args.input),
357
+ output_path=Path(args.normalized_output),
358
+ source_dataset=args.source_dataset,
359
+ )
360
  pack = write_card_pack(
361
  input_path=Path(args.input),
362
  output_path=Path(args.output),
tests/test_knowledge_card_dataset_transformer.py CHANGED
@@ -10,8 +10,10 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
10
 
11
  from pozify.knowledge_card_dataset_transformer import (
12
  load_dataset_rows,
 
13
  transform_dataset_rows,
14
  write_card_pack,
 
15
  )
16
 
17
 
@@ -66,6 +68,24 @@ class KnowledgeCardDatasetTransformerTests(unittest.TestCase):
66
  self.assertEqual(len(rows), 2)
67
  self.assertEqual(rows[0]["name"], "Squat")
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  def test_write_card_pack_writes_transform_output(self) -> None:
70
  with tempfile.TemporaryDirectory() as temp_dir:
71
  input_path = Path(temp_dir) / "dataset.json"
@@ -100,6 +120,33 @@ class KnowledgeCardDatasetTransformerTests(unittest.TestCase):
100
  ["Press the weights overhead.", "Lower with control."],
101
  )
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
  if __name__ == "__main__":
105
  unittest.main()
 
10
 
11
  from pozify.knowledge_card_dataset_transformer import (
12
  load_dataset_rows,
13
+ normalize_dataset_rows,
14
  transform_dataset_rows,
15
  write_card_pack,
16
+ write_normalized_exercises,
17
  )
18
 
19
 
 
68
  self.assertEqual(len(rows), 2)
69
  self.assertEqual(rows[0]["name"], "Squat")
70
 
71
+ def test_normalize_dataset_rows_writes_intermediate_schema(self) -> None:
72
+ exercises = normalize_dataset_rows(
73
+ [
74
+ {
75
+ "Exercise_Name": "Air Squat",
76
+ "Equipment": ["Bodyweight"],
77
+ "Target Muscle": ["Quadriceps", "Glutes"],
78
+ "Instructions": "Stand tall.\nSit down.\nStand up.",
79
+ }
80
+ ],
81
+ source_dataset="demo/normalized",
82
+ )
83
+
84
+ self.assertEqual(len(exercises), 1)
85
+ self.assertEqual(exercises[0]["exercise_label"], "squat")
86
+ self.assertEqual(exercises[0]["equipment"], ["Bodyweight"])
87
+ self.assertEqual(exercises[0]["primary_muscles"], ["Quadriceps", "Glutes"])
88
+
89
  def test_write_card_pack_writes_transform_output(self) -> None:
90
  with tempfile.TemporaryDirectory() as temp_dir:
91
  input_path = Path(temp_dir) / "dataset.json"
 
120
  ["Press the weights overhead.", "Lower with control."],
121
  )
122
 
123
+ def test_write_normalized_exercises_writes_json_schema(self) -> None:
124
+ with tempfile.TemporaryDirectory() as temp_dir:
125
+ input_path = Path(temp_dir) / "dataset.json"
126
+ output_path = Path(temp_dir) / "normalized.json"
127
+ input_path.write_text(
128
+ json.dumps(
129
+ [
130
+ {
131
+ "name": "Push Up",
132
+ "equipment": ["Bodyweight"],
133
+ "primary_muscles": ["Chest"],
134
+ }
135
+ ]
136
+ ),
137
+ encoding="utf-8",
138
+ )
139
+
140
+ payload = write_normalized_exercises(
141
+ input_path=input_path,
142
+ output_path=output_path,
143
+ source_dataset="demo/normalized-write",
144
+ )
145
+ written = json.loads(output_path.read_text(encoding="utf-8"))
146
+
147
+ self.assertEqual(payload["exercise_count"], 1)
148
+ self.assertEqual(written["exercises"][0]["exercise_label"], "push_up")
149
+
150
 
151
  if __name__ == "__main__":
152
  unittest.main()