Skip to main content

Tasks

Tasks represent units of work to be tracked and completed within Stoneforge. They support rich metadata, scheduling, assignment, and lifecycle management.

Task Interface

interface Task extends Element {
  readonly type: 'task';
  
  // Content
  title: string;
  descriptionRef?: DocumentId;
  acceptanceCriteria?: string;
  
  // Workflow
  status: TaskStatus;
  priority: Priority;  // 1-5, 1 is highest
  complexity: Complexity;  // 1-5, 1 is simplest
  taskType: TaskTypeValue;
  closeReason?: string;
  
  // Assignment
  assignee?: EntityId;
  owner?: EntityId;
  
  // Scheduling
  deadline?: Timestamp;
  scheduledFor?: Timestamp;
  closedAt?: Timestamp;
  
  // Soft Delete
  deletedAt?: Timestamp;
  deletedBy?: EntityId;
  deleteReason?: string;
  
  // External Integration
  externalRef?: string;
}
title
string
required
Task title, 1-500 characters
descriptionRef
DocumentId
Reference to description Document for detailed information
acceptanceCriteria
string
Definition of done criteria (max 10,000 characters)
status
TaskStatus
required
Current lifecycle state (open, in_progress, blocked, etc.)
priority
Priority
required
Priority scale 1-5, where 1 is highest (default: 3)
complexity
Complexity
required
Complexity scale 1-5, where 1 is simplest (default: 3)
taskType
TaskTypeValue
required
Classification: bug, feature, task, or chore (default: task)

Task Status

Task status represents the current lifecycle state:
const TaskStatus = {
  OPEN: 'open',              // Available for work
  IN_PROGRESS: 'in_progress', // Currently being worked on
  BLOCKED: 'blocked',         // Waiting on dependency
  DEFERRED: 'deferred',       // Deliberately postponed
  BACKLOG: 'backlog',         // Not ready, needs triage
  REVIEW: 'review',           // Awaiting merge/review
  CLOSED: 'closed',           // Completed and merged
  TOMBSTONE: 'tombstone',     // Soft-deleted
} as const;
Open and In Progress are considered “ready for work”:
const READY_STATUSES = ['open', 'in_progress'];

// Check if task is ready
isReadyForWork(task: Task): boolean
BLOCKED status is computed - Never set status: 'blocked' directly. It’s derived from blocking dependencies.

Status Utilities

// Status checks
isReadyForWork(task: Task): boolean
isBlocked(task: Task): boolean
isClosed(task: Task): boolean
isDeleted(task: Task): boolean
isBacklog(task: Task): boolean

// Display names
getStatusDisplayName(status: TaskStatus): string
// Returns: "Open", "In Progress", "Blocked", etc.

Priority

Priority scale from 1-5, where 1 is highest:
const Priority = {
  CRITICAL: 1,  // Production issues, security vulnerabilities
  HIGH: 2,      // Important features, significant bugs
  MEDIUM: 3,    // Standard work items (default)
  LOW: 4,       // Nice-to-have improvements
  MINIMAL: 5,   // Can be done when time permits
} as const;

type Priority = 1 | 2 | 3 | 4 | 5;
DEFAULT_PRIORITY
Priority
default:"3"
Default priority for new tasks

Priority Validation

// Validate priority value
validatePriority(value: unknown): Priority

// Type guard
isValidPriority(value: unknown): value is Priority

// Display name
getPriorityDisplayName(priority: Priority): string
// Returns: "Critical", "High", "Medium", "Low", "Minimal"

Complexity

Complexity scale from 1-5, where 1 is simplest:
const Complexity = {
  TRIVIAL: 1,      // Single-line changes, typo fixes
  SIMPLE: 2,       // Small, well-defined changes
  MEDIUM: 3,       // Moderate changes, some research needed
  COMPLEX: 4,      // Significant changes, multiple components
  VERY_COMPLEX: 5, // Large scope, architectural changes
} as const;

type Complexity = 1 | 2 | 3 | 4 | 5;
DEFAULT_COMPLEXITY
Complexity
default:"3"
Default complexity for new tasks

Complexity Utilities

// Validate complexity value
validateComplexity(value: unknown): Complexity

// Type guard
isValidComplexity(value: unknown): value is Complexity

// Display name
getComplexityDisplayName(complexity: Complexity): string
// Returns: "Trivial", "Simple", "Medium", "Complex", "Very Complex"

