can1357/oh-my-pi · error
Draft ${draft.round} has not been reviewed yet. End this tur
Error message
Draft ${draft.round} has not been reviewed yet. End this turn; the review turn arrives next, and you approve there. What it means
`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.
Source
Thrown at packages/coding-agent/src/compress/protocol.ts:161
losses: losses.map(loss => ({ content: loss.content, reason: loss.reason })),
};
this.#drafts.push(draft);
this.#approved = false;
this.#verdict = undefined;
return draft;
}
/**
* Accept the newest draft and return it.
*
* Throws when no draft exists, or when the newest draft has not been shown back
* to the agent for a verdict — approval is only meaningful after that review.
*/
accept(verdict: string): CompressDraft {
const draft = this.latest;
if (!draft) throw new Error("Call rewrite before approve: there is no draft to accept");
if (draft.round > this.#reviewed) {
throw new Error(
`Draft ${draft.round} has not been reviewed yet. End this turn; the review turn arrives next, and you approve there.`,
);
}
this.#approved = true;
this.#verdict = verdict;
return draft;
}
/** Tool that records a draft. Thin adapter over {@link submit}. */
rewriteTool(): ToolDefinition {
return {
name: "rewrite",
label: "Rewrite",
description: rewriteDescription.trim(),
parameters: rewriteSchema,
approval: "read",
strict: true,
execute: async (_toolCallId, rawParams) => {View on GitHub (pinned to 9690622007)
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
Example fix
// before
const draft = protocol.submit(text, losses);
protocol.accept("ok"); // throws: draft 1 not reviewed
// after
const draft = protocol.submit(text, losses);
// command loop shows metrics + losses back to the agent, then:
protocol.markReviewed(draft.round);
protocol.accept("losses are acceptable"); Defensive patterns
Strategy: try-catch
Validate before calling
const draft = protocol.latest;
if (draft && draft.round > highestReviewed) throw new Error("End the turn; approve in the review turn"); Type guard
function isReviewed(p: CompressProtocol): boolean {
const d = p.latest;
return d !== undefined && d.round <= p.reviewedCount;
} Try / catch
try {
protocol.accept(verdict);
} catch (err) {
if (err instanceof Error && err.message.includes("has not been reviewed yet")) {
// defer approval to the next (review) turn
} else throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Call rewrite before approve: there is no draft to accept
- Replacement text is not valid UTF-8: {err}
- invalid glob `{pattern}`: {error}
- Cannot continue from message role: assistant
- V2 compaction expected exactly one compaction output item, g
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/3f39b17f9d01ee54.
Report an issue: GitHub.