fix(cutter): retain completion-read failures during cleanup retry

This commit is contained in:
morluto
2026-10-24 04:00:10 +08:00
parent ef00b2115a
commit 60a0acb396
3 changed files with 99 additions and 1 deletions
+3
View File
@@ -135,6 +135,9 @@ block new commands until they can be retried successfully. Status proof failures
identify `cutter_command` and retain `status` in
`details.diagnostics.auxiliary_operation`; malformed proof still blocks dispatch.
Descriptor failures likewise retain the requesting operation through cleanup.
If a completion-proof read fails while an earlier command is unresolved, retry
admission preserves both errors and their independently owned resources. Releasing
one owner does not settle the other, and the command is not replayed to recover proof.
Command admission reports typed cancellation and treats the caller's abort reason
as opaque metadata, without inferring cleanup ownership from it. A generation
mismatch is an input correction only when the authenticated reply confirms
+9 -1
View File
@@ -73,7 +73,15 @@ export class CutterBridgeService {
requestId,
pending.completionToken,
);
if (!receipt.ok || !receipt.value) return err(pending.failure);
if (!receipt.ok)
return err(
analysisErrorWithCleanupFailure(
pending.failure,
receipt.error,
"cutter_command",
).bindCleanupDependencies([pending.failure, receipt.error]),
);
if (!receipt.value) return err(pending.failure);
const { failure } = pending;
failure.resolveCleanup();
this.#pendingCleanup.delete(requestId);
@@ -13,6 +13,93 @@ const cutterInput = {
command: "pd 4",
};
it.each(["descriptor-first", "command-first"] as const)(
"retains receipt-read cleanup alongside pending command ownership: %s",
async (order) => {
const descriptor = new ProviderCleanupError(
"cutter",
["descriptor-fixture"],
{ reason: "controlled receipt descriptor close failure" },
{ operation: "cutter_command" },
);
let readFails = true;
let terminal = false;
const execute = vi.fn(async () => ({
requestId: "4b18ce4d-bf31-456e-ae31-e8c547b2f9db",
output: null,
currentFile: null,
documentGeneration: 7,
identityStatus: "partial" as const,
cutterVersion: null,
executionState: "unknown" as const,
error: "missing-reply",
message: "completion remains unknown",
outputTruncated: false,
}));
const service = new CutterBridgeService({
listSessions: async () => ({
sessions: [],
bridge_directory: "/fixture",
discovery_status: "no_live_bridge_found",
bridge_directory_security: "private_verified",
}),
confirmCommandCompletion: async () =>
readFails
? { ok: false, error: descriptor }
: { ok: true, value: terminal },
execute,
});
const dispatched = await service.execute(cutterInput);
if (dispatched.ok) throw new Error("Expected pending completion");
const retried = await service.retryCleanup();
if (retried.ok) throw new Error("Expected receipt-read failure");
expect(retried.error.cleanupResources).toEqual(
expect.arrayContaining([
"descriptor-fixture",
`cutter-session:${cutterInput.session_id}`,
]),
);
expect(retried.error.partialObservation).toBe(
dispatched.error.partialObservation,
);
expect(retried.error.diagnostics).toMatchObject({
cleanup_error: {
details: {
diagnostics: {
reason: "controlled receipt descriptor close failure",
},
},
},
});
if (order === "descriptor-first") {
descriptor.resolveCleanup();
expect(retried.error.cleanupResources).toEqual([
`cutter-session:${cutterInput.session_id}`,
]);
readFails = false;
expect(await service.retryCleanup()).toEqual({
ok: false,
error: dispatched.error,
});
expect(retried.error.cleanupIncomplete).toBe(true);
terminal = true;
expect(await service.retryCleanup()).toEqual({ ok: true, value: null });
} else {
readFails = false;
terminal = true;
expect(await service.retryCleanup()).toEqual({ ok: true, value: null });
expect(dispatched.error.cleanupIncomplete).toBe(false);
expect(retried.error.cleanupIncomplete).toBe(true);
expect(retried.error.cleanupResources).toEqual(["descriptor-fixture"]);
descriptor.resolveCleanup();
}
expect(retried.error.cleanupIncomplete).toBe(false);
expect(retried.error.cleanupResources).toEqual([]);
expect(execute).toHaveBeenCalledTimes(1);
await service.close();
},
);
it("retains uncertain command completion as Evidence without encouraging a retry", async () => {
const service = new CutterBridgeService({
listSessions: async () => ({