← Back to overview

Road to Berlin 2026 · Development Journey

How this
got built.

It started as a ChatGPT project in January 2026. Four months of conversations, screenshots, and plan adjustments later, the chat hit its context limit — and a real dashboard had to exist.

100+Conversations
40+Strava screenshots
50+Saved memory entries
6Major pivots
Jan–Apr2026
How this all started

Four months in a ChatGPT project.

Before there was a codebase, there was a chat. These are the key moments — reconstructed from the project audit.

Jan 2026

Running log and 2,000 km goal

The project formed as a serious attempt to track a 2026 running habit. First anchors: a yearly goal of 2,000 km, monthly progress tables (target / min OK / actual / buffer), and a fitness baseline of roughly 5K at 5:15/km. The emphasis was on sustainability — not an aggressive plan that would collapse under work and family pressure.

Annual targetRun logBaseline fitness
Feb 2026

Logging becomes a system

Multiple runs were logged, reconciled, and tracked against the 2,000 km goal. The project moved from conversational coaching into something closer to a database — 252.93 km across 21 runs by mid-February, with strict rules: always anchor to actual dates, never alter logged entries retroactively. Strength training (3-day upper/lower split, RPE-tracked) was folded in at the same time.

Master logStrength integrationImmutable logging
Late Feb 2026

Road to Berlin begins to crystallise

Structured workout templates were saved for the first time — 5×2 km at half-marathon pace, 10×1 km between 10K and HM pace, Mona fartlek scaled to pace. A March training draft took shape: threshold Fridays, long Sunday, weekly volume targets of 55–62 km. The chat started looking less like advice and more like a coaching system.

Workout libraryWeekly structureRTB framework
Mar 3 2026

The Road to Berlin macro plan

The biggest single moment in the project. A full race-specific blueprint was created covering January 1 through Berlin Marathon on September 27, 2026. Three parallel views: Planned Blueprint, Actual Performance, Combined Variance Dashboard. Training zone reference tables, monthly projections, weekly structures — all locked into a single named arc: Road to Berlin.

Macro planRTBVariance trackingSub-3:30
Mar 4 2026

Output formats standardised

Strict response schemas were defined for monthly, weekly, and daily coaching views. The "Show me [month]" format locked in HR anchors, pace anchors, session tables, and progression summaries. This was the product-design instinct appearing in a chat: repeatable dashboards, schemas, source-of-truth rules — applied to personal training.

Dashboard schemasFormatting rulesCoaching templates
Mar 7 2026

The data governance pivot

A discrepancy in the March totals surfaced. The user challenged the numbers, cross-referenced Strava, and refused to accept a patch without understanding the delta. The outcome: Strava screenshots became the authoritative source of truth. Existing entries would never be altered retroactively. New runs only appended. It was the moment the project became an audited system, not just a conversation.

Data governanceSource of truthAudit rules
Late Mar–Apr 2026

Adaptive coaching around real life

Intense headaches, nine consecutive days of 3–5 hours sleep, work travel — the chat adapted the plan around all of it. Sleep debt became part of training load. Headache and fatigue became signals, not excuses. The coaching approach evolved: do not force missed mileage back, protect long-run consistency, use easy runs to re-establish rhythm. The plan had to survive real life to be worth having.

Adaptive planningRecovery managementLife resilience
Apr 2026

The context limit

Four months in, ChatGPT told me the conversation had reached its maximum length. Every run, every adjustment, every piece of advice that made sense in the context of the last twelve weeks — about to become read-only. I exported what I could. The plan was fine. The working coach that knew my history was leaving the building. That was the moment it became obvious: this needed to live somewhere it couldn't accidentally end.

Context limitThe pivotWhat came next
Built so far

Building, in chronological order.

Twenty-seven beats since March — what shipped, when, and why it mattered.

Mar 2026

Project foundation

Training plan loaded into Supabase. Strava OAuth + activity sync. A /log page that reconciles every run against its planned session. Working on day one, useful on day two.

Strava syncTraining planRun log
Mar 2026

Mobile-first PWA

Bottom-nav iOS-style pattern, installable PWA, run detail drawer. The dashboard was always meant to be checked from a phone first.

iOS PWABottom navRun drawer
Apr 2026

Lap-by-lap pace chart

Every run gets a per-lap chart with HR overlay. Tempo blocks and interval repeats become visible at a glance instead of buried in Strava's lap table.

Lap analysisHR overlay
Apr 2026

Segment detector

Auto-identifies the genuine tempo block inside an easy-warmup-then-tempo run, and stitches the fast laps of an interval workout into one effort. The first piece of the dashboard reading the run, rather than the user reading the run.

Segment detectorAuto-categorisation
Apr 2026

Confidence score v1 — the first readiness number

Five components, weekly cadence, blunt but legible. Proved that the dashboard could compress training state into a single trustworthy number without becoming a vibes meter.

Readiness v1Weekly review
Apr 2026

Plan recalibration to sub-3:30

After a poor April, the goal moved from sub-3:25 to sub-3:30. Six weeks of plan paces retuned in a single SQL migration with mirrored audit rows under one batch_id — reversible by replaying the audit log.

RecalibrationAudit trail
Apr 2026

Garmin Health API partner application

Filed with Garmin's developer portal for the official OAuth + webhook path. The durable answer to wellness data; an unofficial bridge keeps the dashboard fed in the meantime.

Garmin APIPartner program
May 2026

Race Readiness v2

Score expanded to 13 evidence-based components covering threshold fitness, long-run base, MP-zone volume, chronic load, recovery, taper, and more. Adaptive scoring — missing components drop out of the denominator instead of being imputed, so the score is honest about partial data.

Readiness v2Adaptive scoringEvidence completeness
May 2026

Daily check-in

Niggle status (every day) and self-efficacy (Sundays). Two-tap submit, feeds two readiness components. The cheapest piece of subjective data that meaningfully sharpens the score.

Daily check-inB-side components
May 2026

Garmin sleep + HRV schema

wellness_log schema built with columns for sleep duration, sleep score, HRV, RHR, and body battery. A Python sidecar (garth) handles auth. A mock data route keeps development working while the live data path is sorted.

Garmin sleepHRVwellness_log
May 8 2026

Garmin sync: the dead end

Built a token capture script, stored the OAuth credential in Vercel, deployed a Python cron. The cron authenticated successfully. Then returned zero rows. Every wellness endpoint — sleep, HRV, RHR — came back empty.

Debug pass: a probe function hitting every Garmin API path, tested with both the JWT-embedded GUID and the profile URL UUID as the username, confirmed from a local machine that it wasn't an IP or deployment restriction. Root cause: Garmin's GARMIN_CONNECT_MOBILE_ANDROID_DI client — the one garth reverse-engineers — is API-restricted at the endpoint level. Valid token, valid auth, no data.

Then: Garmin confirmed their developer program is suspended for a few months. Both paths closed at once. The pivot: Garmin Connect already writes sleep, HRV, and RHR to Apple Health. An iOS Shortcut reading HealthKit daily and posting to a /api/wellness/ingest endpoint needs no developer account and no partner approval. That's the path now.

Garmin investigationAPI restrictionHealthKit bridge
May 2026

Daily Guidance reads recovery

Morning read on /today now adapts to last night's sleep + HRV. When recovery is short, quality sessions get pulled back and HR replaces pace as the day's anchor. AI generates the prose with a hand-coded rules-based fallback if the model fails.

Daily GuidanceRecovery-aware AI
May 2026

Post-run AI assessment

Every Strava sync triggers a structured paragraph on each run — execution vs plan, physiology, observations, forward-look. Cached by inputs hash so the same run never costs the model twice; new versions mint when the user adds RPE or how-it-felt later.

Post-run AICached promptsVersioned
May 2026

Sleep × volume correlation chart

Insights page overlays weekly volume and average sleep score on the same axis. Trends become visible before they become injuries.

InsightsCorrelation chart
May 2026

Design system + marketing site split

Migrated from a glass-dark aesthetic to paper-and-ink editorial — token-driven, light by default, one source of truth shared between the marketing site and the dashboard. azimuth-design tokens v0.1.0 adopted across both surfaces. azimuthrun.com landing now deploys independently.

Design systemPaper-and-inkLanding split
May 2026

Internal methodology specification

An ~18,900-word document committed to the repo covering scoring math, plan adjustment rules, AI prompt patterns, missing-data behaviour, and where the as-built has drifted from the original briefs. The basis from which this page and the gated full methodology page are derived.

Methodology specDocumentation
May 2026

Backlog drawer in the dashboard

A list-todo button next to the settings cog opens a panel of every backlog item — in progress, soon, someday, deliberately decided against. The dashboard now tells you what's left to build, not just what's been done.

Backlog drawerIn-app dev workflow
May 2026

Mid-flight weekly check-in

The dashboard writes a mid-week check-in, not just a retrospective on Sundays. Two sentences: progress vs target, and the most important coaching note for the rest of the week.

Mid-flight AIWeekly review v2
May 2026

Inline SVG brand mark + theme-aware icons

Replaced bitmap PWA icons with a single inline SVG brand mark that picks up theme tokens — one source of truth for the dashboard, marketing site, and app icon.

Brand markSVG iconTheme-aware
May 6 2026

/approach page + brand graphic language

A marketing page explaining the philosophy: how the AI is bounded, what the readiness score actually measures, why the constraints earn their trade-offs. The page also introduced a section-illustration system — the start of a graphic language for the brand.

/approachMarketing pagesGraphic language
May 6 2026

/devjourney restructured with origin timeline

The story page reframed as the development journey it actually is. Eight ChatGPT-era moments from January through April 2026, a stats strip showing the 100+ conversations and 40+ Strava screenshots that preceded the codebase, and the build audit trail that follows.

/devjourneyOrigin timelineProject audit
May 6 2026

Design system documentation site

A standalone /design-system docs viewer with typography specimens, brand mark variants, illustration library, and a global code-snippet toggle. The token system finally has its own home rather than living implicitly in landing.css.

Design systemDocs site/design-system
May 6 2026

Row-level security hardening

Enabled RLS on 8 Supabase tables that were missing it, and locked the search_path on the update_updated_at function. Hardening the substrate before multi-user lands.

RLSSecurityMulti-user prep
May 6 2026

StoryBrand homepage rewrite

Homepage copy reworked along StoryBrand lines — sharper hero, problem/solution framing front-and-centre, less feature list and more story. Hero headline tightened in a follow-up.

Homepage copyStoryBrand
May 8 2026

Weather wired into pre-run guidance

Pre-run guidance now reads tomorrow's forecast from Open-Meteo — temperature, humidity, wind, precip likelihood — and adjusts the prescription. Hot day? HR cap. Headwind on the long run? Target adjusted. Another input the daily read can lean on.

WeatherOpen-MeteoAdaptive guidance
May 8 2026

Garmin sync running on real data

After capturing the OAuth token via the local garth flow, the daily 04:30 UTC cron now hits the real Garmin endpoint. wellness_log fills with actual sleep / HRV / RHR rows tagged source='garmin'. The recovery side of Race Readiness finally has live data — not mock fixtures.

Garmin real syncOAuth tokenLive wellness
May 8 2026

AI brief — Plan/Today/Log rework

The largest single update since v2 readiness landed. Skip/miss handling on the plan, weekly summary v2, restructured cards across the three working pages. The dashboard's working surfaces tightened around how training actually unfolds — including the missed sessions.

AI briefPlan reworkSkip/miss handlingWeekly summary v2
Decisions & pivots

The calls that shaped it.

Eight load-bearing decisions — what the call was, what alternative it ruled out, and why it earned the trade-off.

Coming next

In order of priority.

Everything below is confirmed work — not a wishlist. Order reflects what unblocks the most for runners in training right now.

Soon

Login and access control

The dashboard is currently single-user. Auth via Supabase — email + magic link — gives each runner their own isolated view. Required before the alpha cohort can expand beyond one.

Soon

Waitlist confirmation emails

The waitlist form is live but the submission goes nowhere. Next up: a transactional confirmation via Resend, so runners on the list know they're in the queue.

Soon

HealthKit bridge for wellness data

Garmin Connect already writes sleep, HRV, and RHR to Apple Health. An iOS Shortcut reads those values daily and posts to a /api/wellness/ingest endpoint — no Garmin developer account needed, no OAuth dance. The unofficial garth bridge is API-restricted and the official Garmin developer program is suspended, so this is the active path.

Later

Official Garmin Health API

Garmin's developer program is suspended as of May 2026. When it reopens, the official OAuth + webhook path can replace the HealthKit bridge with a more direct integration. Application already filed — no action needed until Garmin announces the program is open again.

Later

Post-run feel capture

A two-question prompt after every run syncs — perceived effort (1–10) and how it felt relative to expectation. A harder-than-expected easy run is a better fatigue signal than any HRV number. The readiness score will weight this accordingly.

Later

Strava auto-sync

Runs currently sync on a manual tap. Strava's webhook API means a finished run appears in the dashboard automatically — the post-run assessment fires without touching the phone.

Later

Race-day pacing and fuelling plan

Target splits at every 5 km mark, a gel and drink schedule, and a weather check for race week. The readiness score tells you if you're fit enough; the pacing plan tells you how to use it.