Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesExercisesExercises — code map

Exercises — code map

API

Controllers

FileBase pathNotes
apps/api/src/exercises/exercises.controller.ts/organizations/:orgId/exercisesStandard CRUD + library + overrides
apps/api/src/exercises/exercise-search.controller.ts/exercisesGET /search — hybrid search, optional ?orgId
apps/api/src/exercises/admin-canonical.controller.ts/admin/exercises/canonicalPlatform-admin: seed, enrich, video-discover, video-refresh, video-rank, cues, jobs, video verify/demote

Services

FileResponsibility
apps/api/src/exercises/exercises.service.tsCRUD, library list, single-read
apps/api/src/exercises/exercise-overrides.service.tsUpsert/reset overrides, setMedia (org-scoped demo media; library ids go through the org fork); exports mergeExerciseWithOverride
apps/api/src/exercises/exercise-media.tsEffective movement media per org: pickExerciseMedia (the rules) + batched loadExerciseMedia
apps/api/src/common/workout-section.tsMember-facing section serializer; movementMedia (per-occurrence media first), sectionExerciseIds (input to one batched media lookup)
apps/api/src/common/is-media-url.validator.ts@IsMediaUrl() — https, ≤2048, for DTOs
apps/api/src/exercises/exercise-search.service.tsRRF lexical + trgm + semantic search
apps/api/src/exercises/canonical-seed.service.tsR2 JSON → canonical upsert + Voyage embed
apps/api/src/exercises/canonical-seed.processor.tsBullMQ processor for seed jobs
apps/api/src/exercises/canonical-enrich.service.tsAnthropic Haiku Hebrew/Russian alias enrichment
apps/api/src/exercises/canonical-enrich.processor.tsBullMQ processor for enrichment jobs
apps/api/src/exercises/canonical-utils.tsSlug / equipment / muscle normalization helpers
apps/api/src/exercises/movement-content-model.tsMovementContentModel provider interface (TKN-318) + Anthropic implementation; DI token MOVEMENT_CONTENT_MODEL
apps/api/src/exercises/video-search-provider.tsVideoSearchProvider interface (TKN-318) + plain-fetch YouTube Data API v3 implementation; DI token VIDEO_SEARCH_PROVIDER; exports parseIso8601DurationSeconds
apps/api/src/exercises/video-trusted-channels.tsVIDEO_TRUSTED_CHANNELS — editable, empty-by-default channel allowlist consumed by ranking (prompt hint) and discovery (extra query)
apps/api/src/exercises/canonical-video-discovery.service.tsYouTube search pass, appends video_candidates; exports buildDiscoveryQueries, dedupeDiscoveredIds, withinDurationRange
apps/api/src/exercises/canonical-video-discovery.processor.tsBullMQ processor for the discovery job; enqueues the ranking job on completion when thenRank: true
apps/api/src/exercises/canonical-video-ranking.service.tsVideo candidate scoring pass (scores the union of existing + discovered candidates); exports pickBestCandidate, orderByReferenced, excludeManualRows, VIDEO_SCORE_THRESHOLD
apps/api/src/exercises/canonical-video-ranking.processor.tsBullMQ processor for the video ranking job
apps/api/src/exercises/canonical-cues.service.tsCues/faults/scaling generation pass; exports needsCuesRebuild
apps/api/src/exercises/canonical-cues.processor.tsBullMQ processor for the cues job
apps/api/src/exercises/canonical-video-moderation.service.tsverify/demote (admin) + report (org member; one report per user per url, library auto-demote needs reporters from 2+ orgs)
apps/api/src/exercises/canonical-duplicates.service.tsDuplicate-candidate detection (embedding + same pattern), alias-collision report (lookup normalization via normalizeExerciseIdentity), variant-qualifier exclusion, suggested-winner ranking; exports findAliasCollisions, isVariantPair, VARIANT_QUALIFIERS, findDuplicateCandidatePairs, suggestWinnerId, DUPLICATE_EMBEDDING_THRESHOLD
apps/api/src/exercises/canonical-duplicates.processor.tsBullMQ processor for the duplicates job
apps/api/src/exercises/canonical-merge.service.tsMerges an approved duplicate pair — repoints referencing tables, moves/drops overrides, soft-deletes the loser; exports mergeAliasLists, pickWinnerVideoUrl, partitionPersonalRecordConflicts, partitionOverrideConflicts

DTOs (apps/api/src/exercises/dto/)

  • create-exercise.dto.ts, update-exercise.dto.ts, upsert-exercise.dto.ts
  • canonical-seed.dto.ts, canonical-enrich.dto.ts
  • canonical-video-ranking.dto.ts, canonical-cues.dto.ts, canonical-video-discovery.dto.ts
  • canonical-duplicates.dto.ts, canonical-merge.dto.ts

