Move shared function, dossier, inventory, reference and output semantics from the legacy Hopper module to native domain owners. Migrate provider, adapter, test and verifier consumers directly.
88 lines
3.2 KiB
TypeScript
88 lines
3.2 KiB
TypeScript
import type { Evidence } from "./evidence.js";
|
|
import { AnalysisInputError } from "./analysisErrorCore.js";
|
|
import type { EvidenceIntegrityError } from "./evidenceErrors.js";
|
|
import { readEvidence, readEvidencePayload } from "./evidenceValidation.js";
|
|
import type { JsonValue } from "./jsonValue.js";
|
|
import { err, ok, type Result } from "./result.js";
|
|
import { compareUnicodeCodePoints } from "./unicodeCodePointOrder.js";
|
|
import {
|
|
functionDossierSchema,
|
|
type FunctionDossier,
|
|
} from "./native/functionDossier.js";
|
|
import { assessFunctionCallCoverage } from "./functionCallCoverage.js";
|
|
|
|
/** Complete function observation parsed from one analyze_function Evidence record. */
|
|
export interface FunctionSnapshot {
|
|
readonly evidence: Evidence;
|
|
readonly subject: NonNullable<Evidence["subject"]>;
|
|
readonly dossier: FunctionDossier;
|
|
readonly limitations: readonly string[];
|
|
readonly callCoverageBlockers: readonly string[];
|
|
readonly unresolvedCallCaveats: readonly string[];
|
|
readonly callCoverageDiagnostics: readonly string[];
|
|
}
|
|
|
|
/** Parse one complete analyze_function Evidence record for comparison workflows. */
|
|
export const parseFunctionEvidence = (
|
|
input: unknown,
|
|
selection: {
|
|
readonly operation: "compare_functions" | "build_call_path";
|
|
readonly path: readonly (string | number)[];
|
|
},
|
|
): Result<FunctionSnapshot, AnalysisInputError | EvidenceIntegrityError> => {
|
|
const parsed = readEvidence(input);
|
|
if (!parsed.ok) return parsed;
|
|
const evidence = parsed.value;
|
|
const invalidSelection = (message: string, expected?: JsonValue) =>
|
|
err(
|
|
new AnalysisInputError(selection.operation, { userMessage: message }, [
|
|
{
|
|
path: selection.path,
|
|
reason: "invalid_value",
|
|
message,
|
|
...(expected === undefined ? {} : { expected }),
|
|
},
|
|
]),
|
|
);
|
|
if (
|
|
evidence.operation !== "analyze_function" ||
|
|
evidence.predicate_type !== "rea.analysis"
|
|
) {
|
|
const location = selection.path.map(String).join(".");
|
|
const subject =
|
|
location === ""
|
|
? "supplied Evidence"
|
|
: selection.path.length === 1
|
|
? `${location} Evidence`
|
|
: `Evidence at ${location}`;
|
|
return invalidSelection(
|
|
`Function analysis requires analyze_function Evidence with the rea.analysis predicate; the ${subject} has operation ${evidence.operation} and predicate ${evidence.predicate_type}.`,
|
|
{ operation: "analyze_function", predicate_type: "rea.analysis" },
|
|
);
|
|
}
|
|
if (evidence.subject === null)
|
|
return invalidSelection(
|
|
"Function analysis requires artifact-bound Evidence",
|
|
);
|
|
const dossier = readEvidencePayload(
|
|
functionDossierSchema,
|
|
evidence.normalized_result,
|
|
);
|
|
if (!dossier.ok) return dossier;
|
|
const callCoverage = assessFunctionCallCoverage(
|
|
evidence.relationship_coverage,
|
|
dossier.value,
|
|
);
|
|
return ok({
|
|
evidence,
|
|
subject: evidence.subject,
|
|
dossier: dossier.value,
|
|
limitations: [
|
|
...new Set([...evidence.limitations, ...dossier.value.limitations]),
|
|
].sort((left, right) => compareUnicodeCodePoints(left, right)),
|
|
callCoverageBlockers: callCoverage.blockers,
|
|
unresolvedCallCaveats: callCoverage.unresolvedCallCaveats,
|
|
callCoverageDiagnostics: callCoverage.diagnostics,
|
|
});
|
|
};
|