mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
fix(artifacts): retain original I/O failure evidence
This commit is contained in:
@@ -256,6 +256,10 @@ File-error wrappers retain the format owner's specific diagnostic and recovery
|
||||
text. Strict JSON file input, for example, keeps the byte-order-mark explanation
|
||||
and UTF-8 recovery through CLI/MCP projection rather than replacing it with
|
||||
generic invalid-file advice. Consumers do not classify parser message text.
|
||||
When an artifact boundary falls back to an I/O error, retain the original cause
|
||||
on the typed error so provider callers can inspect its filesystem evidence.
|
||||
Keep public error fields under the canonical projector's ownership; retaining
|
||||
a cause does not require exposing it in transport output.
|
||||
|
||||
Update canonical contracts, output schemas, examples, generated artifacts, and
|
||||
relevant docs together. Verify converted advertised schemas and success,
|
||||
|
||||
@@ -37,5 +37,7 @@ export const translateArtifactFailure = (
|
||||
}
|
||||
// Keep typed Evidence and other analysis failures instead of reducing them to I/O.
|
||||
if (cause instanceof AnalysisError) return cause;
|
||||
return new ArtifactOperationError(operation, "io");
|
||||
return new ArtifactOperationError(operation, "io", undefined, undefined, {
|
||||
cause,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -125,6 +125,23 @@ it("preserves a dyld read failure when the same read observes cancellation", asy
|
||||
expect(fault.handles.every((handle) => handle.fd === -1)).toBe(true);
|
||||
});
|
||||
|
||||
it("retains the original dyld I/O cause through provider translation", async () => {
|
||||
const { client } = await fixture();
|
||||
const cause = Object.assign(new Error("controlled dyld EIO"), {
|
||||
code: "EIO",
|
||||
path: fault.path,
|
||||
});
|
||||
fault.readFailure = cause;
|
||||
const result = await client.execute("trace_dylib_resolution", {
|
||||
roots: ["Main"],
|
||||
});
|
||||
if (result.ok) throw new Error("Expected failed dyld read");
|
||||
expect(result.error).toMatchObject({ reason: "io" });
|
||||
expect(result.error.cause).toBe(cause);
|
||||
expect(fault.handles.every((handle) => handle.fd === -1)).toBe(true);
|
||||
await expect(client.close()).resolves.toMatchObject({ ok: true });
|
||||
});
|
||||
|
||||
it("observes cancellation during the final dyld digest close", async () => {
|
||||
const { trace } = await fixture();
|
||||
fault.abortClose = 2;
|
||||
@@ -382,15 +399,31 @@ it("waits for an in-flight final dyld close before completing provider close", a
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps post-admission pathname disappearance as an integrity failure through the provider", async () => {
|
||||
const { client } = await fixture();
|
||||
fault.onRead = async () => {
|
||||
fault.onRead = undefined;
|
||||
await rename(fault.path, `${fault.path}.old`);
|
||||
};
|
||||
const result = await client.execute("trace_dylib_resolution", {
|
||||
roots: ["Main"],
|
||||
});
|
||||
expect(result).toMatchObject({ ok: false, error: { reason: "integrity" } });
|
||||
expect(fault.handles.every((handle) => handle.fd === -1)).toBe(true);
|
||||
});
|
||||
it.each(["image", "digest"] as const)(
|
||||
"retains the path and filesystem cause when the pathname disappears during the dyld %s read",
|
||||
async (stage) => {
|
||||
const { client } = await fixture();
|
||||
fault.onRead = async () => {
|
||||
if (fault.handles.length !== (stage === "image" ? 1 : 2)) return;
|
||||
fault.onRead = undefined;
|
||||
await rename(fault.path, `${fault.path}.old`);
|
||||
};
|
||||
const result = await client.execute("trace_dylib_resolution", {
|
||||
roots: ["Main"],
|
||||
});
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
error: {
|
||||
reason: "integrity",
|
||||
detail: expect.stringContaining(fault.path),
|
||||
cause: {
|
||||
reason: "integrity",
|
||||
cause: { code: "ENOENT", path: fault.path },
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(fault.handles).toHaveLength(stage === "image" ? 1 : 2);
|
||||
expect(fault.handles.every((handle) => handle.fd === -1)).toBe(true);
|
||||
await expect(client.close()).resolves.toMatchObject({ ok: true });
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user