startSprint locks committedPoints=null when items are unestimated at start — immutable baseline never reflects later estimates (breaks burndown/velocity/report)
Found while investigating MOTIR-1285. An active sprint can end up with committedPoints = null even though it now holds hundreds of points of work — which silently breaks the burndown (degrades to a unitless issue-count series), the velocity "Committed" bar, and the sprint report's committed total.
Evidence (live tenant — Sprint 31)
committedIssueCount = 38,committedPoints = null, but the sprint currently holds 100 items / 324 points (303 done, 21 remaining).
Root cause (confirmed in code)
sprintsService.startSprint snapshots the immutable baseline from the sprint's items at the instant of activation:
committedIssueCount = workItemRepository.countSprintIssues(...)committedPoints = workItemRepository.sumStoryPointsForSprint(...)
sumStoryPointsForSprint returns Prisma's _sum.storyPoints, which is null when every item in the sprint has a null storyPoints. So a sprint started with items that are not yet estimated locks committedPoints = null (here: 38 unestimated items at start). The baseline is then immutable — committedPoints is written ONLY in startSprint (no recompute/backfill anywhere), so estimating the work later (or moving more items in) never updates it.
Impact
- Burndown: with
committedPointsnull/0 the series used to degrade to issue-count with no anchor → the chart disagreed with the numeric remaining (the MOTIR-1285 symptom). (MOTIR-1285's PR now makes the burndown robust to this, but the bad baseline remains.) - Velocity chart: this sprint's "Committed" bar reads null/0 → under-reports.
- Sprint report: committed points read null/0.
Options (recommend evaluating)
- Defensive: snapshot
?? 0so a started sprint is never null (committedPoints = (await sumStoryPointsForSprint(...)) ?? 0). Removes the null, but still 0 if unestimated at start. - Product decision — post-start estimation semantics. Jira treats estimates added after commitment as scope changes. If we adopt that,
committedPointsstaying at the start value is "correct", BUT the burndown's scope-change detection keys only onsprintIddiffs, not onstoryPointsedits — so estimating an already-in-sprint item changes remaining work yet is NOT captured as a delta. Closing that gap (treat astoryPointsrevision as a remaining delta) would make the line honest. - Backfill the existing Sprint 31
committedPointsas a one-off data fix (band-aid).
The right fix is likely (1) + a decision on (2). Surfacing for planning rather than silently picking one.
Where
lib/services/sprintsService.ts—startSprint(the snapshot).lib/repositories/workItemRepository.ts—sumStoryPointsForSprint(returns null for all-null_sum).- Consumers:
reportsService.getBurndownSeries/getVelocity, the sprint report.