{"record":{"id":"42bc095995f63a9b","repo":"eyaltoledano/claude-task-master","slug":"invalid-transition-commit-complete-from-non-commi","errorCode":null,"errorMessage":"Invalid transition: COMMIT_COMPLETE from non-COMMIT phase","messagePattern":"Invalid transition: COMMIT_COMPLETE from non-COMMIT phase","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/tm-core/src/modules/workflow/orchestrators/workflow-orchestrator.ts","lineNumber":242,"sourceCode":"\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');\n\t\t\t\tthis.context.currentTDDPhase = 'COMMIT';\n\t\t\t\tthis.emit('tdd:commit:started');\n\t\t\t\tbreak;\n\n\t\t\tcase 'COMMIT_COMPLETE':\n\t\t\t\tif (currentTDD !== 'COMMIT') {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'Invalid transition: COMMIT_COMPLETE from non-COMMIT phase'\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthis.emit('tdd:commit:completed');\n\t\t\t\t// Mark current subtask as completed\n\t\t\t\tconst currentSubtask =\n\t\t\t\t\tthis.context.subtasks[this.context.currentSubtaskIndex];\n\t\t\t\tif (currentSubtask) {\n\t\t\t\t\tcurrentSubtask.status = 'completed';\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase 'SUBTASK_COMPLETE':\n\t\t\t\tthis.emit('subtask:completed');\n\t\t\t\t// Move to next subtask\n\t\t\t\tthis.context.currentSubtaskIndex++;\n\n\t\t\t\t// Emit progress update","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/eyaltoledano/claude-task-master/blob/c0c98d367c55296bfe69e65680625b6db437af02/packages/tm-core/src/modules/workflow/orchestrators/workflow-orchestrator.ts#L224-L260","documentation":"COMMIT is the final phase of each subtask's TDD cycle (after GREEN passes). This error is thrown by handleTDDPhaseTransition when a COMMIT_COMPLETE event is dispatched while the current TDD phase is not 'COMMIT' — the GREEN phase has not been completed (or the cycle already moved on). It preserves the invariant that a subtask can only be marked completed after tests passed and the GREEN -> COMMIT step occurred.","triggerScenarios":"Calling transition({ type: 'COMMIT_COMPLETE' }) while currentTDDPhase is 'RED' or 'GREEN'; sending COMMIT_COMPLETE twice (second time the phase has moved on); dispatching COMMIT_COMPLETE right after SUBTASK_COMPLETE started the next subtask (phase reset to RED).","commonSituations":"Automations that try to shortcut the cycle by jumping straight to commit; retry logic re-sending COMMIT_COMPLETE after a transient failure; event replay from a queue delivering COMMIT_COMPLETE before GREEN_PHASE_COMPLETE.","solutions":["Verify the phase is 'COMMIT' before dispatching; if still in GREEN, first complete GREEN with passing testResults via GREEN_PHASE_COMPLETE.","If already committed, do not send COMMIT_COMPLETE again — send SUBTASK_COMPLETE (or wait for the orchestrator's own flow) instead.","If the orchestrator advanced to a new subtask, restart its cycle at RED_PHASE_COMPLETE rather than sending COMMIT_COMPLETE.","Check whether events are being replayed/duplicated by your dispatch layer and add idempotency guards."],"exampleFix":"// before\nawait orchestrator.transition({ type: 'COMMIT_COMPLETE' });\n\n// after\nconst ctx = orchestrator.getContext();\nif (ctx.currentTDDPhase === 'COMMIT') {\n  await orchestrator.transition({ type: 'COMMIT_COMPLETE' });\n} else if (ctx.currentTDDPhase === 'GREEN') {\n  await orchestrator.transition({ type: 'GREEN_PHASE_COMPLETE', testResults });\n  await orchestrator.transition({ type: 'COMMIT_COMPLETE' });\n}","handlingStrategy":"validation","validationCode":"const ctx = orchestrator.getContext();\nif (ctx.currentTDDPhase !== 'COMMIT') {\n  throw new Error(`Cannot complete COMMIT: current TDD phase is ${ctx.currentTDDPhase ?? 'RED'}`);\n}","typeGuard":"function canCompleteCommit(ctx: { currentTDDPhase?: 'RED' | 'GREEN' | 'COMMIT' }): ctx is { currentTDDPhase: 'COMMIT' } {\n  return ctx.currentTDDPhase === 'COMMIT';\n}","tryCatchPattern":"try {\n  await orchestrator.transition({ type: 'COMMIT_COMPLETE' });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('COMMIT_COMPLETE from non-COMMIT')) {\n    // inspect orchestrator.getContext().currentTDDPhase and resume the cycle from the correct event\n  } else {\n    throw e;\n  }\n}","preventionTips":["Only send COMMIT_COMPLETE after observing the 'tdd:commit:started' event.","Do not resend COMMIT_COMPLETE after success — make dispatch idempotent.","Model the per-subtask cycle as an explicit local state machine mirroring RED -> GREEN -> COMMIT."],"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"}