Hydrolyze Coach Analytics Brainstorm V2

Writing · refreshed Search related

Hydrolyze Coach Data Presentation — Brainstorm v2

Refinement of v1, integrating the user's 2026-06-20 feedback:

  1. Suit adjustment — PBs are recorded in a suit; training usually

isn't. Need a per-(swimmer, stroke, distance) suit delta to normalise times before the training band view.

  1. Dive adjustment — Race swims use a dive start; training

usually doesn't. Need a per-(swimmer, stroke, distance) dive delta to normalise.

  1. Stroke count burden — open question. The user is willing to

defer stroke rate / stroke length to a future Hydrolyze version that has better capture mechanics. v1 doesn't ship this; the option analysis is in §13.

  1. Validations — Coefficient of Variation, per-set pacing

profile, variance trend over time, and population percentile (with data-availability caveat) are confirmed in scope.

  1. External integrations — explicitly deferred (WHOOP, Oura,

Garmin are future).

The structural shape of v1 is unchanged. The diff is in §3.2 (the training band view now has two calibrations baked in), §13 (the stroke count open question is more explicit), and the design tensions in §9 (calibration matters is now load-bearing, not one-of-five).


0. What's unchanged from v1

The 12-section structure of v1 holds. The lucky position (§0) is unchanged — the engine primitives still exist. The McEvoy north star (§1) is unchanged — the F-V framing still anchors. The variance / progression / training-load / pairing-transparency / readiness / squad / ask-the-data axes are unchanged. The first three specs (Swimmer Card v2, Speed-Curve Map, Per-Set Pacing Profile) are unchanged. The "what this is NOT" list is unchanged. The open questions in §12 are partially resolved: the force axis proxy (§12.1) and the phase tagging (§12.7) are still open; the suit/dive calibration is now resolved into v2 §3.2; the stroke count burden is moved to §13.

What follows in this doc is only the diff. For the full brainstorm, see v1. Cross-link both directions.


3.2 The training band view (updated) — the user's correction

The v1 "training band" view showed the last N recorded times as a tight band, with the PB marked. The user pointed out that this view is only honest if the times are effort-equivalent. Two systematic mismatches:

3.2.1 The suit mismatch

Race PBs are usually recorded in a suit. Most training (per McEvoy: "I do most of my swim sessions suited, but we are not going to do any of our sessions suited") is done in a jammers/ brief or no suit. The textile-vs-none delta is ~0.5-1% for sprint events; the tech-vs-none delta (FINA-banned since 2010, but relevant for historical PBs) is ~1-3% for distance events.

Mechanic: a per-(swimmer, stroke, distance) suit delta, learned automatically by the Welford accumulator when the same set context is observed under two different suit_type values. Default: suit_type = 'none'. The training band is computed in suit-equivalent units (typically the suit type of the race PB, to make the band directly comparable to the PB).

Schema: add suit_type to workout_sets and recorded_times. One of none | jammer | textile | fulltech. Default none. Captured at set creation; one extra tap per set.

3.2.2 The dive mismatch

