fix(ai-validation): comprehensive fixes for AI response validation issues

- Fix update command validation when AI omits subtasks/status/dependencies
  - Fix add-task command when AI returns non-string details field
  - Fix update-task command when AI subtasks miss required fields
  - Add preprocessing to ensure proper field types before validation
  - Prevent split() errors on non-string fields
  - Set proper defaults for missing required fields
This commit is contained in:
Ralph Khreish
2025-07-17 21:11:56 +03:00
parent b78de8dbb4
commit 5713bb17cf
2 changed files with 38 additions and 3 deletions

View File

@@ -190,8 +190,25 @@ function parseUpdatedTaskFromText(text, expectedTaskId, logFn, isMCP) {
throw new Error('Parsed AI response is not a valid JSON object.'); throw new Error('Parsed AI response is not a valid JSON object.');
} }
// Preprocess the task to ensure subtasks have proper structure
const preprocessedTask = {
...parsedTask,
// Ensure subtasks is an array and each subtask has required fields
subtasks: Array.isArray(parsedTask.subtasks)
? parsedTask.subtasks.map(subtask => ({
...subtask,
title: subtask.title || '',
description: subtask.description || '',
status: subtask.status || 'pending',
dependencies: Array.isArray(subtask.dependencies) ? subtask.dependencies : [],
details: typeof subtask.details === 'string' ? subtask.details : String(subtask.details || ''),
testStrategy: typeof subtask.testStrategy === 'string' ? subtask.testStrategy : String(subtask.testStrategy || '')
}))
: []
};
// Validate the parsed task object using Zod // Validate the parsed task object using Zod
const validationResult = updatedTaskSchema.safeParse(parsedTask); const validationResult = updatedTaskSchema.safeParse(preprocessedTask);
if (!validationResult.success) { if (!validationResult.success) {
report('error', 'Parsed task object failed Zod validation.'); report('error', 'Parsed task object failed Zod validation.');
validationResult.error.errors.forEach((err) => { validationResult.error.errors.forEach((err) => {

View File

@@ -196,7 +196,18 @@ function parseUpdatedTasksFromText(text, expectedCount, logFn, isMCP) {
); );
} }
const validationResult = updatedTaskArraySchema.safeParse(parsedTasks); // Preprocess tasks to ensure required fields have proper defaults
const preprocessedTasks = parsedTasks.map(task => ({
...task,
// Ensure subtasks is always an array (not null or undefined)
subtasks: Array.isArray(task.subtasks) ? task.subtasks : [],
// Ensure status has a default value if missing
status: task.status || 'pending',
// Ensure dependencies is always an array
dependencies: Array.isArray(task.dependencies) ? task.dependencies : []
}));
const validationResult = updatedTaskArraySchema.safeParse(preprocessedTasks);
if (!validationResult.success) { if (!validationResult.success) {
report('error', 'Parsed task array failed Zod validation.'); report('error', 'Parsed task array failed Zod validation.');
validationResult.error.errors.forEach((err) => { validationResult.error.errors.forEach((err) => {
@@ -442,7 +453,14 @@ async function updateTasks(
data.tasks.forEach((task, index) => { data.tasks.forEach((task, index) => {
if (updatedTasksMap.has(task.id)) { if (updatedTasksMap.has(task.id)) {
// Only update if the task was part of the set sent to AI // Only update if the task was part of the set sent to AI
data.tasks[index] = updatedTasksMap.get(task.id); const updatedTask = updatedTasksMap.get(task.id);
// Merge the updated task with the existing one to preserve fields like subtasks
data.tasks[index] = {
...task, // Keep all existing fields
...updatedTask, // Override with updated fields
// Ensure subtasks field is preserved if not provided by AI
subtasks: updatedTask.subtasks !== undefined ? updatedTask.subtasks : task.subtasks
};
actualUpdateCount++; actualUpdateCount++;
} }
}); });