task-validation.test.ts
1 import assert from 'node:assert/strict' 2 import { test } from 'node:test' 3 import { validateTaskCompletion } from '@/lib/server/tasks/task-validation' 4 import type { BoardTask } from '@/types' 5 6 test('validateTaskCompletion fails screenshot delivery tasks without artifact evidence', () => { 7 const validation = validateTaskCompletion({ 8 title: 'Take screenshot and send it every minute', 9 description: 'Schedule a screenshot capture and deliver it to the user.', 10 result: 'Existing schedule verified for taking screenshots every minute. Waiting for next run.', 11 error: null, 12 } as Partial<BoardTask>) 13 14 assert.equal(validation.ok, false) 15 assert.ok(validation.reasons.some((reason) => reason.includes('Screenshot delivery task is missing artifact evidence'))) 16 }) 17 18 test('validateTaskCompletion accepts screenshot delivery tasks with upload artifact evidence', () => { 19 const validation = validateTaskCompletion({ 20 title: 'Take screenshot and send it', 21 description: 'Capture Wikipedia and return the file to the user.', 22 result: 'Captured and sent screenshot successfully: sandbox:/api/uploads/1234-wikipedia.png', 23 error: null, 24 } as Partial<BoardTask>) 25 26 assert.equal(validation.ok, true) 27 }) 28 29 test('validateTaskCompletion accepts concise non-implementation result summaries', () => { 30 const validation = validateTaskCompletion({ 31 title: 'Answer greeting', 32 description: 'Respond to a basic hello prompt.', 33 result: 'Hello! How can I help you today?', 34 error: null, 35 } as Partial<BoardTask>) 36 37 assert.equal(validation.ok, true) 38 }) 39 40 test('validateTaskCompletion still enforces stricter minimum for implementation tasks', () => { 41 const validation = validateTaskCompletion({ 42 title: 'Fix retry bug', 43 description: 'Implement queue retry fixes and verify.', 44 result: 'Patched queue retry bug.', 45 error: null, 46 } as Partial<BoardTask>) 47 48 assert.equal(validation.ok, false) 49 assert.ok(validation.reasons.some((reason) => reason.includes('Result summary is too short'))) 50 }) 51 52 test('validateTaskCompletion fails implementation task with unfinished next-step language', () => { 53 const validation = validateTaskCompletion({ 54 title: 'Build weather dashboard', 55 description: 'Implement dashboard and run dev server.', 56 result: 'I prepared an outline. Next I will run the server once access is granted.', 57 error: null, 58 } as Partial<BoardTask>) 59 60 assert.equal(validation.ok, false) 61 assert.ok(validation.reasons.some((reason) => reason.includes('unfinished work'))) 62 }) 63 64 test('validateTaskCompletion fails implementation task that requests shell access', () => { 65 const validation = validateTaskCompletion({ 66 title: 'Create blog and run server', 67 description: 'Create markdown blog and serve it.', 68 result: 'I created the blog file at data/workspace/blog/swarmclaw-blog.md, but I need access to the shell to proceed. Once the access is granted, I will finish setup.', 69 error: null, 70 } as Partial<BoardTask>) 71 72 assert.equal(validation.ok, false) 73 assert.ok(validation.reasons.some((reason) => reason.includes('unfinished work'))) 74 }) 75 76 test('validateTaskCompletion fails untitled tasks with empty metadata', () => { 77 const validation = validateTaskCompletion({ 78 title: 'Untitled Task', 79 description: '', 80 result: 'Could you provide more information about what you need?', 81 error: null, 82 } as Partial<BoardTask>) 83 84 assert.equal(validation.ok, false) 85 assert.ok(validation.reasons.some((reason) => reason.includes('metadata is too vague'))) 86 }) 87 88 test('validateTaskCompletion enforces explicit quality gate evidence requirements', () => { 89 const validation = validateTaskCompletion({ 90 title: 'Ship API migration summary', 91 description: 'Summarize the migration outcome.', 92 result: 'Migration summary completed successfully with no extra artifacts included.', 93 qualityGate: { 94 enabled: true, 95 minResultChars: 20, 96 minEvidenceItems: 2, 97 requireArtifact: true, 98 }, 99 error: null, 100 } as Partial<BoardTask>) 101 102 assert.equal(validation.ok, false) 103 assert.ok(validation.reasons.some((reason) => reason.includes('insufficient completion evidence'))) 104 assert.ok(validation.reasons.some((reason) => reason.includes('artifact evidence is required'))) 105 }) 106 107 test('validateTaskCompletion accepts short replies when prompt explicitly asks for one', () => { 108 const cases = [ 109 { description: 'Reply with the word PONG only.', result: 'PONG' }, 110 { description: 'Answer in one word.', result: 'Yes' }, 111 { description: 'Yes or no: is it raining?', result: 'No' }, 112 { description: 'Just say hi.', result: 'hi' }, 113 { description: 'Respond with the number 42 only.', result: '42' }, 114 ] 115 for (const c of cases) { 116 const validation = validateTaskCompletion({ 117 title: 'Ping', 118 description: c.description, 119 result: c.result, 120 error: null, 121 } as Partial<BoardTask>) 122 assert.equal(validation.ok, true, `expected ok for "${c.description}" -> "${c.result}", got: ${validation.reasons.join('; ')}`) 123 } 124 }) 125 126 test('validateTaskCompletion still rejects short generic replies when no short-answer signal in prompt', () => { 127 const validation = validateTaskCompletion({ 128 title: 'Summarize the project', 129 description: 'Give an overview of the codebase.', 130 result: 'Done.', 131 error: null, 132 } as Partial<BoardTask>) 133 assert.equal(validation.ok, false) 134 assert.ok(validation.reasons.some((r) => r.includes('Result summary is too short'))) 135 }) 136 137 test('validateTaskCompletion passes explicit quality gate when evidence checks are met', () => { 138 const validation = validateTaskCompletion({ 139 title: 'Ship API migration summary', 140 description: 'Summarize the migration outcome.', 141 result: 'Ran npm test and tests passed. Updated src/api/migrate.ts. Uploaded evidence: sandbox:/api/uploads/migration-proof.png.', 142 artifacts: [{ 143 url: 'sandbox:/api/uploads/migration-proof.png', 144 type: 'image', 145 filename: 'migration-proof.png', 146 }], 147 qualityGate: { 148 enabled: true, 149 minResultChars: 20, 150 minEvidenceItems: 2, 151 requireArtifact: true, 152 requireVerification: true, 153 }, 154 error: null, 155 } as Partial<BoardTask>) 156 157 assert.equal(validation.ok, true) 158 })