mirror of
https://github.com/morluto/rea.git
synced 2026-11-13 10:12:50 +00:00
fix(ghidra): preserve lifecycle ownership and failure provenance
Refs #4502, #4506, #4508, #4518, #4522, #4526. Retain snapshot, endpoint, launch rollback and compiler cleanup owners. Join request finalization and cleanup cancellation. Preserve bounded compiler output and original byte counts through capture and Ghidra failures. Normalize execFile cancellation once at its producer. Validation includes real macOS Ghidra TCP MCP success workflows, controlled Node compiler/process fault fixtures, focused regressions and static checks. Native Swift compiler faults, Windows execution and stdio transport were not verified. Broader startup primary classification in #4517 and native adapter propagation remain unfinished.
This commit is contained in:
1 parent
5f9ac2c98a
commit
9d940c2f28
29 files changed
+3340
-578
No files matched your search
@@ -12,7 +12,6 @@ interface ProcessCaptureErrorOptions extends AnalysisErrorOptions {
|
||||
readonly userMessage?: string;
|
||||
readonly userCategory?: "cancelled";
|
||||
readonly reason?: "capture_failed" | "cleanup_incomplete" | "cancelled";
|
||||
readonly cleanupResources?: readonly string[];
|
||||
readonly executionFailure?: string;
|
||||
readonly partialObservation?: PartialProcessCaptureObservation;
|
||||
readonly cleanupReport?: ProcessCaptureCleanupReport;
|
||||
@@ -25,8 +24,6 @@ export class ProcessCaptureError extends AnalysisError {
|
||||
override readonly userMessage: string | undefined;
|
||||
override readonly userCategory: "cancelled" | undefined;
|
||||
readonly reason: NonNullable<ProcessCaptureErrorOptions["reason"]>;
|
||||
override readonly cleanupIncomplete: boolean;
|
||||
override readonly cleanupResources: readonly string[];
|
||||
override readonly executionFailure: string | undefined;
|
||||
override readonly partialObservation:
|
||||
| PartialProcessCaptureObservation
|
||||
@@ -40,12 +37,58 @@ export class ProcessCaptureError extends AnalysisError {
|
||||
this.reason =
|
||||
options?.reason ??
|
||||
(options?.userCategory === "cancelled" ? "cancelled" : "capture_failed");
|
||||
this.cleanupIncomplete = this.reason === "cleanup_incomplete";
|
||||
this.cleanupResources = options?.cleanupResources ?? [];
|
||||
this.cleanupIncomplete ||= this.reason === "cleanup_incomplete";
|
||||
this.executionFailure = options?.executionFailure ?? message;
|
||||
this.partialObservation = options?.partialObservation;
|
||||
this.cleanupReport = options?.cleanupReport;
|
||||
}
|
||||
|
||||
/** Add final capture observations while preserving primary facts and cleanup ownership. */
|
||||
withFinalization(
|
||||
options: Pick<
|
||||
ProcessCaptureErrorOptions,
|
||||
"executionFailure" | "partialObservation" | "cleanupReport"
|
||||
> & { readonly cleanupFailure?: AnalysisError },
|
||||
): ProcessCaptureError {
|
||||
const secondary = options.cleanupFailure;
|
||||
const resources = [
|
||||
...new Set([
|
||||
...this.cleanupResources,
|
||||
...(secondary?.cleanupResources ?? []),
|
||||
]),
|
||||
];
|
||||
const executionFailure = this.executionFailure ?? options.executionFailure;
|
||||
const partialObservation =
|
||||
this.partialObservation ?? options.partialObservation;
|
||||
const cleanupReport = this.cleanupReport ?? options.cleanupReport;
|
||||
return new ProcessCaptureError(this.message, {
|
||||
...(Object.hasOwn(this, "cause") ? { cause: this.cause } : {}),
|
||||
...(this.userMessage === undefined
|
||||
? {}
|
||||
: { userMessage: this.userMessage }),
|
||||
...(this.userCategory === undefined
|
||||
? {}
|
||||
: { userCategory: this.userCategory }),
|
||||
reason: this.reason,
|
||||
...(this.diagnostics === undefined
|
||||
? {}
|
||||
: { diagnostics: this.diagnostics }),
|
||||
...(this.capturedOutput === undefined
|
||||
? {}
|
||||
: { capturedOutput: this.capturedOutput }),
|
||||
...(secondary === undefined
|
||||
? this.cleanup === undefined
|
||||
? {}
|
||||
: { cleanup: this.cleanup }
|
||||
: { cleanup: { reason: secondary.message, resources } }),
|
||||
...(executionFailure === undefined ? {} : { executionFailure }),
|
||||
...(partialObservation === undefined ? {} : { partialObservation }),
|
||||
...(cleanupReport === undefined ? {} : { cleanupReport }),
|
||||
}).bindCleanupDependencies([
|
||||
this,
|
||||
...(secondary === undefined ? [] : [secondary]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return one non-recursive, domain-neutral summary of an execution failure. */
|
||||
@@ -73,26 +116,26 @@ export const normalizeCaptureFailure = (
|
||||
): unknown => {
|
||||
if (cause instanceof ProcessCaptureError) return cause;
|
||||
if (cause instanceof DarwinProcessOwnershipInspectionError) {
|
||||
const compiler = cause.compilerOutcome;
|
||||
return new ProcessCaptureError(cause.message, {
|
||||
const normalized = new ProcessCaptureError(cause.message, {
|
||||
cause,
|
||||
...(compiler === undefined
|
||||
...(cause.cancellation === undefined
|
||||
? {}
|
||||
: { userCategory: "cancelled" as const, reason: "cancelled" as const }),
|
||||
...(cause.cleanupFailure === undefined
|
||||
? {}
|
||||
: {
|
||||
capturedOutput: {
|
||||
stdout: "",
|
||||
stderr: compiler.stderr,
|
||||
truncated: compiler.stderrTruncated || compiler.outputTruncated,
|
||||
stderr_bytes: compiler.stderrBytes,
|
||||
exit_code:
|
||||
compiler.outcome === "exited" &&
|
||||
typeof compiler.code === "number"
|
||||
? compiler.code
|
||||
: null,
|
||||
signal: compiler.signal,
|
||||
cleanup: {
|
||||
reason: cause.cleanupFailure.message,
|
||||
resources: cause.cleanupFailure.cleanupResources,
|
||||
},
|
||||
}),
|
||||
...(cause.capturedOutput === undefined
|
||||
? {}
|
||||
: { capturedOutput: cause.capturedOutput }),
|
||||
});
|
||||
return cause.cleanupFailure === undefined
|
||||
? normalized
|
||||
: normalized.bindCleanupDependencies([cause.cleanupFailure]);
|
||||
}
|
||||
if (
|
||||
signal?.aborted === true &&
|
||||
|
||||
Reference in new issue
Block a user