red → green → refactor, made visible

Your tests have a heartbeat.

TDD Heartbeat traces the rhythm of a coding session as a living EKG — amplitude is failing tests, color is what you did — then scores how effectively you practiced TDD by catching the anti-patterns.

Pure TS core100% test-drivenIt built itself
live trace · session_healthy.jsonl

Watch a session beat

Three real sessions. Hover the trace to inspect any beat; the scorecard updates live.

What the trace shows

Every transition between two test runs is classified. Three of them are healthy.

RED

You wrote a new failing test and drove the suite red. The spike that starts every cycle.

GREEN

You made the failing test pass with a modest amount of code. Back to baseline.

REFACTOR

You improved production code while the suite stayed green, with no new tests. Textbook.

The smells it catches

The differentiator: a mechanical answer to “did you actually do TDD?”

TEST_AFTER

A new test passed the instant it was added — it was never seen failing, so it wasn’t written test-first.

OVER_RED

You piled on more failing tests while already red, instead of getting back to green first.

REFACTOR_UNDER_RED

You changed production code while the suite was red, with no new test to justify it.

BIG_LEAP

You reached green, but wrote far more code than one small step needs — you leapt instead of stepping.

Run it on your own code

A tiny CLI. Record a live session, then print the scorecard.

# install
npm i && npm run build

# record a live TDD session (re-runs on every save)
heartbeat watch -- vitest run

# print the scorecard for a recorded session
heartbeat report .heartbeat/session-*.jsonl

# or replay any repo's git history, commit by commit
heartbeat replay --from v0.1.0 -- npm test

It built itself.

This project was written test-first, with every commit tagged red:, green:, or refactor:. The “This repo” trace above is its own git history, replayed by the very tool you’re looking at — each commit’s suite run and classified. The big-leap markers are honest: whole modules landed in single green commits.

Read the history ↗

We pointed it at ourselves — and got better

The tool didn't just measure us, it changed how we worked. Here's the real loop: replay → read the smell → fix it in small steps → replay again.

Before · default commit style
adherence
grade
smells
→
After · same work, small steps
adherence
grade
smells
1

Replay our own history — the tool flags the problem

$ heartbeat replay -- npm test
  …27 commits classified…

  Adherence       65%  (grade D)
  smells          9 total
            ⚠ BIG_LEAP ×9  passed, but wrote far too much in one step

Nine BIG_LEAP markers. The diagnosis is mechanical: our green: commits each landed a whole module at once — classify.ts arrived in a single commit of +64 lines. That's a leap, not a step.

2

Rebuild every leap as small red → green steps

We rebuilt the whole codebase commit-by-commit. Instead of one 64-line green for classify.ts, each rule became its own failing test, then one line to pass it. Here's one real pair from the rebuild:

red: add the failing test
test("piling failing tests while red → OVER_RED", () => {
  const prev = beat({ passed: 3, failed: 1, ... });
  const curr = beat({ passed: 3, failed: 3, ... });
  expect(classifyStep(prev, curr).kind)
    .toBe("OVER_RED");
});
green: one line to pass it
// + src/classify.ts
else if (s0 === "RED" && s1 === "RED"
         && dTestCount > 0)
  kind = "OVER_RED";

Repeated across the whole project — every module introduced as small red→green steps, the bulky I/O added as honest refactors.

3

Replay again — the heartbeat proves it

$ heartbeat replay -- npm test
  …61 commits classified…

  Adherence       100%  (grade A)
  healthy         RED 23   GREEN 23   REFACTOR 14
  smells          none — clean rhythm ✓

A steady pulse of small spikes snapping back to baseline — exactly what healthy TDD is supposed to look like.