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
- 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
Upcomingwithvelocity,planned_points, andcompleted_pointsall set to 0. - Activate -- update the cycle status to
ActiveviaupdateCycle. Only one cycle per project should be active at a time. - Assign tasks -- add tasks to the cycle via
addTaskToCycle, which writes to thecycle_tasksjunction table and also sets the task'scycle_idcolumn. - Track progress -- the active cycle card shows a
ProgressRingwith completion percentage, total points, completed points, task count, and days remaining. - Complete --
completeCyclecalculates velocity by iterating all cycle tasks, summing theestimatefield of tasks with statusDone, and writingcompleted_points,planned_points, andvelocityto 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
GoalCardcomponent 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_idrelationships - 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
- Boards & Tasks -- Kanban board, task views, and task detail
- Analytics & Planning -- analytics dashboards, roadmap, and workload
- Server Actions -- complete Server Action reference