From 448af0a55c7e0afd185aee91ab5b716dd8e896b1 Mon Sep 17 00:00:00 2001 From: Anjali Sridhar Date: Wed, 18 Feb 2026 13:37:21 -0800 Subject: [PATCH 1/2] chore(core): improve ID generation and add runtime task validation --- packages/core/src/services/trackerTypes.ts | 37 ++++++++++++++-------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/packages/core/src/services/trackerTypes.ts b/packages/core/src/services/trackerTypes.ts index 06509fc770..2e5d4e36b9 100644 --- a/packages/core/src/services/trackerTypes.ts +++ b/packages/core/src/services/trackerTypes.ts @@ -4,18 +4,29 @@ * SPDX-License-Identifier: Apache-2.0 */ -export type TaskType = 'epic' | 'task' | 'bug'; +import { z } from 'zod'; -export type TaskStatus = 'open' | 'in_progress' | 'blocked' | 'closed'; +export const TaskTypeSchema = z.enum(['epic', 'task', 'bug']); +export type TaskType = z.infer; -export interface TrackerTask { - id: string; - title: string; - description: string; - type: TaskType; - status: TaskStatus; - parentId?: string; - dependencies: string[]; - subagentSessionId?: string; - metadata?: Record; -} +export const TaskStatusSchema = z.enum([ + 'open', + 'in_progress', + 'blocked', + 'closed', +]); +export type TaskStatus = z.infer; + +export const TrackerTaskSchema = z.object({ + id: z.string().length(6), + title: z.string(), + description: z.string(), + type: TaskTypeSchema, + status: TaskStatusSchema, + parentId: z.string().optional(), + dependencies: z.array(z.string()), + subagentSessionId: z.string().optional(), + metadata: z.record(z.unknown()).optional(), +}); + +export type TrackerTask = z.infer; From 2d6ee8f00558f536cd7bee5e54d60c9b8f694415 Mon Sep 17 00:00:00 2001 From: Anjali Sridhar Date: Wed, 18 Feb 2026 15:42:22 -0800 Subject: [PATCH 2/2] fix: address code review comments from bot in trackerService.ts - Use cryptographically secure ID generation with node:crypto - Implement runtime validation for JSON parsing using Zod - Optimize circular dependency validation to avoid N+1 file reads --- packages/core/src/services/trackerService.ts | 29 ++++++++++++-------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/core/src/services/trackerService.ts b/packages/core/src/services/trackerService.ts index f50eb711c1..37028fdd18 100644 --- a/packages/core/src/services/trackerService.ts +++ b/packages/core/src/services/trackerService.ts @@ -6,7 +6,8 @@ import fs from 'node:fs/promises'; import path from 'node:path'; -import type { TrackerTask } from './trackerTypes.js'; +import { randomBytes } from 'node:crypto'; +import { TrackerTaskSchema, type TrackerTask } from './trackerTypes.js'; export class TrackerService { private readonly trackerDir: string; @@ -28,7 +29,7 @@ export class TrackerService { * Generates a 6-character hex ID. */ private generateId(): string { - return Math.random().toString(16).substring(2, 8).padEnd(6, '0'); + return randomBytes(3).toString('hex'); } /** @@ -53,8 +54,8 @@ export class TrackerService { const taskPath = path.join(this.tasksDir, `${id}.json`); try { const content = await fs.readFile(taskPath, 'utf8'); - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - return JSON.parse(content) as TrackerTask; + const data: unknown = JSON.parse(content); + return TrackerTaskSchema.parse(data); } catch (error) { if ( error && @@ -82,8 +83,8 @@ export class TrackerService { path.join(this.tasksDir, f), 'utf8', ); - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - return JSON.parse(content) as TrackerTask; + const data: unknown = JSON.parse(content); + return TrackerTaskSchema.parse(data); }), ); return tasks; @@ -159,10 +160,17 @@ export class TrackerService { private async validateNoCircularDependencies( task: TrackerTask, ): Promise { + const allTasks = await this.listTasks(); + const taskMap = new Map( + allTasks.map((t) => [t.id, t]), + ); + // Ensure the current (possibly unsaved) task state is used + taskMap.set(task.id, task); + const visited = new Set(); const stack = new Set(); - const check = async (currentId: string) => { + const check = (currentId: string) => { if (stack.has(currentId)) { throw new Error( `Circular dependency detected involving task ${currentId}.`, @@ -175,17 +183,16 @@ export class TrackerService { visited.add(currentId); stack.add(currentId); - const currentTask = - currentId === task.id ? task : await this.getTask(currentId); + const currentTask = taskMap.get(currentId); if (currentTask) { for (const depId of currentTask.dependencies) { - await check(depId); + check(depId); } } stack.delete(currentId); }; - await check(task.id); + check(task.id); } }