Sprints & Modules

Last updated on 2026-09-07

The kit includes four organizational features beyond the core task board: sprint cycles, modules, goals, and wiki pages. Each can be toggled on or off per project via the features flags on the projects table (feat_cycles, feat_modules, feat_pages).

Sprint Cycles

Route: /projects/[id]/cycles (list) and /projects/[id]/cycles/[cycleId] (detail)

Sprint cycles represent time-boxed iterations (sprints). Tasks are assigned to cycles, and velocity is calculated on completion.

Cycle Lifecycle

  1. Create -- a new cycle is created with a name, optional description, start date, and end date. Server-side validation ensures end date is after start date. The initial status is Upcoming with velocity, planned_points, and completed_points all set to 0.
  2. Activate -- update the cycle status to Active via updateCycle. Only one cycle per project should be active at a time.
  3. Assign tasks -- add tasks to the cycle via addTaskToCycle, which writes to the cycle_tasks junction table and also sets the task's cycle_id column.
  4. Track progress -- the active cycle card shows a ProgressRing with completion percentage, total points, completed points, task count, and days remaining.
  5. Complete -- completeCycle calculates velocity by iterating all cycle tasks, summing the estimate field of tasks with status Done, and writing completed_points, planned_points, and velocity to the cycle row.

Cycles Page Layout

The cycles page is divided into three sections:

  • Active Cycle -- a highlighted card with green border showing the current sprint's name, description, progress ring, date range, points progress, task count, days remaining, and team member avatars
  • Upcoming Cycles -- a grid of cards for cycles with status Upcoming, each showing name, date range, task count, and planned points
  • Completed Cycles -- a table with columns for name, date range, planned points, completed points, and velocity

Cycle Statuses

Status Description Style
Active Currently running sprint Green badge, highlighted card
Upcoming Scheduled for the future Blue badge
Completed Finished with velocity calculated Muted badge

Velocity Calculation

When a cycle is completed via completeCycle:

// completeCycle in lib/actions/cycles.ts
const completedPoints = cycleTasks.reduce((sum, ct) => {
  const task = ct.tasks
  if (task && task.status === "Done") {
    return sum + (typeof task.estimate === "number" ? task.estimate : 0)
  }
  return sum
}, 0)

const totalPoints = cycleTasks.reduce((sum, ct) => {
  const task = ct.tasks
  if (task && typeof task.estimate === "number") {
    return sum + task.estimate
  }
  return sum
}, 0)

// Update cycle with final metrics
await supabase.from("cycles").update({
  status: "Completed",
  velocity: completedPoints,
  planned_points: totalPoints,
  completed_points: completedPoints,
})

Cycle Data Sources

Data Table Relationship
Cycles cycles Filtered by project_id
Cycle tasks cycle_tasks Junction table linking cycles to tasks
Task estimates tasks.estimate Numeric field for story points

Modules

Route: /projects/[id]/modules (list) and /projects/[id]/modules/[moduleId] (detail)

Modules group related tasks by feature area. Unlike cycles (time-based), modules are scope-based -- they represent a feature, epic, or functional area.

Module Properties

Property Type Description
Name text Module name (e.g., "Authentication", "Billing")
Description text Module description
Status enum Planned, Active, or Completed
Lead profile Team member responsible for the module
Start/End Date timestamptz Optional date range
Tasks junction Linked via module_tasks table

Module Page Layout

The modules list page shows each module as a card with:

  • Module name and status badge
  • Description text
  • Lead avatar and name
  • Date range (if set)
  • Task count with status breakdown
  • Progress bar showing completion percentage
  • Collapsible task list grouped by status

Module Operations

Operation Server Action Description
List modules getModules(projectId) All modules for a project with task IDs
Get module getModule(id) Single module with task IDs
Create module createModule(data) Insert with default status Planned
Update module updateModule(id, data) Update name, description, lead, dates, status
Delete module deleteModule(id) Delete module
Add task addTaskToModule(moduleId, taskId) Write to module_tasks and set task's module_id
Remove task removeTaskFromModule(moduleId, taskId) Delete from module_tasks and null task's module_id

Goals (OKRs)

Route: /goals

Goals follow the OKR (Objectives and Key Results) pattern. Each goal has a title, owner, due date, status, progress percentage, and a set of key results.

Goal Properties

Property Type Description
Title text The objective (e.g., "Increase test coverage to 90%")
Description text Additional context
Owner profile Team member responsible
Status enum On Track, At Risk, Off Track, Completed
Progress int 0-100, auto-calculated from key results
Due Date timestamptz Target completion date
Key Results relation Linked via key_results table

Key Results

Each goal can have multiple key results, each with:

Property Type Description
Title text The measurable result (e.g., "Unit test coverage")
Target numeric Target value (e.g., 90)
Current numeric Current value (e.g., 72)
Unit text Unit of measurement (e.g., "%", "users", "days")

Goal Progress Auto-Calculation

When a key result is created, updated, or deleted, the recalculateGoalProgress function automatically recalculates the parent goal's progress:

// recalculateGoalProgress in lib/actions/goals.ts
const totalProgress = keyResults.reduce((sum, kr) => {
  const current = typeof kr.current === "number" ? kr.current : 0
  const target = typeof kr.target === "number" ? kr.target : 1
  return sum + (target > 0 ? (current / target) * 100 : 0)
}, 0)
progress = Math.round(totalProgress / keyResults.length)

Goals Page Layout

The goals page includes:

  • Status filter -- filter by All, On Track, At Risk, Off Track, or Completed
  • Create goal dialog -- form with name, description, target date, and owner select
  • Goal cards -- each goal rendered as a GoalCard component showing title, owner avatar, status badge, progress bar, due date, and key results

Goal Operations

Operation Server Action Description
List goals getGoals() All goals with key results, ordered by due date
Get goal getGoal(id) Single goal with key results
Create goal createGoal(data) Insert with default status On Track, progress 0
Update goal updateGoal(id, data) Update title, description, owner, status, progress
Delete goal deleteGoal(id) Delete goal and cascade-delete key results
Create key result createKeyResult(goalId, data) Insert KR and recalculate goal progress
Update key result updateKeyResult(id, data) Update KR and recalculate goal progress
Delete key result deleteKeyResult(id) Delete KR and recalculate goal progress

Wiki Pages

Route: /projects/[id]/pages (list) and /projects/[id]/pages/[pageId] (detail)

Wiki pages provide per-project documentation. Pages support a tree hierarchy via the parent_id column.

Page Properties

Property Type Description
Title text Page title
Content text Page body content
Author profile Creator, set from authenticated user
Parent page Optional parent page for nesting
Project project The project this page belongs to

Pages List Layout

The pages list renders as a tree structure:

  • Tree navigation -- pages are organized in a collapsible tree based on parent_id relationships
  • Page entries -- each entry shows a file icon, title, author avatar, and relative timestamp
  • Create page dialog -- form with title input
  • Empty state -- prompt to create the first page when none exist

Page Operations

Operation Server Action Description
List pages getPages(projectId) All pages for a project, ordered by created_at desc
Get page getPage(id) Single page
Create page createPage(data) Insert with authenticated user as author
Update page updatePage(id, data) Update title and content
Delete page deletePage(id) Delete page

Next Steps