mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 16:26:36 +00:00
After a validated seed, trace_native_values turned a cleanup-incomplete failure from a call-target read or a followed callee dossier into a plain-text unknown, kept dispatching reads, and returned successful Evidence without the cleanup_incomplete status, cleanup reason or owned resources. The trace now classifies child failures with the shared stopsTraversal predicate that call graph traversals use. A cancelled child still ends the trace with its cancellation. A child whose provider cannot confirm cleanup stops the trace: the provider's own error is the outcome, so its resources and bound cleanup retry stay intact, and the work admitted before the stop is returned as the trace's normal shape, with traversal truncated by cleanup_incomplete and a located unknown at the stopping read. That stop stays primary over a concurrent caller abort and over the output-size check. Ordinary child failures remain unknowns. MCP already retains such partials as workflow Evidence. The one-shot CLI dropped them, so runDirectAnalysis now retains the partial workflow Evidence on the failure through the same shared owner. Fixes #8371 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
88 lines
3.4 KiB
TypeScript
88 lines
3.4 KiB
TypeScript
import type { CallRelationshipDirection } from "../contracts/enhancedInputs.js";
|
|
import type {
|
|
AnalysisExecution,
|
|
AnalysisOperation,
|
|
ExecutionOptions,
|
|
} from "./AnalysisProvider.js";
|
|
import type { AnalysisError } from "../domain/analysisErrorBase.js";
|
|
import type { AnalysisOutputError } from "../domain/analysisErrorCore.js";
|
|
import type { JsonValue } from "../domain/jsonValue.js";
|
|
import { parseRelatedAddresses } from "../domain/native/nativeObservation.js";
|
|
import type { Result } from "../domain/result.js";
|
|
|
|
/** The provider read and response relation for each supported traversal direction. */
|
|
export const CALL_RELATIONSHIP_TRAVERSALS = {
|
|
forward: { operation: "procedure_callees", relation: "callees" },
|
|
backward: { operation: "procedure_callers", relation: "callers" },
|
|
} as const satisfies Readonly<
|
|
Record<
|
|
CallRelationshipDirection,
|
|
{
|
|
readonly operation: AnalysisOperation;
|
|
readonly relation: "callees" | "callers";
|
|
}
|
|
>
|
|
>;
|
|
|
|
/** One observed provider operation, as composed workflows dispatch it. */
|
|
export type CallRelationshipObserver = (
|
|
name: AnalysisOperation,
|
|
arguments_: Readonly<Record<string, JsonValue>>,
|
|
signal?: AbortSignal,
|
|
selectorOrigin?: ExecutionOptions["selectorOrigin"],
|
|
) => Promise<Result<AnalysisExecution, AnalysisError>>;
|
|
|
|
/**
|
|
* One procedure's relationship read. An unavailable or unreadable read leaves
|
|
* the rest of the traversal usable; a stopped read must end it.
|
|
*/
|
|
export type CallRelationshipRead =
|
|
| {
|
|
readonly status: "observed";
|
|
readonly execution: AnalysisExecution;
|
|
readonly related: readonly string[];
|
|
}
|
|
| { readonly status: "unavailable"; readonly error: AnalysisError }
|
|
| { readonly status: "unreadable"; readonly error: AnalysisOutputError }
|
|
| { readonly status: "stopped"; readonly error: AnalysisError };
|
|
|
|
/**
|
|
* Whether a failed child read must end a traversal rather than become one of
|
|
* its unknowns. A cancelled read ends it; so does a read whose provider cannot
|
|
* confirm release of resources it owns, because that read has not finished and
|
|
* dispatching more reads would build on that state.
|
|
*/
|
|
export const stopsTraversal = (error: AnalysisError): boolean =>
|
|
error._tag === "AnalysisCancelledError" || error.cleanupIncomplete;
|
|
|
|
/** Read and parse one procedure's direct relationships for a traversal. */
|
|
export const readCallRelationships = async (
|
|
observe: CallRelationshipObserver,
|
|
direction: CallRelationshipDirection,
|
|
procedure: string,
|
|
signal?: AbortSignal,
|
|
): Promise<CallRelationshipRead> => {
|
|
const { operation, relation } = CALL_RELATIONSHIP_TRAVERSALS[direction];
|
|
const execution = await observe(operation, { procedure }, signal, "provider");
|
|
if (!execution.ok)
|
|
return {
|
|
status: stopsTraversal(execution.error) ? "stopped" : "unavailable",
|
|
error: execution.error,
|
|
};
|
|
const related = parseRelatedAddresses(
|
|
execution.value.result,
|
|
relation,
|
|
operation,
|
|
);
|
|
return related.ok
|
|
? { status: "observed", execution: execution.value, related: related.value }
|
|
: { status: "unreadable", error: related.error };
|
|
};
|
|
|
|
/** The admitted traversal's caveat when unconfirmed cleanup stopped it. */
|
|
export const cleanupStoppedTraversal = (
|
|
procedure: string,
|
|
unexpanded: number,
|
|
): string =>
|
|
`Traversal stopped at ${procedure} because the provider did not confirm cleanup of resources it owns; ${unexpanded === 1 ? "1 discovered procedure was" : `${unexpanded} discovered procedures were`} not expanded.`;
|