{"record":{"id":"a38967f65a7f4774","repo":"can1357/oh-my-pi","slug":"call-rewrite-before-approve-there-is-no-draft-to","errorCode":null,"errorMessage":"Call rewrite before approve: there is no draft to accept","messagePattern":"Call rewrite before approve: there is no draft to accept","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/compress/protocol.ts","lineNumber":159,"sourceCode":"\t\t\tround: this.#drafts.length + 1,\n\t\t\ttext,\n\t\t\tlosses: losses.map(loss => ({ content: loss.content, reason: loss.reason })),\n\t\t};\n\t\tthis.#drafts.push(draft);\n\t\tthis.#approved = false;\n\t\tthis.#verdict = undefined;\n\t\treturn draft;\n\t}\n\n\t/**\n\t * Accept the newest draft and return it.\n\t *\n\t * Throws when no draft exists, or when the newest draft has not been shown back\n\t * to the agent for a verdict — approval is only meaningful after that review.\n\t */\n\taccept(verdict: string): CompressDraft {\n\t\tconst draft = this.latest;\n\t\tif (!draft) throw new Error(\"Call rewrite before approve: there is no draft to accept\");\n\t\tif (draft.round > this.#reviewed) {\n\t\t\tthrow new Error(\n\t\t\t\t`Draft ${draft.round} has not been reviewed yet. End this turn; the review turn arrives next, and you approve there.`,\n\t\t\t);\n\t\t}\n\t\tthis.#approved = true;\n\t\tthis.#verdict = verdict;\n\t\treturn draft;\n\t}\n\n\t/** Tool that records a draft. Thin adapter over {@link submit}. */\n\trewriteTool(): ToolDefinition {\n\t\treturn {\n\t\t\tname: \"rewrite\",\n\t\t\tlabel: \"Rewrite\",\n\t\t\tdescription: rewriteDescription.trim(),\n\t\t\tparameters: rewriteSchema,\n\t\t\tapproval: \"read\",","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/compress/protocol.ts#L141-L177","documentation":"`CompressProtocol.accept()` throws this when `approve` is called before any `rewrite` call has submitted a draft. The compress protocol requires a draft to exist before a verdict can be recorded; with an empty draft ledger there is nothing to approve. It is a strict ordering guard for the two-tool rewrite/approve flow.","triggerScenarios":"Calling `protocol.accept(verdict)` (or the `approve` tool) on a fresh `CompressProtocol` instance before any `rewrite`/`submit()` call has been made.","commonSituations":"An agent skips the rewrite step and goes straight to approve because it misreads the tool descriptions; a driver script invokes approve first; a session is replayed with a protocol instance that lost its draft state.","solutions":["Call the `rewrite` tool (or `protocol.submit(text, losses)`) to record a draft first","Re-run the compress session from the start so the agent produces a draft before approving","Check `protocol.latest` / `protocol.rounds` before calling `accept` to confirm a draft exists"],"exampleFix":"// before\nprotocol.accept(\"looks good\"); // throws: no draft\n// after\nprotocol.submit(compressedText, losses);\nprotocol.markReviewed(1);\nprotocol.accept(\"losses are acceptable\");","handlingStrategy":"validation","validationCode":"if (!protocol.latest) throw new Error(\"Cannot approve before rewrite submits a draft\");","typeGuard":"function hasDraft(p: CompressProtocol): p is CompressProtocol & { latest: CompressDraft } { return p.latest !== undefined; }","tryCatchPattern":"try {\n  protocol.accept(verdict);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"no draft to accept\")) {\n    // submit a rewrite first, then approve\n  } else throw err;\n}","preventionTips":["Check `protocol.rounds > 0` before any approve call","Drive the tools strictly in rewrite → review → approve order","Never expose `approve` to the agent until at least one draft exists"],"tags":["protocol","state-machine","tool-ordering"],"backgroundTag":"tool-called-out-of-order","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}