Metric Sets
Status: Shipped. Last reviewed: 2026-05-28
What
A reusable taxonomy + per-member ledger of performance metrics — 1RM lifts, pace, body fat %, etc. — distinct from the body composition timeline in ../body-metrics/. Three primitives:
metric_definitions— global catalogue of measurable quantities.slug(machine),nameI18n(locale-keyed display),unit,kind.metric_sets— named collections of definitions owned by exactly one of: a member, a workout template, or an organization (CHECK enforces 1-of-3).member_metrics— recorded values: member × definition → numeric value, unit, recorded_at, source.
Why
- Programming uses
% 1RMpercentages. Resolving “75% of back squat 1RM” requires knowing the member’s current 1RM —member_metricsis the lookup table. - Coaches assemble sets of “what I care about for this athlete” (member-owned) or “what every athlete on this template needs” (workout-owned) or “the defaults for my gym” (org-owned).
- The old polymorphic
(owner_type, owner_id)shape was replaced with three real FKs + a CHECK constraint for referential integrity.
Personas
| Persona | Surface | Capabilities |
|---|---|---|
| Coach (staff) | Workout builder / assign sidebar | Create sets, attach definitions, resolve a set for a member |
| Coach | Member detail page | Record metric values on behalf of member |
| Member (via mobile / web) | Self-report metrics | List own values, see latest |
| API consumer | GET /metric-sets/:setId/resolve?memberId=... | Returns { definition, latestValue } for each member metric in the set |
Capabilities
- Catalogue —
metric_definitionsis global (not org-scoped). All orgs share the same vocabulary. - Member-owned set — “Athlete X’s personal lifts catalogue”.
- Workout-owned set — pre-fills
% 1RMlookups for a specific workout template. - Org-owned set — gym defaults; surfaced when no more-specific set exists.
- Resolve —
MetricSetsService.resolve(setId, memberId)returns each definition with the member’s latest recorded value.
Capabilities (gaps)
- No UI for managing
metric_definitions(seeded only). - No “fall through” resolver — if a member has no value for a definition, the resolved entry returns null. Programming UI shows it as “unknown 1RM”.
Related
../body-metrics/— separate concept; covers body composition tracking.../workouts/—metric_sets.workoutIdties a set to a workout template for prescription resolution.../goals/— exercise-PR goals can use amember_metricsrow as the current value.
Source code
- API:
apps/api/src/metric-sets/ - DB:
libs/db/src/lib/schema/metric-sets.ts - Shared:
libs/shared/src/lib/schemas/metric-set.ts