Routes

RouteMethod
GET /organizations/:orgId/exercisesfindAll (legacy org-only list)
GET /organizations/:orgId/exercises/libraryfindAllForLibrary (merged + paginated)
GET /organizations/:orgId/exercises/library/:idfindByIdForLibrary
GET /organizations/:orgId/exercises/:idfindById (accepts canonical)
POST /organizations/:orgId/exercisescreate org-custom
PATCH /organizations/:orgId/exercises/:idupdate org-custom — returns the merged EffectiveExercise shape, same as the library endpoints
DELETE /organizations/:orgId/exercises/:idremove org-custom (soft)
POST /organizations/:orgId/exercises/upsertupsert by case-insensitive name
PUT /organizations/:orgId/exercises/:id/overrideupsertOverride
DELETE /organizations/:orgId/exercises/:id/overridedeleteOverride (reset)
POST /organizations/:orgId/exercises/:id/video/reportreportVideo — any org member
GET /exercises/searchHybrid search; ?orgId optional
POST /admin/exercises/canonical/seedPlatform admin
POST /admin/exercises/canonical/enrichPlatform admin
POST /admin/exercises/canonical/video/discoverPlatform admin
POST /admin/exercises/canonical/video/refreshPlatform admin — chains discover → rank
POST /admin/exercises/canonical/video/rankPlatform admin
POST /admin/exercises/canonical/cuesPlatform admin
POST /admin/exercises/canonical/:id/video/verifyPlatform admin
POST /admin/exercises/canonical/:id/video/demotePlatform admin
GET /admin/exercises/canonical/jobsPlatform admin
GET /admin/exercises/canonical/jobs/:queue/:idPlatform admin
POST /admin/exercises/canonical/duplicatesPlatform admin — always dry run
POST /admin/exercises/canonical/mergePlatform admin — synchronous, { pairs, dryRun }

Web

Movements is its own destination (/dashboard/movements, sidebar item next to Workouts) rather than a second tab on the Workouts page — ?tab=library on /dashboard/workouts redirects there for old links. exercise-autocomplete.tsx (hybrid search against /exercises/search) is the only picker: the builder, PR entry, goal creation, and the paste-flow resolution popover all use it. exercise-library-browser.tsx (legacy org-only list) and exercise-video-dialog.tsx (its preview dialog) were deleted once nothing imported them.

Components

  • apps/web/src/app/[lang]/(protected)/dashboard/movements/page.tsx — the route
  • apps/web/src/components/overview/movements/movements-library-view.tsx — search, “My gym’s” chip, Category/Equipment filters
  • apps/web/src/components/overview/movements/movements-data-table.tsx — thumbnail/name/equipment/video-status columns
  • apps/web/src/components/overview/movements/movement-detail-sheet.tsx — single-screen sheet (no tabs): video top, Replace/Report video actions, Cues/Scale it/Common faults, inline “Customize”, collapsed “Advanced”, canonical-only “Reset to Taikan default”
  • apps/web/src/components/overview/movements/movement-video-status.tsx — derives verified/unverified/no-video from videoStatus/videoUrl
  • apps/web/src/components/overview/movements/movement-source-badge.tsx — sourceFor/isGymRow + the small “Your gym” row badge
  • apps/web/src/components/overview/workouts/exercise-autocomplete.tsx — the one picker (hybrid search)
  • apps/web/src/components/overview/workouts/paste-workout/movement-resolution-popover.tsx — paste-flow resolution; its “search library” action opens exercise-autocomplete.tsx

Admin

  • apps/admin/src/pages/canonical/show.tsx — seed/enrich/video/cues/thumbnails job cards + jobs table; the “Find duplicates” card (dry run) whose pairs open the merge review.
  • apps/admin/src/pages/canonical/merge-review-modal.tsx — dry-runs every pair against POST /admin/exercises/canonical/merge, lets the admin choose the kept movement, merges only planned pairs.
  • apps/admin/src/pages/canonical/alias-collisions-panel.tsx — GET alias-collisions table; per-term resolve (keep on one movement / remove from all) with dry run, via POST alias-change.

E2E

  • apps/web/e2e/specs/coach-movements-library.spec.ts — movements browse + customize flow.

DB

libs/db/src/lib/schema/workouts.ts (same file as workouts):

  • exercises — both canonical (org_id null) and org rows.
  • exercise_org_overrides — JSONB diff per (org, exercise).
  • exercise_video_reports — one row per (exercise, user, video url) member report; unique exercise_video_reports_reporter_unique.
  • workout_movements.media_url — per-occurrence demo media.
  • exercise_comments — comments on a workout_movement (lives in comments.ts).

