Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesWorkout ParseWorkout Parse — QA Plan

Workout Parse — QA Plan

Manual test script for the paste-to-structure flow. Automated coverage exists (apps/web/e2e/specs/paste-workout.spec.ts + unit/integration specs); this plan is the human pass over the pieces automation can’t judge — real LLM output quality, copy, RTL, and the cross-org/tier gates.

Setup

  • Org on tier Pro (has workout_builder), owner role.
  • Canonical exercise library seeded (pnpm seed:movements).
  • ANTHROPIC_API_KEY set (scenarios 4+ make one real Sonnet call each, ~5–15s).
  • A second org on Lite tier for scenario 3.

Sample paste (used in several scenarios):

Strength Back Squat 5x5 @ 80% Metcon — AMRAP 12 10 Thrusters 42.5/30 15 Pull-ups 200m Run

Scenarios

  1. Freeform first. /dashboard/workouts/new opens freeform directly.
  2. Translation is available. Clicking “Convert to structured workout” replaces the freeform form with the parse input on the same route.
  3. Tier gate. On a Lite org, ?mode=translate shows the upgrade card, never the textarea. Direct API POST /parse → 403 (tier).
  4. Golden path — paste, preview, commit. Paste the sample text and submit. Expect the original source with movement highlights alongside two structured sections. Commit → success toast → structured workout opens in the normal builder. Its description contains only explicit coach comments, not the pasted source; the parse job retains the complete source. For the Service Cup fixture, verify two sections/nine movements, Part A cap 15 minutes and shuttle definition, female 95/male 135 lb on its four lifts only, and Part B start 900/duration 180 seconds.
  5. Non-English paste. Paste a Hebrew workout (e.g. אימון כוח: סקוואט 5x5). Expect a normal preview — no language error, source retained literally; movements the catalog can’t match show unresolved chips. Then paste English programming under a Hebrew coaching preamble: the preamble becomes the coach note and the sections carry the English programming. The job row’s language holds the detected tag.
  6. Multi-workout warning (D4). Paste a week of workouts separated by day headers (Day 1 …\nDay 2 …). Expect the warning banner (parse-multi-warning) and a preview containing only the first day’s content; later days appear nowhere in the tree.
  7. Adjustment — pick a candidate. Paste text with an ambiguous movement (e.g. “DB press”). An amber suggested chip appears; clicking opens the popover showing the mention, the raw line, and up to 5 candidates with qualitative confidence labels (no raw numbers). Pick one → chip clears, outstanding counter decrements.
  8. Adjustment — create org-local exercise. Include a made-up movement (e.g. “Bosu burpee slam”). Red unresolved chip → popover → “Create new exercise” (name prefilled) → create. Expect success toast, movement resolves to the new exercise. Then verify in a second parse of the same text that the new exercise now auto/suggest-matches (embedding enrichment on create, D10 — allow the queue a minute).
  9. Adjustment — drop to note. On another unresolved movement choose “Drop to note”. The movement row leaves the tree and its raw line appears in the section description.
  10. Commit gating. While any suggested/unresolved movement is unactioned: commit button disabled, “N movements need attention” counter visible, tooltip on hover explains why. After actioning all → enabled.
  11. Discard. Click discard → confirmation dialog → confirm. Toast, back to the root creator’s freeform form. Re-open the job via its ?jobId= URL: preview renders but no commit/discard bar (terminal status).
  12. Refresh / deep link. After a parse, reload the ?jobId= URL: the same draft renders (GET endpoint), including chips and remainder. Open the same URL as a coach of a different org → 404/not-found behavior, never another org’s draft.
  13. “Convert to structured workout” flow (entry b). Create a freeform workout with body text, save, reopen it, and press “Convert to structured workout”. Parse + commit. Expect the same workout id in structured mode with sections and the old freeform body cleared. An explicitly supplied coach description must survive; the parse job retains the original body.
  14. Too-long input (D13). Paste > 10,000 chars. The char counter turns destructive and submit is disabled client-side; forcing the API call returns 422 parse_too_long.
  15. Budget breach. With the org at/over its AI budget (or tier caps lowered in a dev DB): submit → inline 429 error with the localized budget message including the period. No ai_parse_jobs row is created (blocked before the LLM call).
  16. Member role. Signed in as a member: no entry points; direct API calls to all four parse routes → 403.
  17. RTL pass (HE locale). Switch the dashboard to Hebrew and rerun scenarios 4–11 visually: page layout mirrors correctly (logical properties); the paste textarea honors dir="auto" (English paste stays LTR inside an RTL page); chips, popover, commit bar, dialogs, and toasts are correctly aligned and fully translated (workouts.parse.* — no English fallbacks); prev/next-style arrows are NOT double-flipped.
  18. Observability. In PostHog (production/staging with a real key): a parse run emits parse.requested → parse.completed; a commit emits parse.committed with zeroEdit/editDistance; blocked attempts emit parse.blocked with the right reason. No event property contains pasted text, mention text, or titles — counts/enums only. Events carry $groups.organization.

Negative / robustness

  • Submit with an empty textarea → button disabled.
  • Kill the API mid-parse → generic parse_failed inline error; the job row (dev DB) ends failed with error_code.
  • Double-click commit → single workout created; second attempt (or a stale tab) surfaces the generic failure toast (409 server-side).
  • e2e suite: make test-e2e-web (e2e must target the taikan_test DB on 55432).

Performance expectations

  • Parse round-trip (paste → preview) under ~20s for a typical WOD; stage_timings on the job row breaks down extraction vs resolution.
  • Preview interactions (chip popovers, drag, prescription edits) are the builder’s own — no added latency budget.