Task Type

Task type classification:
const TaskTypeValue = {
  BUG: 'bug',         // Defect requiring fix
  FEATURE: 'feature', // New functionality
  TASK: 'task',       // General work item
  CHORE: 'chore',     // Maintenance, cleanup, technical debt
} as const;

type TaskTypeValue = 'bug' | 'feature' | 'task' | 'chore';
DEFAULT_TASK_TYPE
TaskTypeValue
default:"task"
Default task type for new tasks

Task Type Utilities

// Validate task type
validateTaskType(value: unknown): TaskTypeValue

// Type guard
isValidTaskType(value: unknown): value is TaskTypeValue

// Display name
getTaskTypeDisplayName(taskType: TaskTypeValue): string
// Returns: "Bug", "Feature", "Task", "Chore"

Creating Tasks

interface CreateTaskInput {
  title: string;
  createdBy: EntityId;
  
  // Optional fields
  id?: ElementId;  // Pre-generated ID for hierarchical tasks
  descriptionRef?: DocumentId;
  acceptanceCriteria?: string;
  status?: TaskStatus;  // default: 'open'
  priority?: Priority;  // default: 3
  complexity?: Complexity;  // default: 3
  taskType?: TaskTypeValue;  // default: 'task'
  assignee?: EntityId;
  owner?: EntityId;
  deadline?: Timestamp;
  scheduledFor?: Timestamp;
  externalRef?: string;
  tags?: string[];
  metadata?: Record<string, unknown>;
}

// Create a new task
await createTask(input: CreateTaskInput, config?: IdGeneratorConfig): Promise<Task>

Updating Tasks

Update Status

interface UpdateTaskStatusInput {
  status: TaskStatus;
  closeReason?: string;  // Required when closing
}

// Update task status with transition validation
updateTaskStatus(task: Task, input: UpdateTaskStatusInput): Task

Soft Delete

interface DeleteTaskInput {
  deletedBy: EntityId;
  deleteReason?: string;
}

// Soft-delete a task (transitions to tombstone status)
softDeleteTask(task: Task, input: DeleteTaskInput): Task
Soft-deleted tasks (tombstone status) are terminal and cannot be reopened. Use deferred or backlog for postponed work.

Scheduling Utilities

// Check if task is scheduled for the future
isScheduledForFuture(task: Task): boolean

// Check if task is past its deadline
isPastDeadline(task: Task): boolean

Assignment Utilities

// Check if task has an assignee
isAssigned(task: Task): boolean

// Check if task has an owner
hasOwner(task: Task): boolean

Filtering and Sorting

Filter Functions

// Filter by status
filterByStatus<T extends Task>(tasks: T[], status: TaskStatus): T[]

// Filter by priority
filterByPriority<T extends Task>(tasks: T[], priority: Priority): T[]

// Filter by assignee
filterByAssignee<T extends Task>(tasks: T[], assignee: EntityId | undefined): T[]

// Filter ready tasks
filterReadyTasks<T extends Task>(tasks: T[]): T[]

Sort Functions

// Sort by priority (highest first)
sortByPriority<T extends Task>(tasks: T[]): T[]

// Sort by deadline (earliest first, null deadlines last)
sortByDeadline<T extends Task>(tasks: T[]): T[]

Validation Constants

MIN_TITLE_LENGTH
number
default:"1"
Minimum title length
MAX_TITLE_LENGTH
number
default:"500"
Maximum title length
MAX_ACCEPTANCE_CRITERIA_LENGTH
number
default:"10000"
Maximum acceptance criteria length
MAX_CLOSE_REASON_LENGTH
number
default:"1000"
Maximum close reason length
MAX_DELETE_REASON_LENGTH
number
default:"1000"
Maximum delete reason length

Type Guards

// Check if value is a valid Task
isTask(value: unknown): value is Task

// Comprehensive validation with detailed errors
validateTask(value: unknown): Task

Best Practices

Never Set Blocked Directly

blocked status is computed from dependencies. Use the dependency system instead.

Use Status Transitions

Always validate status transitions with validateStatusTransition()

Link to Documents

Use descriptionRef to link detailed descriptions instead of storing large text

Track Reasons

Always provide closeReason when closing and deleteReason when soft-deleting

Build docs developers (and LLMs) love