Indexes (exercises):

IndexColumns / clause
exercises_slug_unique_idxUNIQUE on slug WHERE slug IS NOT NULL AND organization_id IS NULL
exercises_search_tsv_idxGIN on search_tsv
exercises_name_trgm_idxGIN on name gin_trgm_ops (pg_trgm, migration 0039)
exercises_embedding_idxHNSW on embedding (vector_cosine_ops)
exercises_movement_pattern_idxpartial on movement_pattern WHERE not null
exercises_org_id_idxon organization_id
exercises_forked_from_id_idxpartial WHERE not null

Indexes (exercise_org_overrides):

  • exercise_org_overrides_org_exercise_unique UNIQUE on (organization_id, exercise_id)
  • exercise_org_overrides_org_idx on organization_id

Every table with an exercise_id FK (the set canonical-merge.service.ts repoints on a merge): workout_movements.exercise_id, workout_set_results.exercise_id, personal_records.exercise_id (nullable — exactly one of exercise_id/library_workout_id set), goals.exercise_id (nullable — exactly one of exercise_id/metric_type set), exercise_org_overrides.exercise_id (moved, not repointed — see behavior.md). personal_records additionally carries a partial UNIQUE on (user_id, exercise_id) WHERE deleted_at IS NULL, which is what forces the retire-instead-of-repoint conflict rule on merge.

Shared

  • libs/shared/src/lib/schemas/exercise.schema.ts — Zod schemas for read responses, category/kind enum values.
  • libs/shared/src/lib/media-kind.ts — MediaKind, MediaSource, EffectiveMedia, mediaKindOf, isValidMediaUrl, MEDIA_URL_MAX_LENGTH, toEffectiveMedia (mobile mirrors it at src/lib/media-kind.ts).
  • libs/shared/src/lib/schemas/workout.schema.ts — mediaUrlSchema, effectiveMediaSchema; mediaUrl/media on movement schemas.
  • libs/shared/src/lib/agent-schemas/exercises.ts — exercisesSetMediaInputSchema, mediaUrl on exercisesCreateCustomInputSchema.
  • libs/shared/src/lib/exercise-overrides.ts — overridableExerciseFieldKeys (the allowlist), OverridableExerciseFields type, EffectiveExercise type, MovementSourceFilter enum. TODO: verify exact filename.
  • libs/shared/src/lib/agent-schemas/read.ts — agent-facing exercise search query schema (exerciseSearchQuerySchema).

Tests

FileType
apps/api/src/exercises/exercise-overrides.service.unit.spec.tsUnit — merge logic, override upsert
apps/api/src/exercises/exercise-search.int.spec.tsIntegration — RRF, lexical/semantic/hybrid modes
apps/api/src/exercises/canonical-video-ranking.service.unit.spec.tsUnit — threshold selection, referenced-first ordering, manual-row exclusion
apps/api/src/exercises/canonical-cues.service.unit.spec.tsUnit — rebuild gating
apps/api/src/exercises/canonical-video-moderation.service.unit.spec.tsUnit — verify/demote fall-through, report dedupe + auto-demote rules
apps/api/src/exercises/exercise-media.unit.spec.tsUnit — effective media rules
libs/shared/src/lib/media-kind.unit.spec.tsUnit — mediaKindOf, isValidMediaUrl
apps/api/src/exercises/movement-media.int.spec.ts (+ .int.driver.ts)Integration — org override isolation, clear → live library fallback, per-occurrence media through set_sections + release, setMedia fork reuse / not-found, report dedupe and the 2-org rule
apps/api/src/exercises/canonical-video-discovery.service.unit.spec.tsUnit — dedup vs existing/rejected ids, duration filter, quota stop + resumable report, no-op when key missing, dry run
apps/api/src/exercises/canonical-video-discovery.processor.unit.spec.tsUnit — refresh chains discover → rank
apps/api/src/exercises/canonical-duplicates.service.unit.spec.tsUnit — normalization, variant-qualifier exclusion, alias/embedding pairing, suggested-winner rule, service wiring
apps/api/src/exercises/canonical-merge.service.unit.spec.tsUnit — alias/video merge rules, PR/override conflict partitioning, repoints per referencing table, idempotency, dry run
apps/api/src/exercises/canonical-merge.service.driver.tsDriver backing the merge spec — models the Drizzle transaction chain
apps/web/e2e/specs/coach-movements-library.spec.tsE2E — library browse + override