docs Document workout import and promotion

Document canonical observations, the provisional import lifecycle, strict Hevy adapter, whole-workout and partial-batch promotion, immutable source and provenance, persistence independence, merged Progression insights, recovery gating, routes, and layered test coverage.

Commit
40b79891038e80f7859c405cabb6f69916087a3f
Author
Marius Peter <dev@marius-peter.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
ARCHITECTURE.org
index 069df689..2cd885d3 100644..100644
@@ -227,6 +227,12 @@
227 227 oldest first, and =readiness= computed from the last finished workout. Evidence
228 228 is dated because a stall is *defined* by two weeks of nothing.
229 229
230 Added: =Observation= is the canonical insight input: a catalogued exercise, a confirmed
231 Added: effort, and a performance time. Native workouts produce observations through
232 Added: =Log=. A cleaned import can produce the same type without pretending that it had
233 Added: a prescription or clearance. =Progression= judges observations and therefore
234 Added: does not know their provenance.
235 Added:
230 236 =Feedback= is a typed vocabulary for reported signals — sleep, appetite,
231 237 readiness, motivation, difficulty, pain, injury, insufficient preparation — with
232 238 duplicate categories rejected. Leveled signals carry a five-point score, 1 (very
@@ -333,6 +339,37 @@
333 339 destination. =Handlers.error_handler=, wired into =Dream.run=, covers the rest:
334 340 =5xx= responses and uncaught exceptions.
335 341
342 Added: ** Workout import
343 Added:
344 Added: Native Hevy CSV import is an application use case, not a core entity. The
345 Added: =Hevy_csv= web adapter parses the strict 14-column native format and retains the
346 Added: complete source text. The upload supplies an explicit UTC offset because Hevy
347 Added: exports local wall time without a timezone.
348 Added:
349 Added: A =Workout_import.batch= owns provisional workouts, ordered warnings, confirmed
350 Added: exercise mappings, workout-level acknowledgements, revisions, and prior
351 Added: promotion state. Warm-ups and unsupported rows create warnings but no
352 Added: provisional sets. A drop set blocks its complete workout. A normal set requires
353 Added: one explicit acknowledgement for the workout. Every exercise mapping requires
354 Added: user confirmation.
355 Added:
356 Added: Promotion is atomic for one workout and partial for a batch. Eligible workouts
357 Added: promote while blocked workouts remain provisional for later review. Each
358 Added: promoted set becomes one confirmed =Evidence.Observation=. The promoted record
359 Added: retains batch identity, workout identity, source rows, acknowledgement, and
360 Added: revision. Re-promotion after an edit replaces the prior observations instead of
361 Added: adding duplicates.
362 Added:
363 Added: Provisional batches and promoted records have independent lifecycles. Deleting
364 Added: a batch never deletes promoted evidence. The original CSV remains immutable so
365 Added: a later importer can reprocess rows that the current version dropped.
366 Added:
367 Added: Promoted observations feed =Progression= with native observations. Provenance
368 Added: does not change judgment. The latest promoted workout also gates recovery with
369 Added: the routine's 48-hour training interval when it is newer than native evidence.
370 Added: Imports never advance routine rotation, and diagnostics remain native-only
371 Added: because imports do not carry prescription volume or clearance structure.
372 Added:
336 373 * The cycle
337 374
338 375 #+begin_src mermaid
@@ -399,6 +436,12 @@
399 436 | =/profile= | GET | Account profile: rename and change password |
400 437 | =/profile/username= | POST | Change the username |
401 438 | =/profile/password= | POST | Change the password (verifies the current one) |
439 Added: | =/profile/import= | POST | Upload a native Hevy CSV export |
440 Added: | =/profile/import/:id= | GET | Review one provisional import batch |
441 Added: | =/profile/import/:id/workouts/:workout/map= | POST | Confirm one exercise mapping |
442 Added: | =/profile/import/:id/workouts/:workout/confirm= | POST | Confirm all normal sets in a workout |
443 Added: | =/profile/import/:id/workouts/:workout/promote= | POST | Promote one eligible workout |
444 Added: | =/profile/import/:id/promote= | POST | Promote every eligible workout in a batch |
402 445 | =/assets/hito.css= | GET | Stylesheet (embedded at build time) |
403 446 | =/assets/workout-client.js= | GET | Enhancement client (embedded at build time) |
404 447
@@ -511,6 +554,15 @@
511 554 - =test_bcrypt_hash= — the hashing adapter satisfies the =Password_hash.S=
512 555 port: a hash verifies against itself, a wrong password fails, and a malformed
513 556 stored hash is rejected rather than raised.
557 Added: - =test_workout_import= — provisional cleaning, blockers, whole-workout
558 Added: promotion, partial batches, provenance, revision replacement, and idempotence.
559 Added: - =test_hevy_csv= — the native schema, RFC4180 fields, explicit timezone offset,
560 Added: grouping, warm-up and unsupported-row warnings, and immutable source retention.
561 Added: - =test_import_codec=, =test_import_repo=, and =test_import_sqlite= — safe
562 Added: round-trip storage, reconnects, trainee isolation, replacement, and independent
563 Added: batch and promoted-record lifecycles.
564 Added: - =test_import_service= — duplicate override, cleaning persistence, partial
565 Added: promotion, merged insights, recovery gating, and native-only routine rotation.
514 566 - =test_codec= — a stored workout round-trips faithfully, and
515 567 malformed or incomplete text is *reported*, not raised.
516 568 - =test_sqlite_repo= — state survives a reconnect, trainees stay