enode Tracking app — features & use cases
@enode/tracking is the coach-facing training app: the screen a strength &
conditioning coach holds on the gym floor while athletes train with enode
velocity sensors. It plans nothing and analyses nothing after the fact — that
is the portal's job. This app owns the session:
connect a sensor, watch reps arrive live, decide the next load, record the set,
and get the result safely to the backend.
This page describes the product. For the testable, ID'd statement of the same scope see tracking app requirements; for the manual protocol that exercises it see tracking system tests.
At a glance
| Who it is for | Strength & conditioning coaches, team staff, testing/rehab practitioners, and individual athletes |
| Platforms | Native iOS and Android (Capacitor shell) and installable PWA — one codebase, static export |
| Hardware | enode velocity sensors over Bluetooth LE, including SmartBar dual-sensor pairs; also Apple Watch, phone, and pure manual entry |
| Works offline | Yes — a downloaded training day starts, records and finishes with no network |
| Backend | The enode API; the app is a client, never a server |
1. Core concepts
Five nouns explain most of the UI.
- Workout — the plan: which exercises, in which order, for which athletes. It comes from the portal's schedule, from a template, or is built ad hoc.
- Session — one athlete's execution of one workout item. Sessions key off the workout item, not the exercise, so the same exercise twice in a plan stays two independent slots.
- Set — one bout of work: load, reps, measurement values, warm-up vs working, tracked side (bilateral / left / right), an optional video clip, a note, tags, and the copied prescription (intensity / exertion / rest).
- Rep — one repetition as the sensor recorded it: concentric and optionally eccentric phase, each with a full raw trajectory from which every metric is derived, plus a validity flag.
- Station — one training slot on screen: an athlete, an exercise, and optionally a sensor. A tablet runs up to three in parallel; a phone runs one.
Two rules shape a great deal of behaviour:
- The focus metric is the single headline number per session, resolved from the exercise and equipment group — 1RM for strength, peak force for strength on a flywheel, top load for weightlifting, jump height for jumps. Live readouts, spoken feedback, charts and summaries all orient around it.
- Invalid reps are always visible and never counted. A rep the validator or the coach rules out keeps its place and its number in every list, chart and timeline — muted — but contributes to no count, mean, estimate or export. See invalid reps.
2. Feature areas
2.1 Access, accounts and roles
- Passwordless email + one-time-code sign-in. The code auto-submits on the last digit; a stored token means a returning coach lands straight in the session screen on cold start.
- Sign-in and sign-up are separate flows so a typo cannot silently create a duplicate account; picking the wrong one offers a one-tap switch.
- Pro-only deployment. A legacy One account is blocked at sign-in with a pointer to the One app. Registration (organisation name + email) works in-app, and legacy accounts can start a migration.
- Roles — owner, administrator, coach, athlete. An athlete-role user is auto-selected as the sole participant, sees no athlete-editing affordances, and is pinned to one station.
- Privilege-aware UI — the backend's privilege catalogue (data export, raw data, planner write, custom charts, firmware update, …) gates the affordances that need it.
- Guided redirects. Device-session limits, pending invitations and incomplete onboarding are resolved through dedicated dialogs, not a raw error code.
- OTP-protected account deletion from the profile drawer.
Use case — a coach picks up the gym's shared tablet, signs in with their email and the code from their inbox, and is on today's schedule in under a minute.
2.2 Today's schedule
The landing surface lists the week's workouts for the signed-in coach, bucketed by day with today labelled first, then sub-grouped by time of day ("all day" pinned ahead of the timed buckets). The workout closest to the current time is highlighted.
- Pull to refresh, or the sidebar's Refresh row, picks up a workout just scheduled in the portal.
- An empty day says so explicitly; a failed load offers Try again rather than a blank screen.
- An offline-ready chip tells the coach, before they walk into a basement gym, whether that day will start without signal — and downloads it on tap.
2.3 Starting a training
From an empty station — the workout list, its + menu and its actions — the coach can:
| Entry | What it is for |
|---|---|
| Start a scheduled workout | The normal path — the plan the portal already holds |
| Create a new workout | Nothing was planned; build it on the spot |
| Template library | The one template entry: browse own and shared templates and start one (§2.4) |
| Feedback training | Sensor-only work with no plan at all (§2.9) |
| Log past workout | Retrospective entry for a training that already happened (§2.13) |
The prep drawer is where a workout is shaped before it starts: name it, pick athletes, add and reorder exercises, edit each item's blocks (volume, intensity, exertion, set count), multi-select several items and edit their blocks together, and add notes and tags. Mismatched selections are explained rather than silently merged — "can't edit together" for incompatible exercise groups, and a destructive confirm before shared blocks overwrite individual ones. Closing with unsaved edits prompts to discard.
Exercises are found with multi-token search plus a typo-tolerant "did you mean?" fallback, and can be starred as favourites.
Pressing Start verifies a recording slot is free, then commits the training view behind the drawer so the drawer's slide-out reveals it, instead of a hard screen cut.
Licence-aware participation. An athlete whose licence blocks recording can still be shown as a participant — the picker warns first, their chip is badged, and they never appear in the in-session athlete switcher, so no data is ever recorded for them. A workout whose participants are all blocked cannot be started. Athletes archived in the portal disappear from every live surface, and athletes whose exercises aren't cached are flagged before an offline start.
Use case — a scheduled team session runs, but two athletes swap in at the door. The coach opens the prep drawer, replaces the participants, adds a superset for the accessory block, and starts — the plan on the server is untouched.
2.4 The template library — own and shared
Workout templates are authored in the Enode Portal; the tracking app is where they get used. The Library drawer opens from the create menu, the sidebar panel or the collapsed sidebar spine, and lists the coach's own templates alongside the ones colleagues share with them.
- Yours · Organisation · Public. A single-select segmented picker scopes the list — one segment at a time, not a combination — and it appears only when there is actually more than one scope to choose from. A coach nobody shares with sees the Library exactly as it was before sharing existed: one flat list, no extra chrome.
- Attribution, not a permission label. A template from someone else carries a building glyph (inside the organisation) or a globe (published to everyone), plus the author's name when the backend supplies one. Own templates are marked too, so an author can see at a glance what they published.
- Found by what is inside it. Search matches the template's name, its tags, the names of the exercises it contains, and the muscles and muscle groups it trains — a shared template is titled in someone else's vocabulary, so its content is often the only usable way in, and "legs" is the most natural query there is even though no muscle is called that. A muscle query ranks by how much of the session that muscle actually is; a name match still wins over it. A miss offers typo-tolerant "did you mean" chips.
- Read, then run. Tapping a card opens a read-only detail: provenance, note, tags, and the exercise list. Its only action is Use template, which hands the template to the normal prep drawer — the word "Start" is reserved for the one button that really begins recording, at the end of that drawer. On a tablet, opening the Library from a station column prepares the workout in that column.
- Nothing is edited in place — starting already copies. Someone else's template is never editable, even when the backend grants write access. Use template hands the prep drawer its own copy of the row, so the coach can rename it, swap exercises and rewrite every block for this session without touching the source. That is why the Library has no separate "Save a copy": on a training device a template is a starting point, not an entry to file away. Curating your own template list is portal work. See sharing.md for why ownership and reach are separate questions.
- Foreign exercises are made yours automatically. A shared template can contain an exercise its author created, which the backend would refuse to prepare for your athletes. Opening it in the prep drawer quietly puts that exercise into your own catalogue, so it records like any other. It never blocks — and if the device has no connection, the drawer says so once instead of letting the exercise fail silently in the training.
- Honest about what it cannot do. Templates are not part of the offline snapshot, so with no cached list and no connection the Library says so rather than pretending to be empty. A stale-but-usable list gets a hint above it, not a block. And because nothing tells the app a template was withdrawn, an open detail view that drops out of the feed flips to "No longer shared" with its actions disabled.
Use case — a coach joins a club that already runs enode. Without importing anything, the Library shows the head coach's published templates; the coach searches "split squat", finds a session by its contents, uses it, and edits the accessory block for their own athletes in the prep drawer. The head coach's version is untouched.
2.5 Stations — one screen, several athletes
The training shell is device-aware:
- Phone — one full-viewport station.
- Tablet — up to three station columns side by side, each fully independent (own workout, own sensor, own drawers). Columns snap-scroll when they overflow, and columns that no longer fit are flagged with a guarded "close hidden stations?" action.
- The last station can never be closed — it falls back to the schedule view.
A spine/rail sidebar collapses to icons and expands to a panel, holding Settings, Refresh, Open new station, Load workouts for offline, Manage stations, Pending uploads, Profile, and (in development builds) the debug area.
Recording slots are licensed. The device session carries a weight granting how many stations may record; the rest are feedback-only. An empty column past the limit offers Request station slot, and a station that becomes over-limit gets a blocking overlay with Request slot / Finish & save / Discard. Athletes are always pinned to one recording slot.
A sensor can be owned by the station rather than by a session, so it survives finishing one athlete and starting the next — the "keep connected?" prompt at finish defaults to keeping it.
Use case — three lifters rotate through a squat rack, a trap-bar deadlift and a jump station on one iPad. Each column tracks its own athlete; the coach taps between them without disconnecting anything.
2.6 Sensors, placement and calibration
Full technical detail lives in BLE & sensors.
One connect flow on every platform — Web Bluetooth in the browser, native CoreBluetooth/GATT on iOS and Android — with distinct Searching and Connecting phases, live signal strength where the platform allows it, and cancel at any point. Bluetooth being switched off is detected before scanning, so the sheet says "turn Bluetooth on" instead of timing out into "no sensor found".
What can be tracked from:
| Source | Notes |
|---|---|
| Single enode sensor | Streams motion at ~50 Hz plus a rep-end event per rep |
| SmartBar pair | Two sensors on one bar become one logical unit; sides assignable, swappable and removable; one sensor alone still works |
| Apple Watch / phone | Alternative tracking sources per exercise |
| Manual entry | No hardware at all |
The connect sheet searches with a looping "bring your sensor to the device" animation that cross-fades into a live 3D view of the connected sensor, and from there the coach can see battery and calibration state, recalibrate, add a second SmartBar side, assign an already-connected sensor to this station, swap sides, stop an auto-reconnect, or disconnect. It opens both from an active training and from an empty station column, so pairing can happen before the workout does.
The connect pill shows state at a glance: the enode mark when nothing is paired, a sensor silhouette with battery for a single sensor, a bar badge with left/right battery for a SmartBar, and a red corner badge when the link is weak.
Where the sensor goes. A placement card is resolved from the exercise's equipment — barbell, bar-end sensor, wrist (dumbbell/kettlebell), body/hip (bodyweight), weight stack (cable/machine), flywheel — with an illustration and the sensor glowing in position, shown once per placement per workout. A bar sensor guard shows a non-dismissible "incompatible" card and stops rep capture when a SmartBar is on a non-barbell exercise.
Training modes. The sensor is configured per exercise: strength, weightlifting, flywheel, jump squat, counter-movement jump, drop jump, plus raw / science modes and a selectable low-pass filter.
What each mode measures:
| Mode | Per-rep metrics |
|---|---|
| Strength / weightlifting | Duration, distance, mean velocity, mean propulsive velocity, velocity of movement, peak velocity, peak power, peak RFD |
| Flywheel | Duration, distance, mean & peak velocity, mean & peak power, mean & peak force |
| Jumps | Duration, jump height, peak velocity, peak RFD, peak power; CMJ/squat jump add RSI-mod, drop jump adds RSI and contact time |
| Eccentric phase | Duration, distance, mean & peak velocity, peak RFD |
Link health and recovery.
- Auto-reconnect — an involuntary drop keeps the sensor in the pool as reconnecting and re-searches for the same device for 60 s, then re-wires the existing sensor so session state survives. A deliberate disconnect does not trigger it.
- Weak-connection warning — per-sensor throughput is measured against the nominal 50 Hz; a sustained shortfall flags the link, so the coach knows the radio is saturated rather than guessing.
- Live device readouts — battery, signal strength, temperature, firmware and hardware revisions, and an explicit "sensor API too new for this app" signal.
- Reassignable without reconnecting — sensors live in a pool, so one (or a whole SmartBar pair) can move between stations mid-session.
- Steady-state detection — the app knows when the bar is at rest, and drives a "sensor ready / wait after impact" state from it.
- Durable rep buffering — unsaved reps are parked per station × item × athlete, so a crash or an athlete switch cannot lose them.
Calibration — a six-face routine with a live 3D sensor view that highlights the face currently pointing down and remembers which faces are done.
Use case — mid-set a sensor drops out of range. The station shows "reconnecting" rather than pretending to record; when it comes back the coach finishes the set with the reps that were captured.
2.7 The tracking view
Opening an exercise gives a per-exercise sheet with:
- a sticky header card — exercise name, athlete avatar, focus-metric value and the rest timer;
- the athlete switcher (tap the avatar) to rotate through everyone with a session for this item, and the exercise switcher (tap the title) to jump to another item keeping the current athlete;
- the tag row — an exercise guidance chip (see below) ahead of the session tags — and a per-side toggle for unilateral work;
- the set list;
- a floating card stack for recommendations, load advice and the load bar — a +/- stepper over the active set's load, in whatever unit that exercise loads in: kilograms or pounds for barbell and weight-stack work, grams square metre for a flywheel's moment of inertia;
- a camera button (bottom-left) and an Insights button (bottom-right);
- an options menu.
Exercise guidance — how the movement is performed. The tag row under the header card leads with one "Instructions" chip — with a play glyph when the exercise description in force carries a demonstration video — that opens the athlete's answer to "what am I supposed to do here": the workout item's note, the cue on that description, the exercise's own detail, the catalogue text, and the video.
The chip is deliberately quiet. It renders only when there is something to show — most exercises carry no guidance at all — it never opens itself, and it has exactly a tag pill's footprint, so a row the athlete already scans without reading gains one more chip rather than a new surface. The item note is no longer printed as a line of its own; it is the first block in the sheet, and the chip's hover title. Tapping the chip opens a bottom sheet with the video first, then each block of text; blocks are labelled only when there is more than one to tell apart.
The tag row itself folds after three pills into a "+N" chip (tap to expand, "Show less" to fold), so a session that inherited a dozen tags never pushes the set list down.
The video is click-to-play: nothing is requested from YouTube or Vimeo until
somebody presses play, and the parsing routes to the no-cookie / do-not-track
hosts. A link that cannot be classified is offered as a plain link, never framed.
See sharing for where the video lives and who may edit it, and
training/exercise-guidance.ts for the ordering and de-duplication rules.
The options menu holds: focus-metric selection, how the movement is performed (the same sheet as the guidance row, always in the same place), feedback mode (metric / muscle quality / technique, remembered per exercise group), a sensor distance threshold override, voice feedback, single-rep review, show eccentric reps, and the set-summary confirmation step.
Distance threshold (minimum rep distance): the menu offers 5 cm and
10–100 cm in 10 cm steps; every value written to the sensor is clamped to the
firmware's 5–100 cm envelope (below 5 cm the sensor produces ghost reps), so an
older persisted 110 / 120 cm override reaches it as 100 cm. The override is
device-wide: feedback training shows the same menu as a ruler button in its
header (default there: 20 cm, as it has no exercise to take one from), and a
value picked in either place applies in both. Values and helpers live in
training/distance-threshold.ts.
Split detail mode. On a wide enough column, Insights renders inline beside the tracking pane instead of as a bottom sheet.
2.8 Live tracking
While a set is running:
- Reps appear as they happen — a live overlay flashes each rep with its focus-metric value the instant the sensor reports it, over a stats row.
- A live RIR / exertion readout estimates how close the athlete is to failure from the velocity trend, without needing a stored 1RM.
- A set-end threshold indicator turns the card negative when the prescribed stop criterion (e.g. velocity loss) is met.
- An inactivity countdown can auto-confirm the set; otherwise the coach ✓ commits or ✗ discards.
- A rest timer counts down to the recommended rest target (from the block's prescription or the rest estimator) or up from the last set — tap to flip, and the choice is remembered. The countdown turns red at zero.
- An optional set-summary step lets the coach review the draft set before it commits, invalidating or deleting reps first. A new rep arriving while the summary is open auto-commits and resumes recording, so hesitating loses nothing.
Feedback modes change what the athlete-facing body shows:
| Mode | Shows |
|---|---|
| Metric | A focus-metric grid — the latest value per metric and its drop from the set's best |
| Muscle quality | A stimulus ring splitting the set into power / hypertrophy / strength, a ranked list, a set-capacity bar and a per-rep bar chart |
| Technique | Sagittal and frontal bar path, two focus metrics, and a velocity/acceleration dual-axis chart |
Spoken feedback reads each rep's focus-metric value aloud in the app's language — "0.45 meters per second", with the unit only on the first rep of a set. It respects the phone's silent switch and never delays a rep. Switched on per device in the Local settings sheet; on a multi-station pad exactly one station speaks, shown by a lit speaker symbol and moved by tapping a muted one. See voice feedback.
The screen stays awake for as long as any station is live.
Use case — velocity-based training. The bar-speed target is 0.75 m/s; the coach hears the app call each rep and stops the set the moment the threshold indicator turns.
2.9 Feedback training — sensor-only, no plan
A separate flow for pure live feedback with no workout behind it: pick a configured feedback mode, connect a sensor, and train. The same live rep overlay drives capture; sets accumulate in a transient list (newest first, capped) and are never uploaded. Each station runs its own independent feedback flow, and it works even on a device with no recording slots left.
Load-dependent metrics are workout-only. With no workout there is no load,
so metrics that scale by it (force, power, … — the metric's loadingFactor
flag) are neither shown nor spoken here: the view takes the first
load-independent entry of the coach's focus-metric selection as its focus
metric, the rep overlay leaves load-dependent cells out, the focus-metrics sheet
lists those rows locked as "Only measured in a workout", and a note under the
mode name says where they are measured. The saved selection is shared with the
workout flow and stays untouched.
Spoken rep feedback works here too, using the same device-wide switch and the same one-station-speaks rule as active training — see voice feedback.
Use case — an athlete drops in for a quick jump test between sessions. The coach opens feedback training, taps CMJ, and reads the numbers off the screen — nothing is scheduled, nothing is stored.
2.10 Planned sets, warm-ups and progression
The app materialises the plan into concrete sets ahead of the athlete:
- Prescribed sets are built from the block's normatives plus the athlete's
known 1RM and shown as template rows. They follow the blocks' programmed
order, not the order the server lists them in. - A programmed load is the total load on every exercise. Jump exercises and Bodyweight equipment (pull-ups, dips, …) are programmed and stored as the whole system mass too; Bodyweight equipment only displays it relative to body weight ("BW +20 kg"). A programmed flywheel inertia becomes the set's inertia as is.
- The plan stays visible on planned and active rows. Under the set title a row states the prescribed intensity (percentage of 1RM, load, flywheel inertia, or velocity target) and the set-end stop, like the iOS set cell. The right side shows the load: the prescription on a template row, the load on the bar on the active row.
- The first set's load. With warm-up guidance off, the first working set starts at its programmed load, rounded to a loadable bar increment the same way an accepted load advice is. With warm-up guidance on, the warm-up ramp starts at the initial load suggestion. From the second set on, the active set keeps the last-used load and the programmed load arrives through the load advice.
- A programmed rep count is kept at any load. A rep count the coach programmed stays as written when the load on the bar differs from the prescription. Only a reps-in-reserve target is re-estimated from that load.
- Automatic warm-up ramp — a decaying step model climbs from the current or starting load to the first working set's load, and stops once working sets begin. Gated on the coach's warm-up-guidance setting. Not built for jump exercises or flywheel equipment, as on iOS.
- Unilateral exercises expand one prescribed set into a Left slot then a Right slot; the pair counts once toward progress, and once in the session summaries — each side is half a prescribed set, so a per-side session totals the same as the same work tracked bilaterally (see ADR 0011).
- The set list shows a timeline — completed sets checked, the active set marked, templates hollow — with per-set focus-metric bars and a "schedule completed" banner once the prescribed working sets are done.
- Initial load suggestion where the first set has no programmed load to start from (and as the warm-up ramp's start), with dedicated paths for jump exercises and flywheel equipment and sensible floors for bodyweight and dumbbell work.
- The scheme rebuilds automatically whenever the 1RM, the blocks or the unilateral setting change.
2.11 Completing and editing a set
Finishing a set opens the complete-set sheet, pre-filled with what the sensor detected: reps, load, exertion and the rest target, with a suggested RIR from the coaching engine. Everything is editable before saving; the same sheet edits an already-completed set.
- A numeric metric keyboard built for the gym replaces the OS keyboard for every measurement field on touch devices — per-metric layouts for load, velocity, percentage, reps, RIR, drop and mm:ss times, the decimal key hidden where the metric is an integer, a "6+" overflow bucket for bounded metrics like RIR, and an accessory bar to step between grouped fields. Optional normatives (exertion, rest) can be cleared outright.
- A velocity-profile strip inside the load keyboard shows the predicted velocity at a load, its %1RM placement, and the peak-power and peak-load zones — and doubles as a draggable load slider.
- Load stepping — a floating − / + pill with a fine increment under 20 kg and a coarse one above; tapping the value types an exact load. Advised loads snap to increments a real barbell can be loaded to. Flywheel work takes an inertia value instead of a mass.
- Bodyweight-relative loading — bodyweight exercises display and accept load as a delta ("BW", "BW +10 kg"), while storage stays absolute.
- Per-rep control — expand the reps card, multi-select reps, and toggle them valid or delete them. The coach's override always beats the automatic validator.
- 1RM editing in-session — tap the 1RM stat and the metric keyboard opens with a "reset to automatic value" action; an overridden value is marked.
- Tags and notes at workout, session, set and rep level. Session-eligible tags cascade from the workout down and back out again when removed.
- Move a set to another item and athlete (only where equipment and exercise group match); both source and destination schemes rebuild. Exchange the exercise mid-session, and the sessions re-point — 1RM baseline, models and set scheme follow; slots with completed sets only offer similar exercises.
- Deleting a set also drops its recorded video.
2.12 Coaching intelligence
The coaching math runs on the device, ported from the iOS coaching core.
| Capability | What the coach gets |
|---|---|
| 1RM estimation | A confidence-fused estimate across the session's sets, a momentary 1RM from the latest set, and a synthetic pre-rep estimate from load, exertion and rep count that drives live load advice. Outlier reps are cleaned out first. |
| Exertion / RIR | Reps-in-reserve estimated three ways — from %1RM, from the load–velocity profile, and from a 1RM-independent mean-velocity exertion profile — then fused. Also powers the "target RIR reached, end the set" criterion. |
| Automatic rep validation | After a tracked set, border reps that deviate in distance or velocity, and reps whose raw trajectory shape deviates from the set's mean (compared over vertical position, so it is tempo-invariant), are flagged — never deleted. |
| Capacity index | A "how hard was that set" score from velocity loss, rep count and estimated loading. |
| Muscle stimulus | Splits the set into competing power / hypertrophy / strength scores — the Muscle-Quality feedback mode. |
| Rest recommendation | A model over the last and next set's %1RM and the last RIR, clamped to a sane range, feeding the rest timer's target. |
| Technique analysis | Per-rep phase segmentation (lowering / braking / pause / propulsive / deceleration for strength; unweighting / yielding / braking / propulsive / flight / falling for jumps), plus bar path, inclination/rotation, velocity, acceleration, power and force traces. |
| Dual-sensor fusion | Two SmartBar trajectories are resampled, rotated into a shared frame, validated against the bar geometry and merged — the result is indistinguishable from a single-sensor rep downstream. |
Three recommendation cards surface this during a session, each dismissible and none of them ever acting on its own:
- Load advice — the scheme's recommended load for the active set, shown only when it differs meaningfully from what is on the bar; tap to accept. Derived from the athlete's 1RM, so it applies to mass-loaded exercises only — a flywheel's inertia has no 1RM to prescribe against and gets no advice card.
- Rest / velocity stop — during rest, a soft "consider ending this exercise" when the velocity kill switch fires.
- Switch recommendation — after each set, the next move: rotate to the next athlete, advance to the next exercise, a two-option choice inside supersets, an "exercise complete" card, or "workout finished".
Load and switch advice respect the coach's AI-guidance settings.
Use case — testing day. The coach works up in load; after each set the app posts an updated 1RM estimate and an RIR reading, and eventually recommends stopping before a genuine failure attempt.
2.13 Retrospective entry — logging a past workout
A separate, self-contained flow for a training that already happened: choose a source (a scheduled workout this week, a template, or a new one), set the start date and time, then fill in each exercise card. A per-exercise rest value drives the set timestamps, so one start time reconstructs a plausible session clock. Rows are seeded from the prescribed working sets and the athlete's 1RM, rounded to loadable bar increments, with inline reps / load / RIR cells, per-set notes, tags and removal, and independent entries per athlete. Upload builds the same wire payload as a live finish. Exiting with data prompts to discard.
2.14 Video
See set video recording.
- Record a clip per set from a native camera overlay, with the live rep feedback cards drawn over the preview. Available while a sensor is connected; only one station can hold the camera at a time.
- Front/back camera switchable before the first rep, and a shutter toggle between auto and a fast (1/500 s) exposure for crisp bar-path frames.
- Frame-accurate sync. Each clip stores the wall-clock timestamp of its first frame, so every rep's raw trace lines up with the footage to the frame — scrubbing the chart seeks the video, playing the video drives the chart crosshair.
- Automatic trimming to the working window (±2 s of context), so no dead time is stored or scrolled through.
- Review in-session — the clip pins above the technique charts, loops the current rep's window, and goes full screen.
- Full-screen set review — video plus a set timeline of per-rep eccentric/concentric segments, best-rep markers, the technique charts for the rep under the playhead, and chevrons stepping through the exercise's other reviewable sets. Phones get an edge-to-edge layout with a filmstrip timeline.
- Saving a set does not wait for the trim/re-encode — the session keeps moving while the clip finishes in the background.
- Storage is managed — a settings card shows cloud usage with a clear action; local files are only deleted once their upload is confirmed.
2.15 Insights
Insights opens as a bottom sheet from the chart button, or renders inline beside tracking in split mode, in four tabs:
- Overview — seven session-summary cards (1RM, volume load, highest load, total reps, sets, average rest, total time — a unilateral L/R pair counting as one set throughout, per ADR 0011), each with a trend and sparkline tinted by metric category, followed by the athlete's last completed sessions of this exercise.
- Performance — a hero chart for the primary focus metric, a best-set breakdown, a load-response chart once more than one load was used, secondary focus-metric cards, and every remaining metric under "all performance metrics". Tapping a set highlights it across every chart. Any of the compact metric cards can be enlarged into the hero chart's full form — y-axis, session mean ± std band, per-rep bars and best-rep callout — from the control in its header; one at a time, so enlarging a second metric shrinks the first.
- Technique — a set and rep picker that auto-follows the newest recorded set, a shared crosshair scrubbed across dual-axis charts (power & force, velocity & acceleration), separate inclination and rotation charts with a barbell illustration that tilts to the scrubbed angle, a bar-path trajectory with force vectors, and the review video. Trajectory data can be flipped for barbell work.
- Reps — one metric charted across every rep of every set, one card per set, on a session-wide scale so a bar means the same thing in the last set as in the first. One row per rep pair: the concentric as the full bar, its eccentric beneath as a slighter, hatched one with a smaller reading — present and comparable without competing with the lift. A metric the eccentric is never measured on (force, power) drops that line entirely rather than drawing an empty track under every bar. Three ways to the metric: a dropdown in the pinned header naming what is charted and offering every measured metric, the pill row at the foot for switching between neighbours, and swiping the list. Reps can be tap-multi-selected to toggle validity or delete them, exactly as in the complete-set drawer's rep list — but across the whole session, so reps from any number of sets can be ruled out in one action. The floating action pill replaces the metric picker while a selection is live, and every affected set commits in a single session write. Each set is titled above its card — the set number against tracked reps × load (the flywheel's inertia where that is the load), with the charted metric's best and average on a second line — and a Range switch beside the metric picker adds the per-set spread: a strip above the rows drawing each phase's lowest, highest and average value as one bracket, and the same average as a tick in every bar so a rep can be read against its set. The strip doubles as the card's legend — labelled Ø CON / Ø ECC and styled like the bars beneath — so the phases are named once per card instead of on every row. Invalid reps keep their bar but stay out of the spread.
2.16 Finishing a training
- Finish training queues the upload locally first, then closes immediately and uploads in the background. A full-station lock overlay blocks interaction while it commits, and a blocking dialog with Retry / Keep training appears if the data could not even be queued.
- Finishing with nothing recorded asks first — the workout composition is never stored server-side, so it would otherwise be lost silently.
- Discard asks for confirmation too.
- The "keep sensor connected?" prompt is always escapable (✕, backdrop, Escape, hardware back) and tears nothing down when backed out of.
- Confirmed results appear in the portal's history afterwards.
2.17 Offline, durability and recovery
See offline & sync and ADR 0002.
- Ambient connectivity signal — a soft offline banner and a brief "back online" confirmation. Ordinary API traffic doubles as the heartbeat, so nothing polls. The app distinguishes device offline from backend unreachable from planned maintenance.
- Offline-ready days — the day's schedules, every athlete × exercise pair needed to start a session, and the global reference catalogues are cached, so the app cold-starts and trains with no network. Partial coverage is named, not hidden: "N athletes can't be started offline" lists who.
- Durable in-session state — the live session tree is mirrored to IndexedDB as it changes, with raw rep data stored losslessly so charts survive a crash intact. Reps of a not-yet-completed set are parked separately; recovery re-presents them rather than auto-committing.
- The shell survives too — each station's live draft (item order, supersets, participants, mid-session edits) is persisted and re-associated on recovery.
- Crash resume — relaunching after a force-kill offers "resume interrupted training?", restoring stations and sessions, or discarding them along with the local videos.
- Upload outbox — a finished workout's payload is queued before the network call, so finishing never blocks on signal. A floating indicator shows queued sessions, queued videos and their combined size, with a tap to retry and a near-quota storage warning. Videos queue separately, gated on their session having uploaded first, and one poisoned payload cannot wedge the rest.
- Account-scoped storage — the offline database is stamped with the backend and user; switching account or environment wipes it rather than mixing data.
- Leave guards — hardware back or an edge swipe mid-training raises a "leave training?" dialog; a hard navigation raises the browser's own warning.
- Stale-but-usable beats empty — a view with cached data shows a subtle staleness hint; only a view with no usable data escalates to a real error. A reference catalogue the sensor configuration depends on failing to load produces an explicit, blocking "reload the app" dialog rather than a silently broken session.
Use case — a training camp in a gym with no Wi-Fi and no cell service. The coach loads the day on the hotel network in the morning, trains four sessions offline, and everything uploads by itself when the bus gets signal.
2.18 Broadcasting a session live
The tracking app is the producer for the Live Hub: when the device is assigned to a live session, every completed, edited or deleted set is pushed to the backend as it happens, and the portal renders it as a leaderboard, a weightlifting/powerlifting competition attempt board, or a chronological live stream on a gym display. A floating LIVE chip shows the broadcast state.
The push is best-effort by design — a failed broadcast is reported quietly and never disturbs the authoritative local session.
Use case — an in-house competition. Athletes lift; the screen in the corner updates the attempt board and rankings live, without anyone typing anything.
2.19 Settings & personalization
The Settings drawer buffers its edits and commits them on Save:
| Section | Contents |
|---|---|
| Account | Profile — name, email, licence plan (device limit, expiry, source), role, trainable flag, body weight/height, gender, birthdate; legal links; log out |
| App settings | Language, metric ↔ imperial system, beta-tester opt-in |
| In-training guidance | Warm-up guidance, load guidance, rest-period guidance |
| Focus metrics | Which metrics headline each exercise group |
| enode sensor | Set timeout, distance threshold (with reset to default) |
| This device | The device session's name |
| Video storage | Cloud storage usage, with a clear action |
| Help & support | Report a problem, send diagnostics, help center, support & feedback |
- Units apply to every displayed weight, height and load immediately, with no reload. Seventeen measurement dimensions carry their own symbol, long name and precision; only mass and length actually differ between systems.
- Language catalogs are delivered over the air, so a new translation ships without an app release, and the available languages come from the server. Server entity names (exercises, metrics, muscles) localize independently by id, and switching language re-fetches every localized catalogue in the background.
- In-session preferences live in the tracking view's options menu (§2.7) and are remembered across launches.
- The profile drawer shows the app version.
2.20 Reliability, support and platform behaviour
- One error engine. Every failure is classified into offline / recoverable / redirect / blocking and routed to the matching surface — toast, inline alert beside the field, or blocking dialog — showing the backend's localized message where there is one, with Try again and Report on application errors. See error handling.
- Support reports — a versioned diagnostics snapshot (app, OS, device, locale, connectivity, breadcrumbs; secrets excluded) can be sent from Settings, from a crash, or attached to feedback.
- Native shell — iOS safe-area handling, zoom lock, native HTTP stack, and native Bluetooth, camera and realtime plugins.
- Left-edge swipe back app-wide, like a native app, with training-exit opting out.
- Installable PWA with the same feature set minus the native-only pieces (video capture and the native BLE transports).
- Spotlight-tour infrastructure is mounted and completion state is stored per account, so a tour finished on one device stays finished everywhere — but no tracking-app tour is authored yet. Contextual manual sections are registered per screen; the button that surfaces them is currently disabled. See walkthroughs.
2.21 Developer & internal tooling
A /debug/* area is gated in one place and absent from production builds. It
lets most of the app be exercised without hardware:
| Route | Purpose |
|---|---|
/debug/server | Switch which backend the app talks to |
/debug/errors | Fire real endpoints (slow, abort, specific status codes) and watch each failure route through the production error surfaces |
/debug/connection | Every sensor-connection state, single sensor and SmartBar |
/debug/sensor-placement | Every placement card with real copy and images, including the incompatible variants |
/debug/recording | The two live rep-feedback surfaces, with and without the camera, driven by mock reps |
/debug/set-review | The full set-review surface with a sample video and synthetic reps |
/debug/player | The replay player in isolation — loaded, loading, unavailable |
/debug/recommendations | The recommendation-card layouts |
/debug/metric-keyboards | The metric keyboard across field types, plus the live keyboard-mode decision |
/debug/performance-layout | A mock preview of the Performance section layout |
/debug/sidebar | The station sidebar expanded and collapsed |
/debug/styleguide | The design system as implemented |
/debug/globals | Developer globals — detail mode, device-session weight override, licence and token inspection |
/debug/screenshots | App Store / Play Store marketing tiles rendered at exact export resolution from real captured sessions |
3. Representative use cases
| Scenario | What the app does |
|---|---|
| Velocity-based training | Live per-rep velocity, spoken callouts, velocity-loss stop criterion, live RIR readout, load advice snapped to real plate increments |
| Team circuit on one tablet | Up to three parallel stations, per-station sensors and athletes, athlete switcher for rotation, station-owned sensors across athlete changes |
| 1RM / profiling test day | Momentary 1RM per set, fused exertion estimate, peak-power and peak-load zones in the load keyboard, switch/stop recommendations |
| Technique coaching | Camera clip per set, frame-accurate bar-path overlay, sagittal + frontal trajectories, phase segmentation, scrub-linked chart and video |
| Jump and reactive-strength testing | CMJ, squat-jump and drop-jump modes with jump height, RSI / RSI-mod and contact time |
| Flywheel / isoinertial training | Inertia-based loading, force- and power-oriented focus metrics, flywheel-specific charts |
| Unilateral / rehab work | Left/right slots expanded from one prescription, per-side tracking, manual entry where no sensor fits |
| Drop-in feedback session | Sensor-only feedback training with no plan and nothing persisted |
| Remote / offline camp | Offline-ready day bundles, durable in-session persistence, crash resume, automatic upload drain |
| In-house competition | Live broadcast of every set to the portal's attempt board and leaderboard on a gym display |
| Retrospective logging | Reconstruct a training that already happened from a source workout and upload it |
4. Known limits
- Video capture is native-only — the web/PWA build cannot record; playback and review still work where a clip exists.
- Rendered/shareable video export (clip plus animated metrics panel) is designed but not built — see video export.
- No tracking-app spotlight tour is authored yet, and the in-app manual button is currently disabled, although both systems are wired.
- Media and Lottie assets are not cached for offline; everything else in a downloaded day is.
- A demonstration video is missing from a training STARTED offline. Exercise descriptions are a session-lifetime memory cache, not part of the offline day bundle, so the videos are warmed when the training opens online and lost when it never did. The guidance TEXT survives either way — the workout item carries the server-resolved cue inline and the catalogue text is a cold-start snapshot.
- Firmware-rejected reps are silent — a movement the sensor refuses outright creates no rep and, for now, no visible cue.
- Invalid reps cannot be marked on the live broadcast — the live wire carries no validity field, so they are withheld rather than shown unmarked.
- Bar-end sensor detection is not wired — the placement card exists, the detection does not.
- Weightlifting-specific technique detectors from the iOS core are deliberately not ported.
- The portal link from the tracking app exists but is intentionally hidden.
5. Related documentation
| Topic | Document |
|---|---|
| Testable requirements | requirements/tracking-app.md |
| Manual test protocol | testing/tracking-system-tests.md |
| Sensors & BLE | ble.md |
| Video capture | video-recording.md |
| Template & naming sharing | sharing.md |
| Offline & sync | offline-and-sync.md |
| Error handling | error-handling.md |
| Invalid reps | invalid-reps.md |
| Live broadcast | live-performance-hub.md |
| Voice feedback | text-to-speech.md |
| Product tours | walkthroughs.md |
| Auth flow | auth.md |
| Translations | i18n.md |