Skip to content

feat(analytics): burn-down chart per milestone (closes #41)#48

Merged
Musiker15 merged 1 commit into
mainfrom
feat/burndown
May 25, 2026
Merged

feat(analytics): burn-down chart per milestone (closes #41)#48
Musiker15 merged 1 commit into
mainfrom
feat/burndown

Conversation

@Musiker15
Copy link
Copy Markdown
Member

Summary

Phase-7-deferred Burn-Down chart. Now that Milestones (#39 + #47) ship with start / end dates, we can plot scope-remaining vs. ideal pace per milestone.

Landed simpler than the issue proposed:

  • No new schema — the Column.isDone flag was the issue's suggested ADR 0010. Not needed for v1: the existing "last column = Done" convention is already documented and used by every other metric in analytics-view.tsx. Per-board Done flag remains a worthwhile follow-up that benefits all metrics, not just burn-down.
  • No new server endpointGET /api/milestones/[id]/burndown was suggested. Not needed: the client already has cards (with milestoneId), the milestone list (with dates), and the activity log. Computing client-side keeps the path E2EE-clean and avoids an extra round-trip per milestone switch.

What changed

  • computeBurndown() — pure helper, scope held constant at "cards currently in milestone" (textbook burn-down assumption). Done = last CARD_MOVED into a done column; conservative fallback for cards currently in done with no move event.
  • BurnDownChart — pure SVG line chart matching the existing analytics style. Ideal (dashed), actual (solid + accent color), today marker, scope/0 baseline labels.
  • New section in AnalyticsView between Throughput and Cycle-time. Milestone selector top-right. Empty states for "no milestones" + "no dated milestones".
  • BoardClient passes the milestones state through.

Test plan

  • pnpm typecheck clean
  • pnpm lint clean
  • pnpm test — 107/107
  • CI green
  • Manual after merge: create milestone with start = today-3d, end = today+10d, assign 5 cards, drag 2 to "Done". Open Analytics tab → see scope=5, actual line dips to 3 at today, ideal slopes from 5 to 0.

Follow-up (out of scope here)

  • Per-board Column.isDone flag — flexibility for boards that have "Done → Archived" or "Released" columns past "Done". Benefits every analytics metric, not just burn-down.

Closes #41.

🤖 Generated with Claude Code

Closes #41. Phase-7-deferred item — landed simpler than the issue
proposed: no new schema, no new server endpoint, no ADR for a
"Done"-column flag. The existing `last column = Done` heuristic
(documented in analytics-view.tsx since Phase 7) is sufficient for
v1, and all the data the chart needs is already on the client
(cards via the board fetch, milestones via the #39 endpoint, activity
events via the existing `/api/boards/[id]/activity` poll).

### What's in
- `computeBurndown()` — pure helper that builds `BurnDownPoint[]`
  for a milestone. Scope held constant at the count of cards
  currently in the milestone (textbook burn-down assumption).
  Done = most recent `CARD_MOVED` event into a done column;
  cards currently in done with no recorded move fall back to
  their first activity timestamp or milestone start.
- `BurnDownChart` — pure SVG line chart matching the rest of the
  analytics-view's style. Two lines (ideal dashed + actual solid),
  vertical "today" marker, scope/0 baseline labels.
- Burn-Down section in `AnalyticsView`, placed after Throughput.
  Milestone selector top-right when ≥1 dated milestone exists.
  Empty states for "no milestones" and "no dated milestones".

### Wiring
- `AnalyticsView` accepts `milestones: MilestoneView[]`.
- `BoardClient` passes the `milestones` state through to the view.
- `pickedMilestoneId` defaults to null and derives the effective
  selection during render (no `useEffect` setState — avoids the
  react-hooks/set-state-in-effect lint rule).

### Why not a server endpoint
The issue suggested `GET /api/milestones/[id]/burndown`. We already
ship enough data to the client for cycle / lead / CFD analytics; the
burn-down is the same shape. Pushing the aggregation server-side
would have meant either decrypting card content (impossible) or
running an extra round-trip per milestone switch (slow). Same trade-
off the rest of `analytics-view.tsx` makes — keep it in this view.

### Why no Done-column flag yet
The existing convention (sort columns by position, last one wins)
is good enough for v1 — boards seeded by us start with
"Backlog / In progress / Done", and that's also how `doneColumnIds`
in `computeStats` already works. A per-board "this column is Done"
toggle remains a worthwhile follow-up but is independent of burn-
down — every other analytics metric would benefit from it too.

### Test plan
- [x] `pnpm typecheck` clean
- [x] `pnpm lint` clean
- [x] `pnpm test` — 107/107
- [ ] CI green
- [ ] Manual: create milestone with start = today-3d, end = today+10d,
      assign 5 cards, mark 2 done. Open Analytics → see scope=5,
      actual line dips to 3 at today, ideal slopes from 5 to 0.

Closes #41.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Musiker15 <info@musiker15.de>
@Musiker15 Musiker15 merged commit 70bad39 into main May 25, 2026
8 checks passed
@Musiker15 Musiker15 deleted the feat/burndown branch May 25, 2026 14:14
Musiker15 added a commit that referenced this pull request May 29, 2026
Fills the missing *board* templates from §5.7 (only card templates existed).

- src/lib/templates/board-templates.ts: a built-in set (Basic Kanban,
  Sprint, Bug tracker, OKR, Content pipeline, Hiring pipeline). Each is
  expressed as an ExportedBoardV1 (columns + optional labels, no cards) so
  it can be materialised through the existing importBoard() pipeline — E2EE
  under a fresh BoardKey, no new schema or server route. The "done" column
  gets isDone set for Analytics; the bug tracker ships severity labels.
- workspace-client: a "From template" creator (template <select> + name +
  Create) next to the create/import forms; builds the template board and
  navigates to it.
- Tests: tests/unit/board-templates.test.ts (every template builds a valid
  v1 export with exactly one done column; bug labels present).

Docs: CLAUDE.md §5.7 + gap list flipped to ✅, CHANGELOG. Typecheck, ESLint
and the suite (214 tests, +4) all pass. This completes Phase C.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PR 2: Burn-Down Chart in Analytics View (uses Milestones from #39)

1 participant