fix(auto-reply): skip replay when retained window is assistant-only

Codex follow-up review on #70898: the `startIdx < kept.length - 1`
guard still wrote an assistant-first transcript when every turn inside
the retained window was an assistant (e.g. a tail of consecutive
assistant messages). That re-creates the exact role-ordering hazard the
reset path is recovering from.

Advance through the full window and, if no user turn remains, return 0
without touching the target transcript. Missing a few turns of DM
continuity is strictly better than re-triggering the conflict.

Locked in with a new assertion on the existing unit test.

Made-with: Cursor
This commit is contained in:
Neerav Makwana
2026-04-23 23:30:51 -04:00
committed by Josh Lehman
parent b3731e2200
commit 339d3759b0
2 changed files with 15 additions and 1 deletions

View File

@@ -47,6 +47,15 @@ describe("replayRecentUserAssistantMessages", () => {
expect(["user", "assistant"]).toContain(r.message.role);
}
expect(await call(path.join(root, "missing.jsonl"), path.join(root, "out.jsonl"))).toBe(0);
const assistantSource = path.join(root, "all-assistant.jsonl");
const assistantTarget = path.join(root, "all-assistant-out.jsonl");
const onlyAssistants = Array.from({ length: 3 }, () =>
j({ message: { role: "assistant", content: "x" } }),
).join("");
await fs.writeFile(assistantSource, onlyAssistants, "utf8");
expect(await call(assistantSource, assistantTarget)).toBe(0);
await expect(fs.stat(assistantTarget)).rejects.toThrow();
});
it("skips header for pre-existing targets and aligns the tail to a user turn", async () => {

View File

@@ -47,9 +47,14 @@ export async function replayRecentUserAssistantMessages(params: {
return 0;
}
let startIdx = Math.max(0, kept.length - max);
while (startIdx < kept.length - 1 && kept[startIdx].role === "assistant") {
while (startIdx < kept.length && kept[startIdx].role === "assistant") {
startIdx += 1;
}
if (startIdx === kept.length) {
// Retained window is assistant-only; replaying would re-create the same
// role-ordering hazard this reset path is recovering from.
return 0;
}
const tail = kept.slice(startIdx).map((entry) => entry.line);
if (!fs.existsSync(params.targetTranscript)) {
await fsp.mkdir(path.dirname(params.targetTranscript), { recursive: true });