mirror of
https://github.com/morluto/rea
synced 2026-10-26 14:25:49 -04:00
ba866a26ec
Keep procfs absence separate from denied and failed reads. Carry successful rows, thread children and bounded diagnostics together so one failed sibling cannot erase readable peers. Retain root initialization failures and validate ps-discovered Linux candidates through stable procfs identity brackets. Await issued reads during shutdown, retain completed rows and report an unfinished pass through the existing sampling failure/diagnostic contract. Derive partial coverage at its owner and preserve actual read errors when shutdown also interrupts observation. Keep the public capture schema unchanged and verify it with the previous strict capture reader. Update directly affected coverage consumers and regression fixtures, including identity contradictions, mixed outcomes, late failures, cleanup, cancellation, process-only comparison unknowns and converted MCP schemas. Validation: 12 focused files / 123 tests; full check; docs check (5 tests); test discovery (1,635 files). Real macOS CLI/MCP controls exercised source-owned PTY targets. Linux permission behavior uses deterministic boundary fixtures; real Linux and Windows qualification remain unavailable. Completed CLI cost comparisons do not establish a speedup. Fixes #5081 Fixes #5194
118 lines
3.6 KiB
TypeScript
118 lines
3.6 KiB
TypeScript
import { err, ok, type Result } from "../../domain/result.js";
|
|
|
|
/** Retain a count, first failure and interruption fact with their observations. */
|
|
export interface SamplingDiagnostics {
|
|
readonly count: number;
|
|
readonly first: string | null;
|
|
readonly interrupted: boolean;
|
|
}
|
|
|
|
export interface SamplingObservation<T> {
|
|
readonly value: T;
|
|
readonly diagnostics: SamplingDiagnostics;
|
|
}
|
|
|
|
export type SamplingReadFailure =
|
|
| { readonly kind: "cancelled"; readonly reason: unknown }
|
|
| {
|
|
readonly kind: "unavailable";
|
|
readonly systemCode: string | undefined;
|
|
readonly diagnostic: string;
|
|
};
|
|
|
|
export const mergeSamplingDiagnostics = (
|
|
...parts: readonly SamplingDiagnostics[]
|
|
): SamplingDiagnostics => ({
|
|
count: parts.reduce((count, part) => count + part.count, 0),
|
|
first: parts.find((part) => part.first !== null)?.first ?? null,
|
|
interrupted: parts.some((part) => part.interrupted),
|
|
});
|
|
|
|
export const samplingObservation = <T>(
|
|
value: T,
|
|
...diagnostics: readonly SamplingDiagnostics[]
|
|
): SamplingObservation<T> => ({
|
|
value,
|
|
diagnostics: mergeSamplingDiagnostics(...diagnostics),
|
|
});
|
|
|
|
export const samplingFailure = (diagnostic: string): SamplingDiagnostics => ({
|
|
count: 1,
|
|
first: diagnostic,
|
|
interrupted: false,
|
|
});
|
|
|
|
/** An interrupted attempt left an observation or declared traversal unfinished. */
|
|
export const samplingInterrupted = (): SamplingDiagnostics => ({
|
|
count: 0,
|
|
first: null,
|
|
interrupted: true,
|
|
});
|
|
|
|
/** Only this attempt's abort can end a read without a failure observation. */
|
|
export const isSamplingAbort = (cause: unknown, signal: AbortSignal): boolean =>
|
|
signal.aborted &&
|
|
(cause === signal.reason ||
|
|
(cause instanceof Error &&
|
|
cause.name === "AbortError" &&
|
|
cause.cause === signal.reason));
|
|
|
|
/** Settle each issued read, including peers still completing after shutdown. */
|
|
export const readSamplingSource = async <T>(
|
|
source: string,
|
|
signal: AbortSignal,
|
|
read: () => Promise<T>,
|
|
): Promise<Result<T, SamplingReadFailure>> => {
|
|
if (signal.aborted) return err({ kind: "cancelled", reason: signal.reason });
|
|
try {
|
|
return ok(await read());
|
|
} catch (cause: unknown) {
|
|
if (isSamplingAbort(cause, signal))
|
|
return err({ kind: "cancelled", reason: signal.reason });
|
|
const systemCode =
|
|
cause instanceof Error &&
|
|
"code" in cause &&
|
|
typeof cause.code === "string"
|
|
? cause.code
|
|
: undefined;
|
|
const reason =
|
|
cause instanceof Error
|
|
? `${cause.name}: ${cause.message}`
|
|
: String(cause);
|
|
return err({
|
|
kind: "unavailable",
|
|
systemCode,
|
|
diagnostic: `${source}: ${systemCode === undefined ? "" : `${systemCode}: `}${reason}`,
|
|
});
|
|
}
|
|
};
|
|
|
|
export const samplingReadObservation = <T>(
|
|
result: Result<T, SamplingReadFailure>,
|
|
): SamplingObservation<T | undefined> =>
|
|
result.ok
|
|
? samplingObservation(result.value)
|
|
: samplingObservation(
|
|
undefined,
|
|
result.error.kind === "cancelled"
|
|
? samplingInterrupted()
|
|
: samplingFailure(result.error.diagnostic),
|
|
);
|
|
|
|
/** Missing procfs entries are expected exit races; denied reads stay unknown. */
|
|
export const readProcSamplingSource = async <T>(
|
|
path: string,
|
|
signal: AbortSignal,
|
|
read: () => Promise<T>,
|
|
): Promise<SamplingObservation<T | undefined>> => {
|
|
const result = await readSamplingSource(path, signal, read);
|
|
if (
|
|
!result.ok &&
|
|
result.error.kind === "unavailable" &&
|
|
(result.error.systemCode === "ENOENT" ||
|
|
result.error.systemCode === "ESRCH")
|
|
)
|
|
return samplingObservation(undefined);
|
|
return samplingReadObservation(result);
|
|
};
|