mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 05:56:19 -10:00
fix(evidence): preserve caller scope in secondary failures
This commit is contained in:
1 parent
68b1d91310
commit
588a4c81a0
2 files changed
+46
-11
No files matched your search
@@ -33,7 +33,10 @@ import {
|
||||
} from "./hopperStartupFailure.js";
|
||||
import { ProviderAdapterError } from "./providerAdapterError.js";
|
||||
import { UnknownRegistryError } from "./unknownRegistryError.js";
|
||||
import { projectAnalysisError } from "./analysisErrorProjection.js";
|
||||
import {
|
||||
childFailureDiagnostics,
|
||||
projectAnalysisError,
|
||||
} from "./analysisErrorProjection.js";
|
||||
import {
|
||||
analysisErrorRemediationAction,
|
||||
analysisErrorUserMessage,
|
||||
@@ -1530,6 +1533,24 @@ describe("analysis error projection: composed failures for a caller lifetime", (
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps caller lifetime for a secondary failure embedded in child diagnostics", () => {
|
||||
const primary = new AnalysisInputError("read_bytes", undefined, []);
|
||||
const secondary = new AnalysisTimeoutError("search_strings", 250, {
|
||||
limit: analysisSettingLimit("regex.matching_deadline_ms", 250, "setting"),
|
||||
continuingCallerGuidance: continuingOnly,
|
||||
});
|
||||
primary.retainSecondaryFailure(secondary);
|
||||
const history = childFailureDiagnostics({
|
||||
primary_error: { error: primary },
|
||||
});
|
||||
expect(history.diagnostics?.primary_error).toEqual(
|
||||
projectAnalysisError(primary, "continuing"),
|
||||
);
|
||||
expect(history.invocationDiagnostics?.primary_error).toEqual(
|
||||
projectAnalysisError(primary, "invocation"),
|
||||
);
|
||||
});
|
||||
|
||||
it("records one lifetime form when no child depends on the caller", () => {
|
||||
const composed = analysisErrorWithCleanupFailure(
|
||||
new AnalysisInputError("read_bytes", undefined, [
|
||||
|
||||
@@ -301,8 +301,8 @@ export const childFailureDiagnostics = (
|
||||
entries.map(([key, child]) => [key, project(child, "continuing")]),
|
||||
),
|
||||
};
|
||||
const varying = entries.filter(([, { error }]) =>
|
||||
projectionDependsOnCallerLifetime(error),
|
||||
const varying = entries.filter(([, { error, omitRecovery }]) =>
|
||||
projectionDependsOnCallerLifetime(error, omitRecovery !== true),
|
||||
);
|
||||
return varying.length === 0
|
||||
? { diagnostics: continuing }
|
||||
@@ -317,19 +317,33 @@ export const childFailureDiagnostics = (
|
||||
};
|
||||
};
|
||||
|
||||
/** Every lifetime-dependent input the projection reads lies on this chain. */
|
||||
const projectionDependsOnCallerLifetime = (error: AnalysisError): boolean => {
|
||||
/** Check the primary chain and the secondary failures included in its projection. */
|
||||
const projectionDependsOnCallerLifetime = (
|
||||
error: AnalysisError,
|
||||
includeSecondary: boolean,
|
||||
): boolean => {
|
||||
const chainDepends = (first: AnalysisError): boolean => {
|
||||
for (
|
||||
let node: AnalysisError | undefined = first;
|
||||
node !== undefined;
|
||||
node = node.cleanupPrimary
|
||||
)
|
||||
if (
|
||||
node.limit !== undefined ||
|
||||
node.continuingCallerGuidance !== undefined ||
|
||||
node.invocationDiagnostics !== undefined
|
||||
)
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
if (chainDepends(error)) return true;
|
||||
if (!includeSecondary) return false;
|
||||
for (
|
||||
let node: AnalysisError | undefined = error;
|
||||
node !== undefined;
|
||||
node = node.cleanupPrimary
|
||||
)
|
||||
if (
|
||||
node.limit !== undefined ||
|
||||
node.continuingCallerGuidance !== undefined ||
|
||||
node.invocationDiagnostics !== undefined
|
||||
)
|
||||
return true;
|
||||
if (node.secondaryFailures.some(chainDepends)) return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user