Race swims use a dive block. Training may use an in-water start, a push, or a backstroke start. The dive is worth a meaningful fraction of a sprint time (McEvoy's take-off work took 10m from 3.4s to sub-3.1s — a 0.3s delta on a ~22s race, ~1.4%).

Mechanic: a per-(swimmer, stroke, distance) dive delta, learned automatically by the Welford accumulator when the same set context is observed under two different start_type values. Default: start_type = 'in_water'. The training band is computed in dive-equivalent units (typically the start type of the race PB).

Schema: add start_type to workout_sets and recorded_times. One of in_water | push | dive | backstroke_start. Default in_water. Captured at set creation; one extra tap per set.

3.2.3 The cost of NOT doing this

If the engine treats all recorded times as equivalent, the σ for a sprinter with mixed suit-and-dive history will be inflated. The prediction band will be too wide, the pairing confidence will be understated, and the training-band view will mislead the coach into thinking the swimmer is more variable than they are.

Worse: the PB itself is an outlier (suited + dive) relative to the training distribution (no suit + no dive). A coach who compares a swimmer's training time directly to their PB will be confused by a consistent 2-4% gap. The Welford on raw data will hide the systematic bias; the Welford on normalised data will surface it.

3.2.4 What's buildable from existing data

The recorded_times schema already has gear (per the v1.10+ migration). The new fields are suit_type and start_type. The engine computes the per-swimmer ratios from same-set-different-suit or same-set-different-start observations. No new capture burden on the swimmer or coach beyond a single tag at set creation.

The first migration to add the field is the expensive one. The ratios fall out automatically as the coach tags their existing sets going forward.

3.2.5 The engine change

HistoricalTimePredictor.predict() currently uses a 4-way match: (swimmer, stroke, distance, intensity, gear). The v2 change is a 6-way match: add suit_type and start_type. The prediction becomes:

predicted = PB * intensity_mult * gear_mult * suit_mult * start_mult
sigma     = max(sampleStdDev(last5), sigmaFloor) + suit_uncertainty + start_uncertainty

Where suit_mult and start_mult are the per-(swimmer, stroke, distance) ratios from the Welford on the new swimmer_suits and swimmer_starts tables.

This is a small surface-area change in the engine. The Welford accumulators are already in production for swimmer_gears. The schema migration is a column-add on workout_sets and recorded_times, plus two new tables for the per-swimmer calibration factors. The pairing engine and the coach views benefit automatically.


9. Design tensions (updated)

The v1 tensions are still all valid. The user's correction moves "calibration matters" from one-of-five to load-bearing. The training band view is only honest if the engine normalises for the suit and the start. Without that, the view is a lie that happens to be the right shape.

The full updated tension list:

is OFF the main flow. The coach opens it when they want it.

change the pairing default.

decides.

intensities is comparing apples to oranges. Mixing suits is comparing apples to a slightly different apple. Mixing starts is comparing apples to a slightly faster apple. The engine has to normalise.

more use. The maturation curve tells the coach the value compounds.

problem to be fixed.


12. Open questions (updated)

#Questionv2 status
1The force axis proxy. What is the X in the F-V map?Still open. Three options unchanged.
2Privacy on the squad view. Coach-only? Share with swimmers?Still open. Coach-only default for v1.
3Time horizon defaults. 4 wk? 8 wk? Season-to-date?Still open. 8 weeks default.
4Population percentile scaling. Squad / age / nation?Still open. Squad for v1.
5Where does the surface live? iOS tab? Web?Still open. iOS tab.
6Manual wellness v1 vs skip to wearable integration.Still open. Manual v1.
7Phase tagging. Engine-learned or coach-tagged?Still open. Coach-tagged.
8Negative-split detector. Always on? Coach-tunable?Still open. Always on.
9Suit and dive adjustment fields. When do they land?Resolved in v2 §3.2. Engine change is a 6-way match; schema is a column-add + two new Welford tables.
10Stroke count capture. Future. Best mechanic?Open. See §13.

13. The stroke count burden — option analysis (NEW)

The user is right that stroke count is the gold-standard proxy for stroke length (SL), but the burden of per-rep counting is real. Per-rep counting is a deal-breaker for v1. The options:

v1 options (low burden)

Option A: Per-set swimmer-reported count. At the end of a set, the swimmer tells the coach "I did 18 strokes per length." The coach enters ONE number per set. This is the lowest-burden option that still gives the SL insight. Cost: one verbal exchange per set. Coverage: per-set, not per-rep. Sufficient for the per-swimmer SL benchmark and the SL trend over time.

Option B: Per-set coach-observed count. The coach watches one rep in a set, counts strokes, enters the number. The number becomes the set's SL benchmark. Same coverage as A; lower verbal exchange cost; higher coach-attention cost.

Option C: Per-rep tap entry. The coach taps once per stroke on the phone. WORSE than just counting silently. No.

Recommendation for v1: Option A (per-set swimmer-reported) or B (per-set coach-observed). Both are one number per set. Either works. The deciding factor is which the coach prefers — swimmer agency (A) or coach authority (B).

v2 options (zero burden)

Option D: Apple Watch / Garmin integration. Most modern swim watches detect stroke count automatically via the accelerometer. The data lands in Apple HealthKit or Garmin Connect. Hydrolyze imports via HealthKit (iOS) or a Garmin webhook. Cost to swimmer: wear the watch. Cost to coach: zero. Coverage: per-rep.

Option E: FORM Smart Swim Goggles integration. FORM goggles have an in-goggle accelerometer that detects stroke count, stroke rate, and time per stroke. The data lands in the FORM app. Hydrolyze imports via FORM's API. Cost to swimmer: wear the goggles (already done by some squads). Cost to coach: zero. Coverage: per-rep, with the most accurate stroke detection in the consumer market.

Recommendation for v2: Option D (Apple Watch) as the default, Option E (FORM) as a secondary integration. The user's own squad (per the product framing — "the coach is the authority") suggests that not all swimmers wear watches, so the v1 manual option stays in place as a fallback.

v3 options (research-level)

Option F: Phone audio detection. The coach's phone, poolside, listens for splash patterns and counts strokes. Feasible but unreliable (water sounds, other swimmers, ambient noise). Research-grade; not a product feature.

Option G: Computer vision. The coach's phone, on a tripod, films the lane. A model counts strokes. Cool but heavy lift (inference cost, camera mount, post-pool review time). Out of scope for v1-v3.

Recommendation: skip F and G. The cost / value ratio is wrong for a swim-coaching app.

The v1 cost of NOT having stroke count

Without stroke count, the SL insight is invisible. The Staunton et al. 2025 finding (SL is the dial for 50m sprint, not SR) becomes a teaching point the coach can talk about but can't visualise. The "raise the F-V curve at the bodyweight point" framing (McEvoy) becomes harder to communicate without the SL data.

The v1 workaround: surface the capacity for SL tracking ("stroke count for this set: not captured") in the swim record. When the coach knows the data isn't there, they can decide whether to ask. Better than pretending the data exists.


14. Validations (the user's confirmed-in-scope items)

The user explicitly endorsed the following from v1:

correction (now in §3.2).

caveat (small squads make percentiles meaningless).

The user did NOT explicitly call out the following, but the broad endorsement ("all great ideas") implies v1 §1.1-1.3, §2.4, §3, §4, §5, §6, §7, §8, §10, §11 remain in scope. None of those sections were contested; the user just didn't enumerate them because the callouts above were the ones with active feedback.

The user DEFERRED §6.3 (external integrations: WHOOP, Oura, Garmin). Still future. The v1 manual wellness check (§6.1-6.2) stays in v1 scope.


15. What's in the next-version diff

The v1 → v2 diff is small and concrete:

  1. Add suit_type and start_type columns to workout_sets and

recorded_times.

  1. Add swimmer_suits and swimmer_starts Welford-accumulator

tables.

  1. Update HistoricalTimePredictor.predict() to do a 6-way match

(add suit + start) and apply the per-(swimmer, stroke, distance) multipliers.

  1. Add a suit_delta and start_delta field to the Prediction

returned from the predictor, so the coach UI can show "this time was X% off due to suit/start calibration."

  1. Update the swim record UI to show "stroke count: not captured"

when the data is missing, instead of pretending it's there.

  1. Update the spec docs (ios-native/.../docs/) to reflect the

6-way match.

None of this is a structural rewrite. The engine primitives stay. The Welford infrastructure stays. The 4-way match becomes a 6-way match. The pairing engine and the coach views benefit automatically.


See also

original brainstorm (now superseded; the structural shape is preserved here)

adjustment mechanic in detail

that the calibration is a precondition for

calibration that this sits alongside

suit + dive choices are the most-published in the sport

sibling intensity calibration

— the engine file to extend

existing migration pattern (gear column + swimmer_gears table)

Source

effort of the suit off our non-suit times", "if you can find a better way of doing [stroke count capture], that would be great", "These are all great ideas, and I will look into incorporating all of them")

(Social Kick Ep. 189, Sep 2024)

Published and managed by TARS, an AI co-author built on Nathan's gbrain.