/** * TaskDependencyRepository - CRUD + list_for_task, list_dependents for task_dependencies table (§8) * * Implements Repository * per contracts §6. * * @module packages/runtime/src/storage/repositories/TaskDependencyRepository */ import type { Repository, TransactionHandle, SessionID, TaskID, UUID, ISOTimeString, } from '@aircoding/contracts' import { DatabaseHandle } from '../MigrationRunner.js' import { assertEnumValues } from '../assertEnum.js' // ============================================================================= // Types - per db-schema §8 // ============================================================================= export type TaskDependencyType = 'hard' | 'soft' | 'conflict' | 'serialization' export interface TaskDependencyRecord { id: UUID session_id: SessionID task_id: TaskID depends_on_task_id: TaskID dependency_type: TaskDependencyType reason?: string created_at: ISOTimeString } export type TaskDependencyInsert = Omit & { id?: UUID } export type TaskDependencyUpdate = Partial> // ============================================================================= // TaskDependencyRepository // ============================================================================= export class TaskDependencyRepository implements Repository { private db: DatabaseHandle constructor(db: DatabaseHandle) { this.db = db } /** * Get a task dependency by ID. */ async get(id: UUID, tx?: TransactionHandle): Promise { const stmt = (tx?.db ?? this.db).prepare('SELECT * FROM task_dependencies WHERE id = ?') const row = stmt.get(id) as TaskDependencyRecord | undefined return row } /** * Insert a new task dependency. */ async insert(record: TaskDependencyInsert, tx?: TransactionHandle): Promise { // Validate enum columns assertEnumValues('task_dependencies', { dependency_type: record.dependency_type, }) const stmt = (tx?.db ?? this.db).prepare(` INSERT INTO task_dependencies ( id, session_id, task_id, depends_on_task_id, dependency_type, reason, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?) `) stmt.run( record.id, record.session_id, record.task_id, record.depends_on_task_id, record.dependency_type, record.reason ?? null, record.created_at, ) } /** * Update an existing task dependency. */ async update(id: UUID, patch: TaskDependencyUpdate, tx?: TransactionHandle): Promise { // Validate enum columns if present if (patch.dependency_type !== undefined) { assertEnumValues('task_dependencies', { dependency_type: patch.dependency_type }) } const fields: string[] = [] const values: unknown[] = [] if (patch.dependency_type !== undefined) { fields.push('dependency_type = ?') values.push(patch.dependency_type) } if (patch.reason !== undefined) { fields.push('reason = ?') values.push(patch.reason) } if (fields.length === 0) { return // Nothing to update } values.push(id) const stmt = (tx?.db ?? this.db).prepare(`UPDATE task_dependencies SET ${fields.join(', ')} WHERE id = ?`) stmt.run(...values) } // ============================================================================= // Extra methods // ============================================================================= /** * List all dependencies for a task (what this task depends on). */ async list_for_task(task_id: TaskID): Promise { const stmt = this.db.prepare( 'SELECT * FROM task_dependencies WHERE task_id = ? ORDER BY created_at ASC', ) return stmt.all(task_id) as TaskDependencyRecord[] } /** * List all dependents of a task (tasks that depend on this one). */ async list_dependents(depends_on_task_id: TaskID): Promise { const stmt = this.db.prepare( 'SELECT * FROM task_dependencies WHERE depends_on_task_id = ? ORDER BY created_at ASC', ) return stmt.all(depends_on_task_id) as TaskDependencyRecord[] } }