Skip to content

feat(web): compare a ride to your own best, and your curve to 90 days ago #996

Description

@janlauber

Part of #992.

A rider finishes Sweet Spot 3×12 and has no way to ask the obvious question: was that better than last time? The ride summary stands alone, /history lists rides and draws the fitness and curve charts, and nothing puts today next to your own best.

This is the half of "comparisons" that needs no privacy argument at all — no other rider's data is involved, so nothing here touches rides-private-by-default. It can ship without waiting for #994.

Mockups: https://claude.ai/code/artifact/880fdc31-6238-4547-9d3a-b291cda3f50d — bottom left.

The shape

Sweet Spot 3×12 · today
                    today    your best    Δ
avg watts             214          209   +5
execution            91 %         88 %   +3
energy            612 kJ       598 kJ  +14
time in Z4+        18:20        19:05  −0:45

20-minute power · 90 days
   ╭─────────────── 222 W
  ╱      ·······  90 days ago

Two comparisons, both against yourself:

  1. This ride against your best on the same workout. Same workout name, same rider. Δ column, no verdict attached to it.
  2. Your curve now against 90 days ago, which /history already draws from progression.curve (d30 / d90 / all) — this puts the movement in words next to the ride that caused it.

RESEARCH.md §13.3 ranks power-curve bests fourth overall and says why they are the safe one:

Power-curve bests / PRs — monotonic, unambiguous, always good news; safest fitness signal we own.

Rules it has to keep

  • Describe, never grade (ADR-0016, §13.4). A Δ of −0:45 is a number, not a failure. No judgment words, no red for "worse" — --color-danger means something is wrong, and a slightly easier day is not wrong. Use the muted token.
  • Execution is not difficulty. §13.4 is explicit: 100 % in-band on a recovery spin and on 5×5 VO2 are different achievements. Execution may sit in the table; it may not be summed, ranked, or presented as fitness.
  • Scoped honestly. Every load-derived surface is labelled "based on your WattRoom rides" per ADR-0016, and this is one.
  • Cold start. With one ride of a workout there is no "your best" — say so plainly rather than comparing a ride to itself. §13.4's small-N rule applies.
  • No heart rate in anything that could become shareable (ADR-0008).

Where it goes

The ride detail page (/history/[id]) is the natural home — it is where a rider already lands after a session, and /history already owns both charts since #ADR-0020 folded progression into it. Do not add a fifth stat vocabulary: use the words #993's profile section settles on.

Acceptance criteria

  • A finished ride shows today against the rider's best on the same workout, with a Δ column.
  • The curve comparison names both periods and the number each reaches.
  • A first-ever ride of a workout says so instead of comparing against itself.
  • No verdict, no grade, no judgment words; a negative Δ is muted, never danger-coloured.
  • The surface is labelled as scoped to WattRoom rides.
  • Execution appears as a number only — never ranked, summed, or called fitness.
  • Vitest: Δ arithmetic, the cold-start case, and a ride whose workout name has never been ridden before.
  • make ci green.

Deliberately not in scope

Comparing riders to each other — that is room-scoped, needs #994 and probably an ADR, and is tracked under #992. Also out: importing outside activities to compare against, and any "you should have" suggestion (ADR-0016: nudges never gate, and this issue makes no nudges at all).

Related

#992 (epic), ADR-0016 (tone and scoping), RESEARCH.md §13.3 and §13.4, ADR-0020 (why this lives on /history), #993 (the vocabulary).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestworkoutsworkout engine & player

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions