A Claude Code Story · Mar–May 2026

20 Years of Unfinished Data

Rebuilding a 2003 college running log with Claude Code

The data was a diary, not a database

Hand-written HTML, 2003–2007. Verbatim entries below, original styling preserved.

Wednesday, May 9th
Workout:run Minutes:36:56 Miles:4.5+.5
Comments: ran with Coach H through the Fens, just wandering. Legs a little tight from yesterday; I didn't have time to stretch and I'm not running in the mornings.
Extras: .5 mile over hurdles
Monday, July 4th
Workout:run Minutes:65:15 Miles:~8
Comments: ran over to Government Center via Sci Museum to see the 4th of July festivities, but I guess i got there too early, nothing was going on yet. built in some strides at the end
Extras: situps, pushups
Sunday, September 19th
Workout:long run Minutes:1:45:52 Miles:12.5+
Comments: drove to the Blue Hills Reservation. Hillier than Boston (in a good way) and slightly loose footing in parts, but it was nice. Cool today
Extras:  

4.5+.5 = 4.5 main + 0.5 over hurdles  ·  ~8 = "about eight"  ·  1:45:52 = a value the parser would mis-read 20 years later

"I've wanted to visualize this since 2012."

Why it never happened

  • Data was too informal for a parser
  • Edge cases everywhere — relay splits, multi-event meets, freetext notes
  • Every weekend project hit the same wall

Why now

  • Claude reads the mess and infers intent
  • Not just parsing — interpreting
  • Loop-closer for the hundred tiny seams
Mar 4 · The Spark

Raw HTML → live dashboard in 90 minutes

Beautiful Soup parser, Plotly charts, Netlify deploy. One session.

What Claude figured out without being told

  • Track meets as multi-event days
  • Relay splits attributed to the right person
  • Race detection from freetext ("XC", "Inv.", "Champ.")
  • Trendline placement decisions

What went wrong (of course)

The zoom coupling bug

Five date-axis charts, all listening to plotly_relayout. When chart A relayouts, it tells B, C, D, E to relayout — which fire their own relayout events — which fire A's again.

// the fix: an early-return guard
chart.on('plotly_relayout', (e) => {
  if (e['xaxis.autorange']) return;        // ignore spurious
  if (syncing) return;                     // ignore our own echo
  syncing = true;
  syncOthers(e);
  syncing = false;
});
Live · v0

"Strava Before Strava"

90 minutes after starting · deployed to ducktape.netlify.app

Original v0 running log dashboard
Mar 11

GitHub from the phone

First mobile Claude Code session. PR #1 opened, reviewed, merged — all from a screen smaller than a paperback.

The novel thing wasn't that the code got written on a phone. It was that the code review got done on a phone, by the same person who wrote it, while waiting somewhere.
Mar 18

Wiring the seams nobody wants to wire

Two hooks in ~/.claude/settings.json:

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Write|Edit",
      "hooks": [{ "type": "command",
        "command": "case \"$file\" in *Running\\ Log/index.html)
                      touch ~/.claude/running-log-deploy-pending ;; esac" }]
    }],
    "Stop": [{
      "hooks": [{ "type": "command",
        "command": "[ -f ~/.claude/running-log-deploy-pending ] &&
                    netlify deploy --prod --dir='Running Log' &&
                    rm ~/.claude/running-log-deploy-pending" }]
    }]
  }
}

Any edit to index.html ships to Netlify when the turn ends. No CI to set up, no remembering.

May · Design Thinking

The redesign trigger

"I want to import a design from Claude Design and update my running log dashboard to match that design." — me, kicking it off

One sentence. Two tools. A cross-system handoff begins.

Claude Design output

Dark glass · Geist fonts · workout-type palette · React tweaks panel

▶
Claude Design session recording
Set data-recording-src on <html> at the top of the file
to your recording's path or URL.

The handoff is the spec

Claude Design didn't just produce a mockup. It produced a specification document: design tokens, component decisions, a README — ready to feed back into Claude Code as instructions.

--bg-base: #0d1117;
--bg-surface: #161b22;
--bg-elevated: #1c2230;
--text-primary: #e6edf3;
--accent: #58a6ff;

--c-easy: #2dd4bf;
--c-tempo: #f59e0b;
--c-long: #a78bfa;
--c-race: #f87171;
May 4 · The Big Redesign

3,749 insertions in one session

visualize_log.py rewritten end-to-end against the Claude Design spec. Six sections, SVG heatmap, race classifier, PR cards.

The lesson that cost the most

visualize_log.py is the source of truth. index.html is a generated artifact. Early fixes were applied directly to index.html — and silently wiped on the next regeneration.

→ Name your build system at the start of the session.

The data was fine. The parser wasn't.

Long-run times were written as H:MM:SS — perfectly readable. The parser only handled MM:SS, so it silently dropped the hours digit.

def parse_minutes(s):
    # "7:42"  -> 7.70 minutes  ✓
    # "1:45:52" -> ??? this never happened, right?
    h, m, sec = ...   # nope. m, sec = ... only.
    return m + sec/60
InputParsed asPace
1:45:521.75 min0.13 /micorrupted
1:45:52 ✓105.87 min7:33 /mirecovered

The original log was fine. The bug only existed once we started treating notes-to-self as data.

The long tail of theming

"The light mode colors are very low contrast and hard to see. In Claude Design, the colors were more vibrant."
"There seems to be one lingering visual issue in light mode. The zoom slider on training volume chart is showing in dark mode."

Every hardcoded hex baked into a Python f-string is a future bug. The contract: every color a var(--…) — state it up front, save yourself three round trips.

qa.py

Claude wrote tests so the bugs couldn't come back

"If I want to make a QA agent to self-check most of the issues that I brought up, can you help me write a plan?"
CheckRescued bug
check_detail_panel_no_hex Hardcoded hex colors that broke in light mode
check_easy_pace_no_fill fill:"tozeroy" on a reversed y-axis painted a blue cap upward
check_race_count_consistency "I thought there were only 99 races." (the donut said 100)

What it actually cost

SessionTimePain (1–5)
Mar 4 — The spark~90 min2
Mar 11 — Mobile + zoom sync~45 min1
Mar 18 — Auto-deploy hook~30 min2
May 4 — Big redesign~3 hrs2
May 12 — Polish + QA~3 hrs2

Without AI: a project I'd been "going to do" for 14 years. With AI: a few weekends of fun.

Today

ducktape.netlify.app

2003–2007 college log · light / dark / system · click-to-detail · QA-tested

College Running Log dashboard
Meta

And this deck? Under an hour.

What Claude did

  • Mined session JSONLs for quotes
  • Wrote 19 slides of Reveal.js
  • Screenshotted the v0 + final dashboards via Chrome headless
  • Embedded the Claude Design recording
  • Committed, merged, deployed to Netlify

What I did

  • Approved a plan
  • Answered three multiple-choice questions
  • Caught one bad headline ("hid for 20 years")
  • Said "redeploy"

The talk about Claude Code is, of course, a Claude Code artifact.

Legacy data →
modern interactive tool
in a day.

10 minutes of Claude Code fixed what 10 years of Python procrastination couldn't.