fix(pi-workflow): reset failed verification on retry
This commit is contained in:
@@ -70,7 +70,7 @@ An analysis run uses `action: "start"` with `workflow: { mode: "analysis-script"
|
||||
|
||||
## Recovery
|
||||
|
||||
Interrupted work opens a Recovery Gate with retry, inspect, and abort choices. Completed Assignments and Decisions remain persisted. Potentially mutating interrupted Assignments are preserved and are not replayed automatically; an explicit retry creates newly fenced work where policy permits it. Startup recovery leaves an idle run already waiting at Recovery under its current parent instead of letting another Pi session claim it. If that parent's process has exited, an explicit retry from a previously attached parent session may reclaim the stale lease before continuing.
|
||||
Interrupted work opens a Recovery Gate with retry, inspect, and abort choices. Completed Assignments and Decisions remain persisted. Potentially mutating interrupted Assignments are preserved and are not replayed automatically; an explicit retry creates newly fenced work where policy permits it. Startup recovery leaves an idle run already waiting at Recovery under its current parent instead of letting another Pi session claim it. If that parent's process has exited, an explicit retry from a previously attached parent session may reclaim the stale lease before continuing. Retry clears prior failed-command output and reruns verification from a pending state.
|
||||
|
||||
## Artifacts
|
||||
|
||||
|
||||
@@ -519,6 +519,7 @@ On process or session interruption:
|
||||
- Active Assignments become `interrupted` idempotently.
|
||||
- Potentially mutating Assignments are never replayed automatically.
|
||||
- The run opens a Recovery Gate offering retry, inspect, or abort.
|
||||
- Explicit retry resets failed verification commands to pending and clears their prior output before host execution.
|
||||
- Completed Assignments and accepted Decisions remain intact.
|
||||
|
||||
## Child Execution
|
||||
|
||||
@@ -1512,9 +1512,20 @@ test("Recovery retry reruns failed verification after the remediation limit", as
|
||||
await withTempDir(async (directory) => {
|
||||
const setup = await fixture(directory);
|
||||
let commandRuns = 0;
|
||||
let runId: string | undefined;
|
||||
const hostCommands: HostCommandExecutor = {
|
||||
async run() {
|
||||
commandRuns++;
|
||||
if (commandRuns === 2) {
|
||||
assert.ok(runId);
|
||||
const retrying = await setup.store.loadRun(runId);
|
||||
const [command] = retrying.verificationEvidence?.commands ?? [];
|
||||
assert.equal(command?.status, "running");
|
||||
assert.equal(command?.stdout, undefined);
|
||||
assert.equal(command?.stderr, undefined);
|
||||
assert.equal(command?.exitCode, undefined);
|
||||
assert.equal(command?.error, undefined);
|
||||
}
|
||||
return {
|
||||
result: {
|
||||
code: commandRuns === 1 ? 1 : 0,
|
||||
@@ -1552,6 +1563,7 @@ test("Recovery retry reruns failed verification after the remediation limit", as
|
||||
hostCommands,
|
||||
effectiveConfig,
|
||||
}).execute(startCommand());
|
||||
runId = planned.runId;
|
||||
const approved = await setup.engine("session-1", {
|
||||
hostCommands,
|
||||
effectiveConfig,
|
||||
@@ -1598,6 +1610,7 @@ test("Recovery retry reruns failed verification after the remediation limit", as
|
||||
const liveStolen = structuredClone(untouched);
|
||||
liveStolen.revision++;
|
||||
liveStolen.parentSessionIds.push("transient-session");
|
||||
liveStolen.verificationEvidence!.commands[0]!.error = "stale host exception";
|
||||
liveStolen.lease = {
|
||||
processId: 200,
|
||||
activeParentSessionId: "transient-session",
|
||||
|
||||
@@ -4279,6 +4279,10 @@ export class WorkflowEngine {
|
||||
run.verificationEvidence?.commands.filter(
|
||||
(command) => command.status === "running" || command.status === "interrupted",
|
||||
) ?? [];
|
||||
const failedCommands =
|
||||
run.verificationEvidence?.commands.filter(
|
||||
(command) => command.status === "failed",
|
||||
) ?? [];
|
||||
run = await this.transition(run, (next) => {
|
||||
for (const assignment of next.assignments) {
|
||||
if (
|
||||
@@ -4309,6 +4313,21 @@ export class WorkflowEngine {
|
||||
next.script.error = undefined;
|
||||
}
|
||||
if (next.verificationEvidence) {
|
||||
for (const command of failedCommands) {
|
||||
const persisted = next.verificationEvidence.commands.find(
|
||||
(candidate) => candidate.id === command.id,
|
||||
);
|
||||
if (!persisted) continue;
|
||||
persisted.status = "pending";
|
||||
persisted.exitCode = undefined;
|
||||
persisted.stdout = undefined;
|
||||
persisted.stderr = undefined;
|
||||
persisted.error = undefined;
|
||||
}
|
||||
if (failedCommands.length > 0) {
|
||||
next.verificationEvidence.focusedBehavioralTests = false;
|
||||
next.verificationEvidence.verifierSucceeded = false;
|
||||
}
|
||||
for (const command of interruptedCommands) {
|
||||
const persisted = next.verificationEvidence.commands.find(
|
||||
(candidate) => candidate.id === command.id,
|
||||
|
||||
Reference in New Issue
Block a user