{"record":{"id":"3f39b17f9d01ee54","repo":"can1357/oh-my-pi","slug":"draft-draft-round-has-not-been-reviewed-yet-en","errorCode":null,"errorMessage":"Draft ${draft.round} has not been reviewed yet. End this turn; the review turn arrives next, and you approve there.","messagePattern":"Draft (.+?) has not been reviewed yet\\. End this turn; the review turn arrives next, and you approve there\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/compress/protocol.ts","lineNumber":161,"sourceCode":"\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\",\n\t\t\tstrict: true,\n\t\t\texecute: async (_toolCallId, rawParams) => {","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/compress/protocol.ts#L143-L179","documentation":"`CompressProtocol.accept()` throws this when the newest draft's round number exceeds the highest round marked reviewed via `markReviewed()`. The protocol deliberately separates the turn that produces a draft from the turn that approves it, so the agent reviews measured size and declared losses before certifying its own work. Self-approval inside the producing turn is rejected.","triggerScenarios":"Calling `approve` (or `accept`) in the same turn that just called `rewrite`, without the command loop having called `markReviewed(round)` for the new draft first.","commonSituations":"A model tries to rewrite and approve back-to-back within one turn; a custom driver calls `accept()` immediately after `submit()` without simulating the review turn; a loop forgets to call `markReviewed` after echoing draft metrics back to the agent.","solutions":["End the current turn after `rewrite`; the command loop sends a review turn next, and approval happens there","Ensure the driver calls `protocol.markReviewed(draft.round)` after showing the draft back to the agent","Retry the approve call in the following turn once the review message has been delivered"],"exampleFix":"// before\nconst draft = protocol.submit(text, losses);\nprotocol.accept(\"ok\"); // throws: draft 1 not reviewed\n// after\nconst draft = protocol.submit(text, losses);\n// command loop shows metrics + losses back to the agent, then:\nprotocol.markReviewed(draft.round);\nprotocol.accept(\"losses are acceptable\");","handlingStrategy":"try-catch","validationCode":"const draft = protocol.latest;\nif (draft && draft.round > highestReviewed) throw new Error(\"End the turn; approve in the review turn\");","typeGuard":"function isReviewed(p: CompressProtocol): boolean {\n  const d = p.latest;\n  return d !== undefined && d.round <= p.reviewedCount;\n}","tryCatchPattern":"try {\n  protocol.accept(verdict);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"has not been reviewed yet\")) {\n    // defer approval to the next (review) turn\n  } else throw err;\n}","preventionTips":["Always call `markReviewed(draft.round)` after echoing draft metrics back to the agent","Never approve in the same turn that produced the draft","Model the protocol as an explicit state machine: draft → reviewed → approved"],"tags":["protocol","state-machine","review-gate"],"backgroundTag":"approval-before-review","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}