{"record":{"id":"157422ffa5b8a69e","repo":"eyaltoledano/claude-task-master","slug":"invalid-transition-green-phase-complete-from-non","errorCode":null,"errorMessage":"Invalid transition: GREEN_PHASE_COMPLETE from non-GREEN phase","messagePattern":"Invalid transition: GREEN_PHASE_COMPLETE from non-GREEN phase","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/tm-core/src/modules/workflow/orchestrators/workflow-orchestrator.ts","lineNumber":217,"sourceCode":"\t\t\t\t\t\tthis.context.currentTDDPhase = 'RED';\n\t\t\t\t\t\tthis.emit('tdd:red:started');\n\t\t\t\t\t\tthis.emit('subtask:started');\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// All subtasks complete, transition to FINALIZE\n\t\t\t\t\t\tawait this.transition({ type: 'ALL_SUBTASKS_COMPLETE' });\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// Normal RED phase: has failing tests, proceed to GREEN\n\t\t\t\tthis.emit('tdd:red:completed');\n\t\t\t\tthis.context.currentTDDPhase = 'GREEN';\n\t\t\t\tthis.emit('tdd:green:started');\n\t\t\t\tbreak;\n\n\t\t\tcase 'GREEN_PHASE_COMPLETE':\n\t\t\t\tif (currentTDD !== 'GREEN') {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'Invalid transition: GREEN_PHASE_COMPLETE from non-GREEN phase'\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Validate test results are provided\n\t\t\t\tif (!event.testResults) {\n\t\t\t\t\tthrow new Error('Test results required for GREEN phase transition');\n\t\t\t\t}\n\n\t\t\t\t// Validate GREEN phase has no failures\n\t\t\t\tif (event.testResults.failed !== 0) {\n\t\t\t\t\tthrow new Error('GREEN phase must have zero failures');\n\t\t\t\t}\n\n\t\t\t\t// Store test results in context\n\t\t\t\tthis.context.lastTestResults = event.testResults;\n\n\t\t\t\tthis.emit('tdd:green:completed');","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/eyaltoledano/claude-task-master/blob/c0c98d367c55296bfe69e65680625b6db437af02/packages/tm-core/src/modules/workflow/orchestrators/workflow-orchestrator.ts#L199-L235","documentation":"The TDD workflow orchestrator enforces the strict RED -> GREEN -> COMMIT cycle per subtask. This error is thrown in handleTDDPhaseTransition when a GREEN_PHASE_COMPLETE event arrives while context.currentTDDPhase is not 'GREEN' (e.g. still RED or COMMIT). The orchestrator tracks the current TDD phase internally, and completing the GREEN phase out of order would break the state machine invariant that tests were written (RED) before implementation (GREEN).","triggerScenarios":"Calling orchestrator.transition({ type: 'GREEN_PHASE_COMPLETE', testResults }) when currentTDDPhase is 'RED' (skipping RED_PHASE_COMPLETE), or when it is 'COMMIT' (double-completing GREEN), or after phase was reset to undefined/RED (e.g. after ALL_SUBTASKS_COMPLETE or SUBTASK_COMPLETE moved to a new subtask).","commonSituations":"Driving the orchestrator from custom automation scripts instead of the normal TDD flow; replaying or re-dispatching events after a retry causes duplicate GREEN_PHASE_COMPLETE; resuming a persisted workflow whose phase was restored incorrectly; assuming phases can be skipped because tests already pass.","solutions":["Check the current phase before transitioning: only send GREEN_PHASE_COMPLETE when context.currentTDDPhase === 'GREEN'.","If still in RED, first emit RED_PHASE_COMPLETE (with test results showing at least one failing test) so the orchestrator moves to GREEN.","If in COMMIT, the GREEN phase was already completed — do not re-send GREEN_PHASE_COMPLETE; send COMMIT_COMPLETE instead.","For a new subtask, phases restart at RED; send RED_PHASE_COMPLETE first, not GREEN_PHASE_COMPLETE.","Inspect emitted events ('tdd:green:started') or logged state to confirm the phase actually advanced before sending the completion event."],"exampleFix":"// before\nawait orchestrator.transition({ type: 'GREEN_PHASE_COMPLETE', testResults });\n\n// after\nif (orchestrator.getContext().currentTDDPhase === 'GREEN') {\n  await orchestrator.transition({ type: 'GREEN_PHASE_COMPLETE', testResults });\n} else {\n  // advance through RED first\n  await orchestrator.transition({ type: 'RED_PHASE_COMPLETE', testResults });\n}","handlingStrategy":"validation","validationCode":"const ctx = orchestrator.getContext();\nif (ctx.currentTDDPhase !== 'GREEN') {\n  throw new Error(`Cannot complete GREEN phase: current TDD phase is ${ctx.currentTDDPhase ?? 'RED'}`);\n}","typeGuard":"function canCompleteGreen(ctx: { currentTDDPhase?: 'RED' | 'GREEN' | 'COMMIT' }): ctx is { currentTDDPhase: 'GREEN' } {\n  return ctx.currentTDDPhase === 'GREEN';\n}","tryCatchPattern":"try {\n  await orchestrator.transition({ type: 'GREEN_PHASE_COMPLETE', testResults });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('GREEN_PHASE_COMPLETE from non-GREEN')) {\n    // re-sync: inspect orchestrator.getContext().currentTDDPhase and resume from the correct event\n  } else {\n    throw e;\n  }\n}","preventionTips":["Always drive the cycle through the full RED -> GREEN -> COMMIT sequence per subtask.","Read context.currentTDDPhase (or listen for tdd:*:started events) before dispatching any phase-completion event.","Make dispatch code idempotent so retries cannot resend an already-processed phase event."],"tags":["state-machine","workflow","tdd","invalid-transition"],"backgroundTag":"invalid-state-transition","analyzedSha":"c0c98d367c55296bfe69e65680625b6db437af02","analyzedAt":"2026-08-29T02:56:26.071Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}