{"record":{"id":"8b9c97b6c431a56e","repo":"paperclipai/paperclip","slug":"semantic-result-disposition-does-not-match-the-ter","errorCode":null,"errorMessage":"Semantic result disposition does not match the terminal tool","messagePattern":"Semantic result disposition does not match the terminal tool","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts","lineNumber":898,"sourceCode":"        item: {\n          type: \"tool_call\",\n          id: call.callId,\n          name: tool,\n          arguments: call.arguments,\n        },\n      },\n      { turnId, itemId: call.callId },\n    );\n    if (tool === PRP_COMPLETION_TOOL_NAME || tool === PRP_BLOCK_TOOL_NAME) {\n      const validation = validatePrpStructuredRunResult(call.arguments);\n      if (!validation.ok) throw new Error(\"Invalid semantic result\");\n      if (\n        (tool === PRP_BLOCK_TOOL_NAME &&\n          validation.result.reportedWorkDisposition !== \"blocked\") ||\n        (tool === PRP_COMPLETION_TOOL_NAME &&\n          validation.result.reportedWorkDisposition === \"blocked\")\n      )\n        throw new Error(\n          \"Semantic result disposition does not match the terminal tool\",\n        );\n      if (\n        validation.result.completionClaim.contractRevision !==\n        this.#taskEnvelope.completionContract.revision\n      ) {\n        throw new Error(\n          \"Semantic result completion contract revision does not match\",\n        );\n      }\n      const fingerprint = canonicalJson(validation.result);\n      if (this.#resultFingerprint && this.#resultFingerprint !== fingerprint)\n        throw new Error(\"A different semantic result was already committed\");\n      if (!this.#resultFingerprint) {\n        this.#result = structuredClone(validation.result);\n        this.#resultFingerprint = fingerprint;\n        this.#resultCallId = call.callId;\n        this.#resultTurnId = turnId;","sourceCodeStart":880,"sourceCodeEnd":916,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/opencode/opencode-server-driver.ts#L880-L916","documentation":"The block tool (`prp_block`) must carry `reportedWorkDisposition === \"blocked\"` and the completion tool (`prp_completion`) must not. If the agent's structured result disposition contradicts which terminal tool it called, the driver rejects it. This keeps the machine-readable result consistent with the terminal action the agent took.","triggerScenarios":"Agent calls `prp_completion` with `reportedWorkDisposition: \"blocked\"`, or calls `prp_block` with a disposition other than `\"blocked\"` (e.g. `\"completed\"`), after passing basic schema validation.","commonSituations":"The agent blocked on a missing dependency but picked the completion tool by mistake; prompt instructions describing the two tools are ambiguous or contradictory; a model copies a template from the other tool.","solutions":["Retry the turn with explicit instruction: blocked work must use `prp_block` with `reportedWorkDisposition: \"blocked\"`; finished work must use `prp_completion` with a non-blocked disposition.","Check the agent's system instructions/task envelope to confirm the two terminal tools and their dispositions are documented unambiguously.","Inspect the failing call's arguments in the `item.started` event to confirm which mismatch occurred, then correct the agent's decision procedure.","If the agent genuinely finished despite claiming blocked (or vice versa), clarify the work state and have it re-emit the correct terminal tool call."],"exampleFix":"// before\nprp_completion({ reportedWorkDisposition: \"blocked\", ... }) // mismatch\n\n// after\nprp_block({ reportedWorkDisposition: \"blocked\", summary: \"waiting on API credentials\", ... })","handlingStrategy":"validation","validationCode":"const ok = (tool === 'prp_block') === (result.reportedWorkDisposition === 'blocked');\nif (!ok) throw new Error('disposition/tool mismatch');","typeGuard":"function dispositionMatchesTool(tool, result) {\n  return tool === 'prp_block' ? result.reportedWorkDisposition === 'blocked'\n       : result.reportedWorkDisposition !== 'blocked';\n}","tryCatchPattern":"try {\n  await session.dispatchTool({ tool, callId, arguments });\n} catch (e) {\n  if (e.message.includes('disposition does not match the terminal tool')) {\n    // instruct the agent to re-emit with the matching tool\n  } else throw e;\n}","preventionTips":["Document prp_block vs prp_completion dispositions explicitly in agent prompts.","Give the agent a decision rule: blocked => prp_block, otherwise prp_completion.","Review failing calls' arguments to correct model behavior.","Add prompt examples for both terminal tools."],"tags":["semantic-result","disposition-mismatch","opencode","agent-output"],"backgroundTag":"invalid-enum-value","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}