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.
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.
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.
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.
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:
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");
});
// + 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.
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.