@wattsonclub/wattson-cli 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,460 @@
1
+ # Workout text format
2
+
3
+ This doc proposes a **chat-friendly, mostly lossless text representation** for workouts.
4
+
5
+ Goal:
6
+ - make workouts easy to review in chat
7
+ - give agents a stable text representation for reasoning and diffs
8
+ - make structured validation feedback easier to map back to something human-readable
9
+ - prepare for future renderer/parser work without pretending it already exists
10
+
11
+ Important status note:
12
+ - a first renderer now exists in `packages/domain` via `renderWorkoutText(...)`
13
+ - this is still **not** a fully implemented parser contract yet
14
+ - canonical source of truth is still the structured workout schema in `packages/domain`
15
+
16
+ Related docs:
17
+ - `workout-building-blocks.md`
18
+ - `commands.md`
19
+
20
+ ---
21
+
22
+ ## 1. Why this exists
23
+
24
+ Schema is necessary, but schema alone is not enough for chat interfaces.
25
+
26
+ We need a middle representation that is:
27
+ - readable by humans
28
+ - easy for agents to reason about
29
+ - compact enough for chat
30
+ - stable enough to reference in validation/revision feedback
31
+ - structured enough that it could later become a real render/parse layer
32
+
33
+ This doc proposes **three textual views** of the same workout:
34
+
35
+ 1. **Expanded canonical text**
36
+ - most explicit
37
+ - preferred for careful review
38
+ - closest to a future reversible representation
39
+
40
+ 2. **Compact text summary**
41
+ - shorter
42
+ - good for chat replies
43
+ - still structurally legible
44
+
45
+ 3. **Chat-visual preview**
46
+ - highly readable preview
47
+ - best for quick inspection
48
+ - not necessarily fully lossless on its own
49
+
50
+ ---
51
+
52
+ ## 2. Design principles
53
+
54
+ A good workout text format should be:
55
+
56
+ ### Deterministic
57
+ The same structured workout should render the same way every time.
58
+
59
+ ### Block-oriented
60
+ Blocks and interval steps should stay visible as blocks/steps.
61
+ Do not flatten everything into prose.
62
+
63
+ ### Stable enough for references
64
+ Validation should be able to say things like:
65
+ - `block 2`
66
+ - `block 2 > step 1`
67
+ - `line 6`
68
+
69
+ ### Honest about precision
70
+ If a target is explicit, show it explicitly.
71
+ If it is open, show that too.
72
+
73
+ ### Friendly to diffs
74
+ If a workout changes, the text representation should make the change obvious.
75
+
76
+ ---
77
+
78
+ ## 3. Expanded canonical text format
79
+
80
+ This is the best candidate for a future canonical text representation.
81
+
82
+ ## 3.1 Header fields
83
+ Suggested header fields:
84
+ - `Workout:`
85
+ - `Objective:`
86
+ - `Sport:`
87
+ - `Execution:`
88
+ - optionally `Constraints:`
89
+ - optionally `Rationale:`
90
+
91
+ Example:
92
+
93
+ ```text
94
+ Workout: VO2 20
95
+ Objective: vo2max
96
+ Sport: cycling_indoor
97
+ Execution: erg
98
+ Constraints: indoorOnly, maxPctFtp=1.2
99
+ Rationale: short hard day with 3 repeat efforts
100
+ ```
101
+
102
+ Note:
103
+ - `maxPctFtp` is fractional
104
+ - use `1.2` for `120% FTP`, not `120`
105
+
106
+ ## 3.2 Block lines
107
+ Each top-level block gets an ordinal prefix.
108
+
109
+ ### Simple block form
110
+ ```text
111
+ 1. warmup 5:00 @ 55-70% FTP
112
+ ```
113
+
114
+ Or, when more explicit:
115
+ ```text
116
+ 1. warmup 5:00 target=ramp_pct_ftp(0.55->0.70)
117
+ ```
118
+
119
+ ### Interval-set form
120
+ ```text
121
+ 2. interval_set x3
122
+ 2.1 work 2:00 @ 115% FTP
123
+ 2.2 recovery 2:00 @ 50% FTP
124
+ ```
125
+
126
+ ### Note block form
127
+ ```text
128
+ 3. note "Stay relaxed through the shoulders"
129
+ ```
130
+
131
+ ## 3.3 Optional inline annotations
132
+ Add only when present:
133
+ - cadence
134
+ - HR constraints
135
+ - instructions
136
+
137
+ Example:
138
+ ```text
139
+ 1. steady 8:00 @ 88% FTP cadence=90-95 note="Stay seated"
140
+ ```
141
+
142
+ Or:
143
+ ```text
144
+ 1. steady 8:00 @ 88% FTP hr<=165 instructions=["Relax shoulders", "No sprint starts"]
145
+ ```
146
+
147
+ ## 3.4 Canonical-ish example
148
+
149
+ ```text
150
+ Workout: VO2 20
151
+ Objective: vo2max
152
+ Sport: cycling_indoor
153
+ Execution: erg
154
+ Constraints: indoorOnly, maxPctFtp=1.2
155
+
156
+ 1. warmup 5:00 target=ramp_pct_ftp(0.55->0.70)
157
+ 2. interval_set x3
158
+ 2.1 work 2:00 target=pct_ftp(1.15)
159
+ 2.2 recovery 2:00 target=pct_ftp(0.50)
160
+ 3. cooldown 3:00 target=ramp_pct_ftp(0.55->0.45)
161
+ ```
162
+
163
+ This format is not implemented yet, but it is the right target shape.
164
+
165
+ ---
166
+
167
+ ## 4. Compact text summary
168
+
169
+ This is the shorter chat-friendly view.
170
+
171
+ Example:
172
+
173
+ ```text
174
+ WU 5m @55->70%
175
+ 3x [2m @115% / 2m @50%]
176
+ CD 3m @55->45%
177
+ ```
178
+
179
+ Or, with title/objective:
180
+
181
+ ```text
182
+ VO2 20 — vo2max
183
+ WU 5m @55->70%
184
+ 3x [2m @115% / 2m @50%]
185
+ CD 3m @55->45%
186
+ ```
187
+
188
+ This is great for:
189
+ - quick chat confirmation
190
+ - draft previews
191
+ - revision summaries
192
+
193
+ It is weaker for:
194
+ - exact validation references
195
+ - complex cadence/HR/instruction details
196
+ - future lossless round-tripping
197
+
198
+ So this should usually be considered a **preview**, not the only representation.
199
+
200
+ ---
201
+
202
+ ## 5. Chat-visual preview
203
+
204
+ This is the “looks good in a message thread” version.
205
+
206
+ ## 5.1 Linear block preview
207
+
208
+ ```text
209
+ VO2 20
210
+ [WU 5m] [WORK 2m] [REC 2m] [WORK 2m] [REC 2m] [WORK 2m] [REC 2m] [CD 3m]
211
+ 55-70% 115% 50% 115% 50% 115% 50% 55-45%
212
+ ```
213
+
214
+ ## 5.2 Table-like list preview
215
+
216
+ ```text
217
+ VO2 20
218
+ 1. Warmup 5:00 55-70%
219
+ 2. Work 2:00 115%
220
+ 3. Recovery 2:00 50%
221
+ 4. Work 2:00 115%
222
+ 5. Recovery 2:00 50%
223
+ 6. Work 2:00 115%
224
+ 7. Recovery 2:00 50%
225
+ 8. Cooldown 3:00 55-45%
226
+ ```
227
+
228
+ This is the best format when the user mainly wants to inspect whether the shape “looks right.”
229
+
230
+ ---
231
+
232
+ ## 6. Mapping from structure -> text
233
+
234
+ ## 6.1 Simple block mapping
235
+
236
+ ### Example structured block
237
+ ```json
238
+ {
239
+ "id": "block-1",
240
+ "type": "steady",
241
+ "durationSec": 600,
242
+ "target": { "mode": "pct_ftp", "value": 0.88 },
243
+ "cadence": { "min": 90, "max": 95 },
244
+ "instructions": ["Stay seated"]
245
+ }
246
+ ```
247
+
248
+ ### Expanded canonical render
249
+ ```text
250
+ 1. steady 10:00 target=pct_ftp(0.88) cadence=90-95 instructions=["Stay seated"]
251
+ ```
252
+
253
+ ### Compact render
254
+ ```text
255
+ Steady 10m @88% cadence 90-95
256
+ ```
257
+
258
+ ## 6.2 Interval-set mapping
259
+
260
+ ### Example structured block
261
+ ```json
262
+ {
263
+ "id": "block-2",
264
+ "type": "interval_set",
265
+ "repeat": 3,
266
+ "steps": [
267
+ {
268
+ "id": "step-1",
269
+ "type": "work",
270
+ "durationSec": 120,
271
+ "target": { "mode": "pct_ftp", "value": 1.15 }
272
+ },
273
+ {
274
+ "id": "step-2",
275
+ "type": "recovery",
276
+ "durationSec": 120,
277
+ "target": { "mode": "pct_ftp", "value": 0.50 }
278
+ }
279
+ ]
280
+ }
281
+ ```
282
+
283
+ ### Expanded canonical render
284
+ ```text
285
+ 2. interval_set x3
286
+ 2.1 work 2:00 target=pct_ftp(1.15)
287
+ 2.2 recovery 2:00 target=pct_ftp(0.50)
288
+ ```
289
+
290
+ ### Compact render
291
+ ```text
292
+ 3x [2m @115% / 2m @50%]
293
+ ```
294
+
295
+ ---
296
+
297
+ ## 7. Human-friendly target rendering rules
298
+
299
+ To make chat output readable, we should prefer these human renderings:
300
+
301
+ - `pct_ftp(0.55)` -> `55% FTP`
302
+ - `ramp_pct_ftp(0.55->0.70)` -> `55-70% FTP`
303
+ - `watts(300)` -> `300W`
304
+ - `ramp_watts(180->260)` -> `180->260W`
305
+ - `zone(z2)` -> `Z2`
306
+ - `open` -> `open`
307
+ - `resistance_level(35)` -> `resistance 35`
308
+ - `grade_percent(4)` -> `grade 4%`
309
+
310
+ Structured payload rule:
311
+ - `%FTP` values are fractional
312
+ - use `pct_ftp(0.55)` for `55% FTP`
313
+ - use `pct_ftp(1.15)` for `115% FTP`
314
+ - never send `pct_ftp(55)` or `pct_ftp(115)`
315
+
316
+ This matters because:
317
+ - humans do not want to read raw JSON
318
+ - agents still need a stable rendering convention
319
+
320
+ ---
321
+
322
+ ## 8. Validation feedback and text references
323
+
324
+ Current validation issues already include:
325
+ - `code`
326
+ - `message`
327
+ - `path[]`
328
+ - `severity`
329
+
330
+ That is a good foundation.
331
+
332
+ The text format should help translate issue paths into human-facing references like:
333
+ - `block 2`
334
+ - `block 2 > step 1`
335
+ - `line 6`
336
+
337
+ ### Good future validation examples
338
+
339
+ ```text
340
+ error: block 2 > step 1 exceeds maxPctFtp constraint
341
+ warning: block 1 cadence cue is underspecified for cadence_drill
342
+ error: interval_set x3 causes total duration to exceed requested duration
343
+ warning: cooldown is unusually short for threshold objective
344
+ ```
345
+
346
+ That would make the validate/revise loop much better in chat.
347
+
348
+ ---
349
+
350
+ ## 9. Recommended representation strategy
351
+
352
+ Use all three, with different purposes:
353
+
354
+ ### Structured schema
355
+ Use for:
356
+ - storage
357
+ - validation
358
+ - revision
359
+ - runtime compilation
360
+
361
+ ### Expanded canonical text
362
+ Use for:
363
+ - careful review
364
+ - agent reasoning
365
+ - diffing
366
+ - future parser/renderer contract
367
+
368
+ ### Compact/chat-visual render
369
+ Use for:
370
+ - chat previews
371
+ - quick approval loops
372
+ - human readability
373
+
374
+ ---
375
+
376
+ ## 10. Practical examples for chat interfaces
377
+
378
+ ## 10.1 User says: "Create me a 20 min session with 3 hard intervals"
379
+
380
+ Agent could reply with:
381
+
382
+ ```text
383
+ Draft preview — VO2 20
384
+ WU 5m @55->70%
385
+ 3x [2m @115% / 2m @50%]
386
+ CD 3m @55->45%
387
+ ```
388
+
389
+ And optionally also keep the canonical-ish version for internal validation/revision.
390
+
391
+ ## 10.2 User says: "Make it slightly easier"
392
+
393
+ Agent could show a diff-like preview:
394
+
395
+ ```text
396
+ Before:
397
+ 3x [2m @115% / 2m @50%]
398
+
399
+ After:
400
+ 3x [2m @110% / 2m @50%]
401
+ ```
402
+
403
+ That makes revision feel concrete.
404
+
405
+ ## 10.3 User says: "Too long — shorten it"
406
+
407
+ Agent could show:
408
+
409
+ ```text
410
+ Before:
411
+ WU 5m
412
+ 3x [2m @115% / 2m @50%]
413
+ CD 3m
414
+
415
+ After:
416
+ WU 4m
417
+ 3x [2m @112% / 90s @50%]
418
+ CD 2m
419
+ ```
420
+
421
+ That is much easier to approve in chat than raw JSON.
422
+
423
+ ---
424
+
425
+ ## 11. Recommended next implementation follow-on
426
+
427
+ The next practical code step after this doc should probably be:
428
+
429
+ 1. add a renderer helper that turns structured workouts into:
430
+ - expanded canonical text
431
+ - compact text summary
432
+ - chat-visual preview
433
+ 2. use that renderer in CLI/docs/tests
434
+ 3. later decide whether we want a reverse parser for a canonical text form
435
+
436
+ Important sequencing point:
437
+ - renderer first
438
+ - parser later, only if it proves valuable
439
+
440
+ ---
441
+
442
+ ## 12. Current implementation status
443
+
444
+ Currently implemented:
445
+ - `renderWorkoutText(workout, { mode: "compact" | "canonical" | "chat" })` in `packages/domain`
446
+ - compact preview is now used in planner CLI workout summaries where a full structured workout is available
447
+
448
+ Not implemented yet:
449
+ - reverse parser from text -> workout structure
450
+ - line-stable validation references in CLI output
451
+ - richer diff renderers for workout revisions
452
+
453
+ ## 13. Bottom line
454
+
455
+ The workout schema should stay canonical.
456
+ But for chat interfaces, agents need a stable text form that sits between:
457
+ - raw JSON
458
+ - vague prose
459
+
460
+ That is the real leverage point.