diff --git a/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts b/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts index 06c01c419..327f51759 100644 --- a/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +++ b/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts @@ -256,6 +256,28 @@ test("executePlanSlice writes task planning state and rendered plan artifacts", } }); +test("executePlanSlice marks validation failures with isError", async () => { + const base = makeTmpBase(); + try { + openTestDb(base); + + const result = await inProjectDir(base, () => executePlanSlice({ + milestoneId: "M001", + sliceId: "S01", + goal: "Trigger validation failure for empty tasks.", + tasks: [], + }, base)); + + assert.equal(result.isError, true); + assert.equal(result.details.operation, "plan_slice"); + assert.match(String(result.details.error), /validation failed: tasks must be a non-empty array/); + assert.match(result.content[0].text, /Error planning slice:/); + } finally { + closeDatabase(); + cleanup(base); + } +}); + test("executeSliceComplete coerces string enrichment entries and writes summary/UAT artifacts", async () => { const base = makeTmpBase(); try { diff --git a/src/resources/extensions/gsd/tools/workflow-tool-executors.ts b/src/resources/extensions/gsd/tools/workflow-tool-executors.ts index edc1bfd31..14f179bff 100644 --- a/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +++ b/src/resources/extensions/gsd/tools/workflow-tool-executors.ts @@ -38,6 +38,7 @@ export function isSupportedSummaryArtifactType( export interface ToolExecutionResult { content: Array<{ type: "text"; text: string }>; details: Record; + isError?: boolean; } export interface SummarySaveParams { @@ -57,13 +58,15 @@ export async function executeSummarySave( return { content: [{ type: "text", text: "Error: GSD database is not available. Cannot save artifact." }], details: { operation: "save_summary", error: "db_unavailable" }, - }; + isError: true, + }; } if (!isSupportedSummaryArtifactType(params.artifact_type)) { return { content: [{ type: "text", text: `Error: Invalid artifact_type "${params.artifact_type}". Must be one of: ${SUPPORTED_SUMMARY_ARTIFACT_TYPES.join(", ")}` }], details: { operation: "save_summary", error: "invalid_artifact_type" }, - }; + isError: true, + }; } const contextGuard = shouldBlockContextArtifactSaveInSnapshot( loadWriteGateSnapshot(basePath), @@ -75,7 +78,8 @@ export async function executeSummarySave( return { content: [{ type: "text", text: `Error saving artifact: ${contextGuard.reason ?? "context write blocked"}` }], details: { operation: "save_summary", error: "context_write_blocked" }, - }; + isError: true, + }; } try { let relativePath: string; @@ -108,7 +112,8 @@ export async function executeSummarySave( return { content: [{ type: "text", text: `Error saving artifact: ${msg}` }], details: { operation: "save_summary", error: msg }, - }; + isError: true, + }; } } @@ -163,7 +168,8 @@ export async function executeTaskComplete( return { content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete task." }], details: { operation: "complete_task", error: "db_unavailable" }, - }; + isError: true, + }; } try { const coerced = { ...params }; @@ -176,6 +182,7 @@ export async function executeTaskComplete( return { content: [{ type: "text", text: `Error completing task: ${result.error}` }], details: { operation: "complete_task", error: result.error }, + isError: true, }; } return { @@ -194,7 +201,8 @@ export async function executeTaskComplete( return { content: [{ type: "text", text: `Error completing task: ${msg}` }], details: { operation: "complete_task", error: msg }, - }; + isError: true, + }; } } @@ -207,7 +215,8 @@ export async function executeSliceComplete( return { content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete slice." }], details: { operation: "complete_slice", error: "db_unavailable" }, - }; + isError: true, + }; } try { const splitPair = (s: string): [string, string] => { @@ -257,6 +266,7 @@ export async function executeSliceComplete( return { content: [{ type: "text", text: `Error completing slice: ${result.error}` }], details: { operation: "complete_slice", error: result.error }, + isError: true, }; } return { @@ -275,7 +285,8 @@ export async function executeSliceComplete( return { content: [{ type: "text", text: `Error completing slice: ${msg}` }], details: { operation: "complete_slice", error: msg }, - }; + isError: true, + }; } } @@ -288,7 +299,8 @@ export async function executeCompleteMilestone( return { content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete milestone." }], details: { operation: "complete_milestone", error: "db_unavailable" }, - }; + isError: true, + }; } try { const sanitized = sanitizeCompleteMilestoneParams(params); @@ -297,6 +309,7 @@ export async function executeCompleteMilestone( return { content: [{ type: "text", text: `Error completing milestone: ${result.error}` }], details: { operation: "complete_milestone", error: result.error }, + isError: true, }; } return { @@ -313,7 +326,8 @@ export async function executeCompleteMilestone( return { content: [{ type: "text", text: `Error completing milestone: ${msg}` }], details: { operation: "complete_milestone", error: msg }, - }; + isError: true, + }; } } @@ -326,7 +340,8 @@ export async function executeValidateMilestone( return { content: [{ type: "text", text: "Error: GSD database is not available. Cannot validate milestone." }], details: { operation: "validate_milestone", error: "db_unavailable" }, - }; + isError: true, + }; } try { const result = await handleValidateMilestone(params, basePath); @@ -334,6 +349,7 @@ export async function executeValidateMilestone( return { content: [{ type: "text", text: `Error validating milestone: ${result.error}` }], details: { operation: "validate_milestone", error: result.error }, + isError: true, }; } return { @@ -351,7 +367,8 @@ export async function executeValidateMilestone( return { content: [{ type: "text", text: `Error validating milestone: ${msg}` }], details: { operation: "validate_milestone", error: msg }, - }; + isError: true, + }; } } @@ -364,7 +381,8 @@ export async function executeReassessRoadmap( return { content: [{ type: "text", text: "Error: GSD database is not available. Cannot reassess roadmap." }], details: { operation: "reassess_roadmap", error: "db_unavailable" }, - }; + isError: true, + }; } try { const result = await handleReassessRoadmap(params, basePath); @@ -372,6 +390,7 @@ export async function executeReassessRoadmap( return { content: [{ type: "text", text: `Error reassessing roadmap: ${result.error}` }], details: { operation: "reassess_roadmap", error: result.error }, + isError: true, }; } return { @@ -390,7 +409,8 @@ export async function executeReassessRoadmap( return { content: [{ type: "text", text: `Error reassessing roadmap: ${msg}` }], details: { operation: "reassess_roadmap", error: msg }, - }; + isError: true, + }; } } @@ -403,7 +423,8 @@ export async function executeSaveGateResult( return { content: [{ type: "text", text: "Error: GSD database is not available." }], details: { operation: "save_gate_result", error: "db_unavailable" }, - }; + isError: true, + }; } const validGates = ["Q3", "Q4", "Q5", "Q6", "Q7", "Q8"]; @@ -411,7 +432,8 @@ export async function executeSaveGateResult( return { content: [{ type: "text", text: `Error: Invalid gateId "${params.gateId}". Must be one of: ${validGates.join(", ")}` }], details: { operation: "save_gate_result", error: "invalid_gate_id" }, - }; + isError: true, + }; } const validVerdicts = ["pass", "flag", "omitted"]; @@ -419,7 +441,8 @@ export async function executeSaveGateResult( return { content: [{ type: "text", text: `Error: Invalid verdict "${params.verdict}". Must be one of: ${validVerdicts.join(", ")}` }], details: { operation: "save_gate_result", error: "invalid_verdict" }, - }; + isError: true, + }; } try { @@ -443,7 +466,8 @@ export async function executeSaveGateResult( return { content: [{ type: "text", text: `Error saving gate result: ${msg}` }], details: { operation: "save_gate_result", error: msg }, - }; + isError: true, + }; } } @@ -456,7 +480,8 @@ export async function executePlanMilestone( return { content: [{ type: "text", text: "Error: GSD database is not available. Cannot plan milestone." }], details: { operation: "plan_milestone", error: "db_unavailable" }, - }; + isError: true, + }; } try { const result = await handlePlanMilestone(params, basePath); @@ -464,6 +489,7 @@ export async function executePlanMilestone( return { content: [{ type: "text", text: `Error planning milestone: ${result.error}` }], details: { operation: "plan_milestone", error: result.error }, + isError: true, }; } return { @@ -480,7 +506,8 @@ export async function executePlanMilestone( return { content: [{ type: "text", text: `Error planning milestone: ${msg}` }], details: { operation: "plan_milestone", error: msg }, - }; + isError: true, + }; } } @@ -493,7 +520,8 @@ export async function executePlanSlice( return { content: [{ type: "text", text: "Error: GSD database is not available. Cannot plan slice." }], details: { operation: "plan_slice", error: "db_unavailable" }, - }; + isError: true, + }; } try { const result = await handlePlanSlice(params, basePath); @@ -501,6 +529,7 @@ export async function executePlanSlice( return { content: [{ type: "text", text: `Error planning slice: ${result.error}` }], details: { operation: "plan_slice", error: result.error }, + isError: true, }; } return { @@ -519,7 +548,8 @@ export async function executePlanSlice( return { content: [{ type: "text", text: `Error planning slice: ${msg}` }], details: { operation: "plan_slice", error: msg }, - }; + isError: true, + }; } } @@ -532,7 +562,8 @@ export async function executeReplanSlice( return { content: [{ type: "text", text: "Error: GSD database is not available. Cannot replan slice." }], details: { operation: "replan_slice", error: "db_unavailable" }, - }; + isError: true, + }; } try { const result = await handleReplanSlice(params, basePath); @@ -540,6 +571,7 @@ export async function executeReplanSlice( return { content: [{ type: "text", text: `Error replanning slice: ${result.error}` }], details: { operation: "replan_slice", error: result.error }, + isError: true, }; } return { @@ -558,7 +590,8 @@ export async function executeReplanSlice( return { content: [{ type: "text", text: `Error replanning slice: ${msg}` }], details: { operation: "replan_slice", error: msg }, - }; + isError: true, + }; } } @@ -576,6 +609,7 @@ export async function executeMilestoneStatus( return { content: [{ type: "text", text: "Error: GSD database is not available." }], details: { operation: "milestone_status", error: "db_unavailable" }, + isError: true, }; } @@ -624,6 +658,7 @@ export async function executeMilestoneStatus( return { content: [{ type: "text", text: `Error querying milestone status: ${msg}` }], details: { operation: "milestone_status", error: msg }, - }; + isError: true, + }; } }