fix: recover missing Reasonix sessions

This commit is contained in:
liujing
2026-07-17 17:02:49 +08:00
parent fb39ab289a
commit 717d4344ee
6 changed files with 179 additions and 8 deletions
+63
View File
@@ -267,6 +267,69 @@ echo '{"session_id":"fake-session-123","conversation_id":"fake-conv-456"}'
.toBe("initial_continuation");
});
it("recovers a missing Reasonix handle for a blocked fix cycle", async () => {
const { retryExecuteWorkflow } = await import("./workflow-engine.js");
const { gitHead, initWorkflowState, readState, writeState } = await import("./workflow-state.js");
const runId = "reasonix-missing-handle";
const dir = runDir(repoRoot, runId);
const projectKey = path.resolve(repoRoot).replaceAll(path.sep, "-");
const sessionsDir = path.join(process.env.HOME!, ".reasonix", "projects", projectKey, "sessions");
const sessionFile = path.join(sessionsDir, "matching-session.jsonl");
fs.mkdirSync(sessionsDir, { recursive: true });
fs.writeFileSync(sessionFile, [
JSON.stringify({ role: "system", content: `Current workspace: "${path.dirname(repoRoot)}"` }),
JSON.stringify({ role: "user", content: "# Implementation Task: Test" }),
].join("\n"));
fs.writeFileSync(`${sessionFile}.meta`, "{}");
const fakeReasonix = path.join(tmpDir, "fake-reasonix-resume");
fs.writeFileSync(fakeReasonix, "#!/bin/bash\nexit 0\n", { mode: 0o755 });
const startedAt = new Date(Date.now() - 5000).toISOString();
const state = initWorkflowState({
runId,
repoRoot,
baselineHead: gitHead(repoRoot),
plan: plan(),
executor: "reasonix",
maxCycles: 3,
});
state.status = "blocked";
state.stopDescription = "No session handle available for resume.";
state.currentCycle = 2;
state.cycles = [
{
cycleIndex: 1,
startedAt,
completedAt: new Date().toISOString(),
decisionFile: "cycle-1-decision.json",
decisionOutcome: "fix",
},
{ cycleIndex: 2, startedAt: new Date().toISOString() },
];
state.executorConfig = { binary: fakeReasonix };
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path.join(dir, "cycle-1-decision.json"), JSON.stringify({
version: "1",
outcome: "fix",
findingDecisions: [],
verificationEvidence: [],
decidedAt: new Date().toISOString(),
}));
writeState(dir, state);
await retryExecuteWorkflow({ runId });
const recovered = readState(dir)!;
expect(recovered.status).toBe("awaiting_review");
expect(recovered.sessionHandle).toEqual({ kind: "reasonix", sessionFilePath: sessionFile });
const events = fs.readFileSync(path.join(dir, "events.jsonl"), "utf8")
.trim().split("\n").map((line) => JSON.parse(line));
expect(events.find((event) => event.kind === "executor_session_rebound")?.data).toEqual({
sessionFilePath: sessionFile,
recovery: "reasonix_project_session",
});
});
it("rebinds a validated Claude session before retrying the failed cycle", async () => {
const { retryExecuteWorkflow } = await import("./workflow-engine.js");
const { gitHead, initWorkflowState, readState, writeState } = await import("./workflow-state.js");