4.3.2 Schema + migration — `work_item.storyPoints` + `project` estimation config (`estimationStatistic` / `pointScale` / `customScaleValues`) + the two enums, drift-free
Estimate: 28m
Add the persistence for story points + the project estimation config as ONE Prisma migration, modelled so prisma migrate dev is drift-free (a second run reports "No difference detected").
WorkItem addition: storyPoints Decimal? @db.Decimal(6, 2) — the agile estimate, nullable (null = unestimated), camelCase (NO @map, matching work_item's existing camelCase columns — the same within-table-consistency call the schema records for sprintId / backlogRank; decision-ladder rung 2). Decimal, not Float — story points allow 0.5 increments (Jira-faithful) AND roll up via SUM; Decimal avoids floating-point drift in the roll-up sums (a real-product correctness concern), at the small fixed precision (6, 2). SEPARATE from the existing estimateMinutes (TIME) — both columns coexist; the project statistic config picks which one is THE planning estimate. Index: add storyPoints to a covering index ONLY if the roll-up aggregates need it (the sprint roll-up filters on the existing (projectId, sprintId, backlogRank) composite; the epic roll-up walks parentId — measure, do not add a speculative index).
Project additions (the estimation config — PROJECT-scoped per the module header):
estimationStatistic EstimationStatistic @default(story_points)— which value is THE planning estimate the surfaces display + the roll-ups sum.pointScale PointScale @default(fibonacci)— the suggested-deck the estimate picker offers.customScaleValues Float[] @default([])— the project-defined deck values, used ONLY whenpointScale = custom(these are UI SUGGESTIONS — summation uses theDecimalstoryPoints, soFloat[]for the suggestion list is fine and avoids a Decimal-array column). All camelCase, no@map, matchingproject's existingworkflowPolicyMode/accessLevelcolumns. They backfill to the defaults on migration (every existing project becomes Story Points + Fibonacci — the agile default, no lock-out).
Enums:
EstimationStatistic(@@map("estimation_statistic")):story_points,time_estimate,issue_count— the three Jira estimation statistics. All values exist now so later stories add no enum ALTER.PointScale(@@map("point_scale")):fibonacci,linear,custom.
No new FK (the columns are scalars / a scalar list on existing tables), so the FK-as-@relation rule is n/a here — but the migration must STILL be drift-free (no spurious change on a second migrate dev), and no other model changes.
Acceptance criteria
schema.prismagainswork_item.storyPoints Decimal? @db.Decimal(6, 2)(camelCase, nullable, separate fromestimateMinutes) and theprojectcolumnsestimationStatistic/pointScale/customScaleValueswith the defaults above, plus theEstimationStatistic+PointScaleenums (@@map-ed).- One migration
add_story_points_and_estimation_configcreates the column, the project columns, and the two enums; existing projects backfill tostory_points+fibonacci+[].pnpm prisma migrate devapplies cleanly and a SECOND run reports "No difference detected" (no drift). pnpm prisma generate+pnpm typecheck+pnpm buildpass; no other model changes; no speculative index (add one only if a roll-up query measurably needs it).
Context refs
prisma/schema.prismamodel WorkItem(theestimateMinutes/sprintId/backlogRankcolumns + the camelCase-no-@mapconvention comment) andmodel Project(workflowPolicyMode/accessLevel— the project-config-column pattern to mirror)motir-core/CLAUDE.md(the migration drift rule — even without an FK, a secondmigrate devmust report "No difference detected") + thebug-attachment-fk-migration-driftprecedent- Jira estimation statistics (Story Points / Original Time Estimate / Issue Count) + planning-poker decks (Fibonacci / linear) as the mirror for the enums