fix(evidence): retain malformed function observations

This commit is contained in:
morluto committed 2026-10-27 12:42:18 +08:00
1 parent 44310bba32
commit 39c7f7e359
2 files changed
+63 -7

No files matched your search

@@ -7,6 +7,7 @@ import { createAnalysisProfile } from "../domain/analysisProfile.js";
import { ghidraFunctionDossier } from "../domain/ghidraValues.fixture.js";
import type { BinaryTarget } from "../domain/binaryTargetTypes.js";
import { err, ok } from "../domain/result.js";
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
const provider = {
id: "dossier-fixture",
@@ -157,6 +158,49 @@ it.each(["provider-failure", "invalid-dossier"] as const)(
_tag: "AnalysisOutputError",
operation: "analyze_function",
});
expect(result.error.partialObservation).toBeUndefined();
if (kind === "provider-failure")
expect(result.error.partialObservation).toBeUndefined();
else {
const retained = parseEvidence(result.error.partialObservation);
expect(retained.normalized_result).toEqual({ procedure: "incomplete" });
expect(retained.subject?.local_path).toBe(target.path);
}
},
);
it("retains malformed dossier facts and source links through public error projection", async () => {
const source = createEvidence(undefined, provider, {
operation: "health",
parameters: {},
result: "retained source",
});
const malformed = { procedure: "incomplete", partial: ["caller fact"] };
const result = await executeFunctionAnalysisEvidence(
{
execute: async () =>
ok(
createAnalysisExecution(malformed, provider, {
subject: null,
rawResult: { text: "original provider output" },
limitations: ["Incomplete provider response"],
locations: [{ kind: "address", address: "0x401000" }],
sourceEvidence: [source],
}),
),
},
parameters,
target,
);
if (result.ok) throw new Error("Expected invalid dossier failure");
expect(result.sourceEvidence).toEqual([source]);
const projected = projectAnalysisError(result.error);
const retained = parseEvidence(projected.details?.partial_observation);
expect(retained.normalized_result).toEqual(malformed);
expect(retained.raw_result).toEqual({ text: "original provider output" });
expect(retained.subject).toBeNull();
expect(retained.evidence_links).toEqual([source.evidence_id]);
expect(retained.locations).toEqual([
{ kind: "address", address: "0x401000" },
]);
expect(retained.limitations).toContain("Incomplete provider response");
});
+18 -6
View File
@@ -49,9 +49,25 @@ export const executeFunctionAnalysisEvidence = async (
);
const sources = sourceLedger.recordsForAnalysis();
if (!recorded.ok) return withEvidenceSources(recorded, sources);
const observation = {
...execution.value,
sourceEvidence: sources,
...(execution.value.subject === undefined && target !== undefined
? { subject: target }
: {}),
};
const dossier = parseFunctionDossier(execution.value.result);
if (!dossier.ok) return withEvidenceSources(dossier, sources);
const observation = execution.value;
if (!dossier.ok) {
dossier.error.retainPartialObservation(
createAnalysisExecutionEvidence(
"analyze_function",
arguments_,
observation,
"observed",
),
);
return withEvidenceSources(dossier, sources);
}
return withEvidenceSources(
completeProviderEvidence(
createAnalysisExecutionEvidence(
@@ -59,11 +75,7 @@ export const executeFunctionAnalysisEvidence = async (
arguments_,
{
...observation,
sourceEvidence: sources,
result: dossier.value,
...(observation.subject === undefined && target !== undefined
? { subject: target }
: {}),
},
"observed",
),