fix: a finished all-run is not a run to resume
`/pygienium-all` loaded the previous run-state and skipped every check whose entry was terminal, so a second invocation dispatched nothing at all. The one check that did run was whichever an interrupted run had left non-terminal — registry order made that `comments`, which read as "comments ran, everything after it was skipped". Resumability belongs to unfinished work: continue only when the stored run is not complete (`in_progress`, `partial`, `failed`), so completed checks are carried forward cheaply and the interrupted one picks up where it stopped. A completed run means the next invocation is a new scan, which is what the command name promises; `--fresh` and `/pygienium-resume` keep their explicit jobs. Settle a terminal check's phases on the way past too: an interrupted run can leave a phase `in_progress` behind a check that reads `complete`, which made the state claim both finished and still running. Tests: reverted src/ fails "a completed run is not resumed", "settles a phase an interrupted run left in_progress", and the integration test "an unfinished run-state is continued, not restarted"; all pass with the fix. Suite 164 pass, 1 pre-existing worktree-only failure (MISC-46). bun run typecheck clean; port-to-omp regenerates and typechecks. MISC-47 Co-Authored-By: nw-flash-flex <noreply@neuralwatt.com>
This commit is contained in:
@@ -22,7 +22,7 @@ import {
|
||||
type AgentRunner,
|
||||
} from "../src/agent-runner.js";
|
||||
import { handleAllCommand, type PygieniumCtx } from "../src/commands.js";
|
||||
import { loadRunState } from "../src/run-state.js";
|
||||
import { loadRunState, markCheckStatus, markRunStatus, saveRunState } from "../src/run-state.js";
|
||||
import { canonicalChecksRoot } from "../src/export.js";
|
||||
|
||||
/** Stub check whose fake-runner task writes artifacts + echoes a line. */
|
||||
@@ -147,16 +147,27 @@ describe("/pygienium-all end-to-end (task 14)", () => {
|
||||
expect(out.join("\n")).toContain("no checks registered");
|
||||
});
|
||||
|
||||
it("is resumable: a second call reuses the existing run-state", async () => {
|
||||
it("is resumable: an unfinished run-state is continued, not restarted", async () => {
|
||||
registerCheck(fakeCheck("alpha"));
|
||||
// A completed run is finished work: the next call starts a new run-state.
|
||||
await captureStdout(() => handleAllCommand("--fix", stubCtx(cwd)));
|
||||
const first = await loadRunState(cwd);
|
||||
const firstStarted = first!.startedAt;
|
||||
const completed = await loadRunState(cwd);
|
||||
expect(completed!.status).toBe("complete");
|
||||
await captureStdout(() => handleAllCommand("--fix", stubCtx(cwd)));
|
||||
const fresh = await loadRunState(cwd);
|
||||
expect(fresh!.startedAt).toBeGreaterThan(completed!.startedAt);
|
||||
|
||||
// An unfinished one is continued: same run, same startedAt, and the
|
||||
// completed check is not dispatched again.
|
||||
const interrupted = await loadRunState(cwd);
|
||||
markRunStatus(interrupted!, "in_progress");
|
||||
markCheckStatus(interrupted!, "alpha", "in_progress");
|
||||
await saveRunState(interrupted!);
|
||||
const before = (await loadRunState(cwd))!.startedAt;
|
||||
|
||||
// Second run reloads the existing run-state (same startedAt).
|
||||
await captureStdout(() => handleAllCommand("--fix", stubCtx(cwd)));
|
||||
const second = await loadRunState(cwd);
|
||||
expect(second!.startedAt).toBe(firstStarted);
|
||||
expect(second!.status).toBe("complete");
|
||||
const resumed = await loadRunState(cwd);
|
||||
expect(resumed!.startedAt).toBe(before);
|
||||
expect(resumed!.status).toBe("complete");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user