Skip to content

feat(conditions): heat, humidity & altitude race day calculator - #80

Open
aleexwong wants to merge 1 commit into
mainfrom
claude/something-cool-428xv1
Open

feat(conditions): heat, humidity & altitude race day calculator#80
aleexwong wants to merge 1 commit into
mainfrom
claude/something-cool-428xv1

Conversation

@aleexwong

Copy link
Copy Markdown
Owner

Adds /conditions, a tool that answers the two questions runners actually have on race week:

  1. "It's going to be 28°C and humid — what's my realistic finish time?"
  2. "What pace should I run today's workout at?"

There was no weather-aware tool in the app — no mention of heat, humidity, dew point, or altitude anywhere in the codebase — so this fills a real gap rather than restating what the pace/VDOT calculators already do. It builds on the existing Daniels engine instead of duplicating it.

The model

Two penalties, modelled independently and then composed.

Heat keys on the sum of air temperature and dew point (°F) rather than on temperature or relative humidity alone. Dew point is what caps evaporative cooling, so 20°C/90% RH and 30°C/40% RH are very different runs despite both reading as "warm". The penalty is then scaled by race duration — at 24°C/75% a 5K costs ~2% and a marathon ~4.8%. Most hot-weather pace tables apply one flat percentage across all distances, which is why they tend to feel wrong to experienced runners.

Altitude is applied at the fitness level instead of the pace level: VDOT is scaled by the retained VO₂max fraction (~1.8% lost per 300 m above 300 m, partially recovered when acclimatised) and the race is re-predicted through the existing engine. The altitude cost then grows with race distance on its own, with no distance-specific rules.

The reported effective VDOT is back-solved from the final adjusted time, giving a single "what would this equate to at sea level in perfect weather" number.

What the page shows

  • Adjusted finish time and race pace, with the cost split between heat and altitude
  • Re-targeted paces for all five Daniels zones, each evaluated at its own realistic session duration — a 90-minute long run and a set of 400m reps are not equally exposed to heat
  • A temperature sensitivity curve with the user's forecast marked, visibly flat below ~15°C and then bending hard, which is the honest argument for an early start time
  • A risk band with practical guidance, escalating to an explicit "don't race this" at dangerous heat-stress levels

Cold is deliberately not modelled as a bonus; the FAQ says so rather than leaving it unexplained.

Notable changes outside the feature

  • Slider gains an optional thumbLabel. The Radix thumb is the element that receives focus and carries role="slider", so an aria-label on the root left it announced as an unnamed control. The shared component is only consumed by this new code today, and the prop is optional and additive.
  • Route, side nav, prerender list, and sitemap wired up. The page accepts the same ?d=&t= share params the VDOT calculator emits, so you can go from "here's my fitness" to "here's what race day looks like" without retyping.

Verification

Numbers were checked, not just the build:

  • Magnus dew points exact — 30°C/50% → 18.4°C, 25°C/100% → 25.0°C
  • Altitude retention 92.2% at 1600 m, matching published acute-exposure figures
  • Distance scaling reads 5K +2.0% → marathon +4.8% at 24°C/75%, in line with observed marathon heat data
  • tsc clean; ESLint 0 errors and 0 new warnings; production build passes; /conditions prerenders into its own 35 kB chunk
  • Browser pass at 1280px and 390px: sliders, altitude input, and presets all drive live recalculation, no console errors, no horizontal overflow

The browser pass caught two bugs static checks would have missed: the global #root { text-align: center } from the Vite template was centering copy meant to be left-aligned, and the mobile stack buried the answer below the training-paces table. Both fixed.

Notes for review

  • The heat bands are the conventional community anchors with linear interpolation between them, so a 1° forecast change moves the answer smoothly instead of jumping a whole band. Worth a look if you'd tune the numbers differently.
  • Altitude at 1600 m unacclimatised comes out at ~7% for a marathon, which is toward the higher end of the plausible range for a sea-level runner. The acclimatised figure (~4%) lines up well with typical Denver-resident expectations.
  • There are no unit tests in this repo, so the math was verified via a throwaway harness rather than committed tests. Happy to add a test setup if you'd like this math pinned down.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WER1sSjDfXQunuP45nVShs


Generated by Claude Code

Adds /conditions — a tool that answers the two questions runners actually
have on race week: "it's going to be 28°C and humid, what's my realistic
finish time?" and "what pace should I run today's workout at?"

Two penalties are modelled independently and composed:

- Heat is keyed on the sum of air temperature and dew point (°F) rather
  than on temperature or relative humidity alone, since dew point is what
  caps evaporative cooling — 20°C/90% RH and 30°C/40% RH are very
  different runs despite both reading as "warm". The penalty is scaled by
  race duration, so a 5K and a marathon in the same forecast get
  different costs (~2% vs ~4.8% at 24°C/75%) instead of one flat number.

- Altitude is applied at the fitness level: VDOT is scaled by the
  retained VO2max fraction (~1.8% lost per 300m above 300m, partially
  recovered when acclimatised) and the race is re-predicted through the
  existing Daniels engine, so the altitude cost grows with distance
  without any distance-specific rules.

The reported effective VDOT is back-solved from the final adjusted time,
giving a single "what would this equate to at sea level in perfect
weather" number.

Beyond the race prediction the page shows re-targeted training paces for
all five Daniels zones — each evaluated at its own realistic session
duration, because a 90-minute long run and a set of 400m reps are not
equally exposed to heat — and a temperature sensitivity curve with the
user's forecast marked, which is what makes the case for an early start.

Also:
- Slider gains an optional thumbLabel; the thumb is the element that
  receives focus and carries role="slider", so an aria-label on the root
  left it announced as an unnamed control.
- Route, side nav, prerender list and sitemap wired up. The page accepts
  the same ?d=&t= share params the VDOT calculator emits.

Verified with tsc, eslint (no new warnings), a production build, and a
browser pass covering both breakpoints: dew points match the Magnus
formula, altitude retention matches published acute-exposure figures,
and no horizontal overflow at 390px.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WER1sSjDfXQunuP45nVShs
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
trainpace Ready Ready Preview Aug 2, 2026 12:18am

@netlify

netlify Bot commented Jul 30, 2026

Copy link
Copy Markdown

Deploy Preview for trainpace ready!

Name Link
🔨 Latest commit e8db628
🔍 Latest deploy log https://app.netlify.com/projects/trainpace/deploys/6a75f39f39cf8b00087010e4
😎 Deploy Preview https://deploy-preview-80--trainpace.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 61
Accessibility: 89
Best Practices: 100
SEO: 100
PWA: 60
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@codacy-production

codacy-production Bot commented Jul 30, 2026

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 77 high · 21 medium · 2 minor

Alerts:
⚠ 100 issues (≤ 0 issues of at least minor severity)

Results:
100 new issues

Category Results
UnusedCode 4 medium
BestPractice 1 medium
ErrorProne 7 medium
75 high
Security 2 high
CodeStyle 2 minor
Complexity 9 medium

View in Codacy

🟢 Metrics 155 complexity · 4 duplication

Metric Results
Complexity 155
Duplication 4

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants