Every pbl-* skill in this repo writes a file when it fires. /pbl-add touches product-backlog/.history/2026-06-19_bulk-paste_created. /pbl-done touches …_done. The files are empty — the filename is the whole record, and there are 228 of them covering 63 items across 81 days.
Nobody designed that as a dataset. It is exhaust. But it is a complete, timestamped, append-only event log of every decision this product’s backlog ever made, and it answers a question a snapshot of the backlog cannot: not what is in it, but what happened to it.
This post is that reconstruction. Every chart below is rendered from those filenames and nothing else — no estimates, no story points, no velocity. Drag, hover, and zoom them.
The lifecycle the filenames encode
An item’s state is whatever its most recent event says. Seven event types, one terminal state that isn’t Done.
stateDiagram-v2
[*] --> Idea: created
Idea --> Refining: refined
Refining --> Ready: confirmed
Ready --> InProgress: started
InProgress --> Done: done
Idea --> Discarded: removed
Refining --> Discarded: removed
Discarded --> Idea: created (again)
Idea --> Idea: split
Done --> [*]
InProgress: In Progress
Ready: Ready (Confirmation.md written)
Idea: Idea (Card.md only)
Two transitions in that diagram matter more than the happy path. removed is how an item leaves without being built — it is the only way the backlog shrinks by choice. And the edge back out of Discarded fired exactly once, which is a story further down.
The Cumulative Flow Diagram
Band height is the number of items sitting in that state on that day. The dark floor is everything shipped; everything above it is work the product still owes itself.
View as table
The first thing to read is the top edge, because it is the total size of the backlog and it goes the wrong way.
On day one, /pbl-kickoff turned Project-Idea.md into ten Cards in a single sitting — Create & Publish Course, Enroll, Purchase, Quiz Lessons, Certificate, and the rest. Eighty-one days later, thirty-nine of those items and their descendants are Done. And the backlog is larger than it was after kickoff: fifteen open items against the original ten.
That is not a failure to keep up. Building the thing is what generated the ideas — an Attachment is not something you think of before you have a Lesson to hang it on; Locale-Aware Date Formatting is not something you think of before the app has a second locale. The Idea band is fed by the same work that drains it, and it is fed slightly faster. A backlog that ran dry would mean the product had stopped teaching us anything.
The second thing to read is what is missing: the middle bands are almost never there. Refining, Ready and In Progress are hairline where they exist at all, and on 40 of the 81 days they are collectively empty. The rule in product-backlog/CLAUDE.md is do not refine more than 2 items at a time, and the log does not merely trend that way — across all eighty-one days the three middle bands never once hold more than two items between them. Refinement happens against the item about to be built, not against the backlog at large, so the inventory of half-analysed work stays near zero. Everything that was ever going to be waste as a Card stayed a cheap Card.
Arrivals and departures
The same events, aggregated by week: what entered, what shipped, what was thrown away.
View as table
Three weeks in this chart are not about steady throughput at all.
The week of 8 June is empty — one of only three weeks in eighty-one days with no events at all, and the only one that falls in the middle of an item rather than between them. Quiz Lessons was started on 2 June and not done until 17 June: fifteen days with the In Progress band pinned at exactly one. Quiz was the first item large enough to need its own vocabulary — Question entered CONTEXT.md — and it produced two ADRs on identity alone, ADR-0006 pinning UUIDs for QuizAttempt and ADR-0007 on stable Question Option identifiers. It is the only stretch in the log where one item held the board.
Week of 6 July is an injection, not a delivery. Five items appear in one day — Content Security Policy, Rate-Limit Magic Link, Hide Unpublished Course Pages, Verify Paid Price Matches Course, Server-Verified Quiz Completion — and none of them came from a user. They came from a review pass that also produced PDR-0003 and a same-day fix commit closing a self-enroll payment bypass. This is the backlog doing its least glamorous job: being the place findings go so they stop living in someone’s memory.
Week of 3 August is the tall bar, and it is tall because of a single afternoon covered below.
Two populations, not one distribution
Lead time is created → done in calendar days. The median is 2 days. The 85th percentile is 21. That spread is not noise; it is two different kinds of item wearing the same Card format.
View as table
Nine of the thirty-nine shipped items were created and finished on the same day — lead time zero. Twenty-five of thirty-nine shipped inside a week. Word Match Lesson, Feedback Dashboard, Landing Page and Video Theater Layout each ran Card → refined → confirmed → started → done inside a single date stamp. For those, the Card is not a planning artifact at all. It is a receipt: the Definition of Done attaches to items, not to commits, so writing the Card is how already-decided work acquires an ADR check, a translation check and a blog post. None of it was ever queued.
The long tail is the other population. Retake Quiz (48 days), Lesson Bibliography (37), Sentence-and-Answer (30), Earn Certificate (27) — these sat as Cards for weeks, deliberately. product-backlog/CLAUDE.md says it outright: a Card-only item is intentionally raw; don’t touch it until it reaches the top. Their lead time is almost entirely queue, not work. Sentence-and-Answer is the clearest case: 30 days of lead time, and the work itself began with a refactor — ADR-0020 split the 575-line Lesson LiveView into a LiveComponent per Lesson type before the new type was written, because “custom, differentiated Lesson types are the product’s stated competitive edge” and the monolith was one assign-name collision away from silently corrupting a sibling type.
Reading lead time as how long work takes would be wrong for both populations. For the first it is zero because the Card was written last; for the second it is weeks because the Card was written first and then correctly ignored.
The two ways an item dies
Thirty-nine items were Done. Nine were Discarded, and they did not die the same way.
Set Course Price died of a decision. Created at kickoff on 29 May, it sat as a Card for 43 days. On 11 July it was removed — and on the same day, Explicit Pricing Model was created, refined and confirmed. ADR-0012 explains the swap: the local courses.price integer was doing two jobs at once, printing a fallback amount and signalling free-versus-paid, and the first job was actively harmful when selling across currencies with PPP. A stored €149 rendered before Paddle responds can contradict the localized amount Paddle then shows. So the column was dropped for a pricing enum, and the old Card was not “done” — it was invalidated. The requirement it described stopped being a requirement.
The other eight died of neglect, all at once. On 9 August a single commit archived Evaluation Quizzes, Auto-Complete Video Lessons, Bunny Token Auth, True/False Questions, In-Video Quiz Gates, Server-Verified Quiz Completion, Students Who Are Struggling and Lesson Drop-Off Funnel. Not one of them had ever been refined. The oldest had been sitting for 68 days.
Look at what they have in common. Five are elaborations of video and Quiz — the two Lesson types that existed in June. They were bets on a product whose differentiation came from doing more with video and multiple-choice. What actually shipped between June and August was a different answer to the same question: Word Match, Grouping, Sentence Selection, Sentence-and-Answer, Progressive Answer Hints — new Lesson types rather than deeper video plumbing. The eight Cards were not wrong when they were written. They were made obsolete by the direction the shipped work took, and they stayed on the board for two months because nothing in the process forces a Card to justify its continued existence.
The commit message is careful about this: archived rather than deleted — the Cards are still on disk if any of them comes back. Which one did.
Server Settings Page is the only item that used the edge back out of Discarded. It was created on 27 June and removed the same day — the shortest life in the log. Then on 15 July it was created again. It is still open today, 51 days old and unrefined. An item can be wrong for the moment it was proposed and right later; the log is the only place that fact survives.
What is left, and how long it has been left
View as table
Two items at the top are 80 days old — as old as the backlog itself. Duplicate Course and Lesson Comments were both written at kickoff on 29 May, have never been refined, and survived the 9 August purge that killed items younger than them.
That survival is the interesting part, because it was a judgement, not an oversight. The eight that were archived had been made obsolete by a direction the product took. Duplicate Course and Lesson Comments have not — nothing shipped since May makes either of them a worse idea. They are simply not urgent, and not urgent is a perfectly good reason for a Card to sit still. The failure mode a backlog needs to avoid is not old items; it is old items nobody has re-read.
Everything below them clusters in the second half: nine of the fifteen arrived after 7 July, which is the same pattern the CFD’s top edge shows. The backlog’s centre of gravity keeps moving toward things discovered by shipping — i18n follow-ups (Locale-Aware Date Formatting, Course Language Filter, Invite Email Language), security follow-ups (Sanitize Lesson Markdown HTML, Hide Unpublished Course Pages), and interaction debt from the newest Lesson types (Word Match Drag and Resume, Active Lesson Lifecycle).
What the log is actually good for
None of this required a tool. It required that every lifecycle event leave a file behind, and that the files be committed alongside the code they describe.
The payoff is that the awkward questions have answers. Why is there no price column? — because an item was removed on 11 July and ADR-0012 says what replaced it. Why did nothing ship for two weeks in June? — because one item held the board and produced two ADRs doing it. Did we ever consider gating video with quizzes? — yes, on 27 June, and it was archived on 9 August without ever being refined, along with seven siblings that shared its assumption.
A backlog that is only a list of what is next cannot answer any of those. It has no memory of what it used to want.