MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-1288

startSprint locks committedPoints=null when items are unestimated at start — immutable baseline never reflects later estimates (breaks burndown/velocity/report)

Cancelled
Description

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 immutablecommittedPoints 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 committedPoints null/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)

  1. Defensive: snapshot ?? 0 so a started sprint is never null (committedPoints = (await sumStoryPointsForSprint(...)) ?? 0). Removes the null, but still 0 if unestimated at start.
  2. Product decision — post-start estimation semantics. Jira treats estimates added after commitment as scope changes. If we adopt that, committedPoints staying at the start value is "correct", BUT the burndown's scope-change detection keys only on sprintId diffs, not on storyPoints edits — so estimating an already-in-sprint item changes remaining work yet is NOT captured as a delta. Closing that gap (treat a storyPoints revision as a remaining delta) would make the line honest.
  3. Backfill the existing Sprint 31 committedPoints as 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.tsstartSprint (the snapshot).
  • lib/repositories/workItemRepository.tssumStoryPointsForSprint (returns null for all-null _sum).
  • Consumers: reportsService.getBurndownSeries / getVelocity, the sprint report.