@@ -199,7 +199,7 @@ async function ripgrepFallback(issue, projectRoot) {
199199
200200## Phase 3: Solution Planning
201201
202- ### Task Decomposition
202+ ### Task Decomposition (Closed-Loop)
203203
204204``` javascript
205205function decomposeTasks (issue , exploration ) {
@@ -217,15 +217,104 @@ function decomposeTasks(issue, exploration) {
217217 action: inferAction (group),
218218 description: group .description ,
219219 modification_points: group .points ,
220+
221+ // Phase 1: Implementation
220222 implementation: generateImplementationSteps (group, exploration),
223+
224+ // Phase 2: Test
225+ test: generateTestRequirements (group, exploration, issue .lifecycle_requirements ),
226+
227+ // Phase 3: Regression
228+ regression: generateRegressionChecks (group, issue .lifecycle_requirements ),
229+
230+ // Phase 4: Acceptance
221231 acceptance: generateAcceptanceCriteria (group),
232+
233+ // Phase 5: Commit
234+ commit: generateCommitSpec (group, issue),
235+
222236 depends_on: inferDependencies (group, tasks),
223- estimated_minutes: estimateTime (group)
237+ estimated_minutes: estimateTime (group),
238+ executor: inferExecutor (group)
224239 })
225240 }
226241
227242 return tasks
228243}
244+
245+ function generateTestRequirements (group , exploration , lifecycle ) {
246+ const test = {
247+ unit: [],
248+ integration: [],
249+ commands: [],
250+ coverage_target: 80
251+ }
252+
253+ // Generate unit test requirements based on action
254+ if (group .action === ' Create' || group .action === ' Implement' ) {
255+ test .unit .push (` Test ${ group .title } happy path` )
256+ test .unit .push (` Test ${ group .title } error cases` )
257+ }
258+
259+ // Generate test commands based on project patterns
260+ if (exploration .test_patterns ? .includes (' jest' )) {
261+ test .commands .push (` npm test -- --grep '${ group .scope } '` )
262+ } else if (exploration .test_patterns ? .includes (' vitest' )) {
263+ test .commands .push (` npx vitest run ${ group .scope } ` )
264+ } else {
265+ test .commands .push (` npm test` )
266+ }
267+
268+ // Add integration tests if needed
269+ if (lifecycle? .test_strategy === ' integration' || lifecycle? .test_strategy === ' e2e' ) {
270+ test .integration .push (` Integration test for ${ group .title } ` )
271+ }
272+
273+ return test
274+ }
275+
276+ function generateRegressionChecks (group , lifecycle ) {
277+ const regression = []
278+
279+ switch (lifecycle? .regression_scope ) {
280+ case ' full' :
281+ regression .push (' npm test' )
282+ regression .push (' npm run test:integration' )
283+ break
284+ case ' related' :
285+ regression .push (` npm test -- --grep '${ group .scope } '` )
286+ regression .push (` npm test -- --changed` )
287+ break
288+ case ' affected' :
289+ default:
290+ regression .push (` npm test -- --findRelatedTests ${ group .points [0 ]? .file }` )
291+ break
292+ }
293+
294+ return regression
295+ }
296+
297+ function generateCommitSpec(group, issue) {
298+ const typeMap = {
299+ 'Create': 'feat',
300+ 'Implement': 'feat',
301+ 'Update': 'feat',
302+ 'Fix': 'fix',
303+ 'Refactor': 'refactor',
304+ 'Test': 'test',
305+ 'Configure': 'chore',
306+ 'Delete': 'chore'
307+ }
308+
309+ const scope = group.scope.split('/').pop()?.replace(/\. .*$/, '') || 'core'
310+
311+ return {
312+ type: typeMap[group.action] || 'feat',
313+ scope: scope,
314+ message_template: ` ${typeMap[group .action ] || ' feat' }(${scope}): ${group .title .toLowerCase ()}\n\n${group .description || ' ' }` ,
315+ breaking: false
316+ }
317+ }
229318` ` `
230319
231320### Action Type Inference
@@ -347,11 +436,15 @@ function generateImplementationSteps(group, exploration) {
347436}
348437` ` `
349438
350- ### Acceptance Criteria Generation
439+ ### Acceptance Criteria Generation (Closed - Loop)
351440
352441` ` ` javascript
353442function generateAcceptanceCriteria(task) {
354- const criteria = []
443+ const acceptance = {
444+ criteria: [],
445+ verification: [],
446+ manual_checks: []
447+ }
355448
356449 // Action-specific criteria
357450 const actionCriteria = {
@@ -363,14 +456,41 @@ function generateAcceptanceCriteria(task) {
363456 'Configure': [` Configuration applied correctly` ]
364457 }
365458
366- criteria .push (... (actionCriteria[task .action ] || []))
459+ acceptance. criteria.push(...(actionCriteria[task.action] || []))
367460
368461 // Add quantified criteria
369462 if (task.modification_points.length > 0) {
370- criteria .push (` ${ task .modification_points .length } file(s) modified correctly` )
463+ acceptance.criteria.push(` ${task .modification_points .length } file (s) modified correctly` )
464+ }
465+
466+ // Generate verification steps for each criterion
467+ for (const criterion of acceptance.criteria) {
468+ acceptance.verification.push(generateVerificationStep(criterion, task))
371469 }
372470
373- return criteria .slice (0 , 4 ) // Max 4 criteria
471+ // Limit to reasonable counts
472+ acceptance.criteria = acceptance.criteria.slice(0, 4)
473+ acceptance.verification = acceptance.verification.slice(0, 4)
474+
475+ return acceptance
476+ }
477+
478+ function generateVerificationStep(criterion, task) {
479+ // Generate executable verification for criterion
480+ if (criterion.includes('file created')) {
481+ return ` ls - la ${task .modification_points [0 ]? .file } && head - 20 ${task .modification_points [0 ]? .file }`
482+ }
483+ if (criterion.includes('test')) {
484+ return ` npm test -- -- grep ' ${task.scope}' `
485+ }
486+ if (criterion.includes('export')) {
487+ return ` node - e " console.log(require('./${task.modification_points[0]?.file}'))" `
488+ }
489+ if (criterion.includes('API') || criterion.includes('endpoint')) {
490+ return ` curl - X GET http: // localhost:3000/${task.scope} -v`
491+ }
492+ // Default: describe manual check
493+ return ` Manually verify: ${ criterion} `
374494}
375495` ` `
376496
@@ -413,20 +533,61 @@ function validateSolution(solution) {
413533function validateTask (task ) {
414534 const errors = []
415535
536+ // Basic fields
416537 if (! / ^ T\d + $ / .test (task .id )) errors .push (' Invalid task ID format' )
417538 if (! task .title ? .trim ()) errors .push (' Missing title' )
418539 if (! task .scope ? .trim ()) errors .push (' Missing scope' )
419540 if (! [' Create' , ' Update' , ' Implement' , ' Refactor' , ' Configure' , ' Test' , ' Fix' , ' Delete' ].includes (task .action )) {
420541 errors .push (' Invalid action type' )
421542 }
543+
544+ // Phase 1: Implementation
422545 if (! task .implementation || task .implementation .length < 2 ) {
423546 errors .push (' Need 2+ implementation steps' )
424547 }
425- if (! task .acceptance || task .acceptance .length < 1 ) {
426- errors .push (' Need 1+ acceptance criteria' )
548+
549+ // Phase 2: Test
550+ if (! task .test ) {
551+ errors .push (' Missing test phase' )
552+ } else {
553+ if (! task .test .commands || task .test .commands .length < 1 ) {
554+ errors .push (' Need 1+ test commands' )
555+ }
427556 }
428- if (task .acceptance ? .some (a => / works correctly| good performance| properly/ i .test (a))) {
429- errors .push (' Vague acceptance criteria' )
557+
558+ // Phase 3: Regression
559+ if (! task .regression || task .regression .length < 1 ) {
560+ errors .push (' Need 1+ regression checks' )
561+ }
562+
563+ // Phase 4: Acceptance
564+ if (! task .acceptance ) {
565+ errors .push (' Missing acceptance phase' )
566+ } else {
567+ if (! task .acceptance .criteria || task .acceptance .criteria .length < 1 ) {
568+ errors .push (' Need 1+ acceptance criteria' )
569+ }
570+ if (! task .acceptance .verification || task .acceptance .verification .length < 1 ) {
571+ errors .push (' Need 1+ verification steps' )
572+ }
573+ if (task .acceptance .criteria ? .some (a => / works correctly| good performance| properly/ i .test (a))) {
574+ errors .push (' Vague acceptance criteria' )
575+ }
576+ }
577+
578+ // Phase 5: Commit
579+ if (! task .commit ) {
580+ errors .push (' Missing commit phase' )
581+ } else {
582+ if (! [' feat' , ' fix' , ' refactor' , ' test' , ' docs' , ' chore' ].includes (task .commit .type )) {
583+ errors .push (' Invalid commit type' )
584+ }
585+ if (! task .commit .scope ? .trim ()) {
586+ errors .push (' Missing commit scope' )
587+ }
588+ if (! task .commit .message_template ? .trim ()) {
589+ errors .push (' Missing commit message template' )
590+ }
430591 }
431592
432593 return errors
@@ -500,7 +661,9 @@ function generateOutput(solutions, conflicts) {
500661}
501662` ` `
502663
503- ### Solution Schema
664+ ### Solution Schema (Closed-Loop Tasks)
665+
666+ Each task MUST include ALL 5 lifecycle phases:
504667
505668` ` ` json
506669{
@@ -517,10 +680,62 @@ function generateOutput(solutions, conflicts) {
517680 " modification_points" : [
518681 { " file" : " src/middleware/auth.ts" , " target" : " new file" , " change" : " Create middleware" }
519682 ],
520- " implementation" : [" Step 1" , " Step 2" , " ..." ],
521- " acceptance" : [" Criterion 1" , " Criterion 2" ],
683+
684+ " implementation" : [
685+ " Create auth.ts file in src/middleware/" ,
686+ " Implement JWT token extraction from Authorization header" ,
687+ " Add token validation using jsonwebtoken library" ,
688+ " Handle error cases (missing, invalid, expired tokens)" ,
689+ " Export middleware function"
690+ ],
691+
692+ " test" : {
693+ " unit" : [
694+ " Test valid token passes through" ,
695+ " Test invalid token returns 401" ,
696+ " Test expired token returns 401" ,
697+ " Test missing token returns 401"
698+ ],
699+ " integration" : [
700+ " Protected route returns 401 without token" ,
701+ " Protected route returns 200 with valid token"
702+ ],
703+ " commands" : [
704+ " npm test -- --grep 'auth middleware'" ,
705+ " npm run test:coverage -- src/middleware/auth.ts"
706+ ],
707+ " coverage_target" : 80
708+ },
709+
710+ " regression" : [
711+ " npm test -- --grep 'existing routes'" ,
712+ " npm run test:integration"
713+ ],
714+
715+ " acceptance" : {
716+ " criteria" : [
717+ " Middleware validates JWT tokens successfully" ,
718+ " Returns 401 with appropriate error for invalid tokens" ,
719+ " Passes decoded user payload to request context"
720+ ],
721+ " verification" : [
722+ " curl -H 'Authorization: Bearer <valid>' /api/protected → 200" ,
723+ " curl /api/protected → 401 {error: 'No token'}" ,
724+ " curl -H 'Authorization: Bearer invalid' /api/protected → 401"
725+ ],
726+ " manual_checks" : []
727+ },
728+
729+ " commit" : {
730+ " type" : " feat" ,
731+ " scope" : " auth" ,
732+ " message_template" : " feat(auth): add JWT validation middleware\n\n - Implement token extraction and validation\n - Add error handling for invalid/expired tokens\n - Export middleware for route protection" ,
733+ " breaking" : false
734+ },
735+
522736 " depends_on" : [],
523- " estimated_minutes" : 30
737+ " estimated_minutes" : 30 ,
738+ " executor" : " codex"
524739 }
525740 ],
526741 " exploration_context" : {
@@ -622,6 +837,14 @@ Before outputting solution:
6228376. Include file:line references in modification_points where possible
6238387. Detect and report cross-issue file conflicts in batch mode
6248398. Include exploration_context with patterns and relevant_files
840+ 9. **Generate ALL 5 lifecycle phases for each task**:
841+ - ` implementation` : 2-7 concrete steps
842+ - ` test` : unit tests, commands, coverage target
843+ - ` regression` : regression check commands
844+ - ` acceptance` : criteria + verification steps
845+ - ` commit` : type, scope, message template
846+ 10. Infer test commands from project's test framework
847+ 11. Generate commit message following conventional commits
625848
626849**NEVER**:
6278501. Execute implementation (return plan only)
@@ -632,3 +855,5 @@ Before outputting solution:
6328556. Assume file exists without verification
6338567. Generate more than 10 tasks per issue
6348578. Skip ACE search (unless fallback triggered)
858+ 9. **Omit any of the 5 lifecycle phases** (test, regression, acceptance, commit)
859+ 10. Skip verification steps in acceptance criteria
0 commit comments