{"record":{"id":"c1e90ed7b31e459f","repo":"coleam00/Archon","slug":"run-run-id-s-gate-only-accepts-approve-or-re","errorCode":null,"errorMessage":"Run ${run.id}'s gate only accepts 'approve' or 'reject' — '${decision}' is not one of its declared decisions. Declare `approval.decisions:` on the gate node to author a broader vocabulary.","messagePattern":"Run (.+?)'s gate only accepts 'approve' or 'reject' — '(.+?)' is not one of its declared decisions\\. Declare `approval\\.decisions:` on the gate node to author a broader vocabulary\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/operations/workflow-operations.ts","lineNumber":902,"sourceCode":" * existing `approveWorkflow`/`rejectWorkflow` machinery by `respondToWorkflow`\n * — this function is not consulted for those two ids). Any OTHER decision is\n * legal only for a plain gate node (`approval`/`undefined` suspend type) whose\n * author explicitly declared `approval.decisions:` (`decisionsAuthored`) —\n * `writeback`/`interactive_loop`/`child_workflow` pauses have no author-\n * declared vocabulary and accept only approve/reject; a legacy gate (no\n * `decisions:` authored) also only ever has the synthesized approve/reject\n * pair (#2707 step 2).\n */\nexport function assertRespondable(run: WorkflowRun, decision: string): ApprovalContext {\n  const approval = assertApprovable(run);\n  if (approval.type !== 'approval' && approval.type !== undefined) {\n    throw new Error(\n      `Run ${run.id}'s gate ('${approval.type}') only accepts 'approve' or 'reject' — ` +\n        `'${decision}' is not a valid response here.`\n    );\n  }\n  if (approval.decisionsAuthored !== true) {\n    throw new Error(\n      `Run ${run.id}'s gate only accepts 'approve' or 'reject' — '${decision}' is not one of its ` +\n        'declared decisions. Declare `approval.decisions:` on the gate node to author a broader vocabulary.'\n    );\n  }\n  const declaredIds = (approval.decisions ?? []).map(d => d.id);\n  if (!declaredIds.includes(decision)) {\n    throw new Error(\n      `Run ${run.id}'s gate does not declare decision '${decision}'. Declared decisions: ` +\n        `${declaredIds.join(', ')}.`\n    );\n  }\n  return approval;\n}\n\n/**\n * Resolve a paused gate with an author-declared decision beyond approve/reject\n * (#2707 step 2 — the general `workflow respond <id> <decision> [text]` verb).\n * `approve`/`reject` are NOT handled here — `respondToWorkflow` delegates those","sourceCodeStart":884,"sourceCodeEnd":920,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/core/src/operations/workflow-operations.ts#L884-L920","documentation":"assertRespondable throws this when the gate is an approval gate but has NOT authored a custom decisions list (decisionsAuthored !== true), and the caller supplied a decision other than 'approve'/'reject'. Undecorated approval gates only synthesize the binary approve/reject pair, so any other vocabulary is refused.","triggerScenarios":"Responding to a run with decision strings like 'request_changes' or 'approve_with_comments' when the gate node lacks an `approval.decisions:` declaration; e.g. `respondToWorkflow(runId, 'request_changes')` on a gate authored without decisions.","commonSituations":"A developer adds a richer decision word to a bot/CLI call assuming gates accept free-form decisions; a workflow author forgot to add `approval.decisions:` before wiring custom responses; copy-pasted response code from a workflow that did declare decisions.","solutions":["Add `approval.decisions:` entries on the gate node to declare the custom decision ids you want to accept.","Alternatively respond with the plain 'approve' or 'reject' the uncustomized gate supports.","Fetch the run's approval context first and only send decisions it declares."],"exampleFix":"// before (workflow YAML)\n- id: review\n  type: approval\n// after\n- id: review\n  type: approval\n  approval:\n    decisions:\n      - id: request_changes\n      - id: approve","handlingStrategy":"validation","validationCode":"const allowed = gate.decisionsAuthored ? gate.decisions.map(d => d.id) : ['approve', 'reject'];\nif (!allowed.includes(decision)) {\n  throw new Error(`Decision '${decision}' not allowed; allowed: ${allowed.join(', ')}`);\n}","typeGuard":"function isBinaryDecision(decision) {\n  return decision === 'approve' || decision === 'reject';\n}","tryCatchPattern":"try {\n  await respondToWorkflow(runId, decision);\n} catch (e) {\n  if (e.message.includes('not one of its declared decisions')) {\n    await respondToWorkflow(runId, isBinaryDecision(decision) ? decision : 'reject');\n  } else throw e;\n}","preventionTips":["Declare approval.decisions on any gate intended for non-binary responses.","Derive the UI's decision buttons from the run's declared decisions, not a hardcoded list.","Keep workflow YAML and calling scripts reviewed together when changing gate vocabulary."],"tags":["workflow","gate","decisions","validation"],"backgroundTag":"invalid-gate-decision","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}