mirror of
https://github.com/morluto/rea.git
synced 2026-11-06 06:46:10 +00:00
fix(analysis-view): keep inferred confidence on views of inferred Evidence (#8099)
createAnalysisViewEvidence chose view confidence from the view result kind only, so every summary, page or item view of an inferred parent (compare_source_to_bundle, inspect_evm_interface, partially resolved inspect_il2cpp_metadata) was sealed as derived while keeping the parent's authority. A projection never makes its facts more certain: an inferred parent now yields inferred views through CLI and MCP alike. Observed and derived parents keep their existing view confidence and identities. Fixes #8098 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
317bf3fe23
commit
bfa082f777
7 files changed
+114
-3
No files matched your search
@@ -80,8 +80,10 @@ without bytecode text or duplicated raw stdout:
|
||||
}
|
||||
```
|
||||
|
||||
The result is distinct derived Evidence linked to its complete parent. Its
|
||||
`normalized_result.summary` preserves every producer-returned selector, decoded
|
||||
The result is distinct view Evidence linked to its complete parent. Like the
|
||||
parent, it has `confidence: inferred`; projection never strengthens inferred
|
||||
candidates. Its `normalized_result.summary` preserves every producer-returned
|
||||
selector, decoded
|
||||
byte offset, dispatch, argument/mutability string including null, carrier/decoded
|
||||
identities, original limitations and explicit unknowns. Payload `evidence_kind`
|
||||
remains `inferred`. It preserves stderr/truncation and records stdout text length.
|
||||
|
||||
@@ -482,6 +482,9 @@ session retention refuses summary detail with `capability_unavailable`;
|
||||
For `inspect_artifact`, omission also keeps the original request parameters and
|
||||
complete output shape. Delivery selection never reaches the inventory engine;
|
||||
the complete inspection and its atomic inventory Evidence remain retained.
|
||||
A view is never more certain than its parent: a summary or other view of
|
||||
`inferred` Evidence is `inferred`. Other views are `derived`, unless the
|
||||
view kind itself infers, as described below.
|
||||
|
||||
`inspect_analysis_view` also accepts `api-surface` for retained HAR capture Evidence,
|
||||
grouping request lines by method, origin and format-rule path template. Templated
|
||||
|
||||
@@ -10,7 +10,9 @@ import {
|
||||
analysisViewJavaScriptAnalysisWithSource,
|
||||
analysisViewJavaScriptEvidence,
|
||||
analysisViewLayoutEvidence,
|
||||
analysisViewLayoutFixture,
|
||||
} from "../../../tests/fixtures/analysisView.js";
|
||||
import { BINARY_LAYOUT_TEST_PROVIDER } from "../../../tests/fixtures/binaryDiagnostics/layout.js";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { JAVASCRIPT_APPLICATION_EVIDENCE_EXAMPLE } from "../../contracts/javascript/javascriptRuntimeReconciliationExample.js";
|
||||
@@ -450,6 +452,54 @@ it("projects inline layout Evidence and retains a derived view record", async ()
|
||||
});
|
||||
});
|
||||
|
||||
it("never seals a view as more certain than its parent Evidence", async () => {
|
||||
const layout = analysisViewLayoutFixture();
|
||||
for (const [confidence, expected] of [
|
||||
["observed", "derived"],
|
||||
["derived", "derived"],
|
||||
["inferred", "inferred"],
|
||||
] as const) {
|
||||
const parent = createEvidence(
|
||||
{
|
||||
path: layout.artifact.path,
|
||||
format: "elf",
|
||||
architecture: "x86_64",
|
||||
sha256: layout.artifact.sha256,
|
||||
},
|
||||
BINARY_LAYOUT_TEST_PROVIDER,
|
||||
{
|
||||
operation: "inspect_binary_layout",
|
||||
parameters: { path: layout.artifact.path },
|
||||
result: layout,
|
||||
confidence,
|
||||
limitations: layout.limitations,
|
||||
},
|
||||
);
|
||||
// Inline CLI/MCP views and retained summary delivery share one sealing owner.
|
||||
const views = [
|
||||
await inspectAnalysisView({
|
||||
source: { kind: "inline", evidence: parent },
|
||||
view: { kind: "item", collection: "sections", selector: { index: 2 } },
|
||||
}),
|
||||
projectRetainedAnalysis(
|
||||
parent,
|
||||
{
|
||||
recordEvidence: () => ok("added"),
|
||||
evidenceById: (id) =>
|
||||
id === parent.evidence_id ? parent : undefined,
|
||||
},
|
||||
{ kind: "summary" },
|
||||
),
|
||||
];
|
||||
for (const view of views) {
|
||||
if (!view.ok) throw view.error;
|
||||
expect(view.value.confidence).toBe(expected);
|
||||
expect(view.value.authority).toBe(parent.authority);
|
||||
expect(view.value.evidence_links).toEqual([parent.evidence_id]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("resolves same-session retained JavaScript Evidence without re-running analysis", async () => {
|
||||
const parent = analysisViewJavaScriptEvidence(
|
||||
analysisViewJavaScriptAnalysisWithSource(),
|
||||
|
||||
@@ -416,8 +416,10 @@ const createAnalysisViewEvidence = (
|
||||
result,
|
||||
rawResult: null,
|
||||
confidence:
|
||||
// A projection never strengthens its parent: inferred facts stay inferred.
|
||||
// Grouping is whole-capture, so any templated group or parameter makes the result inferred;
|
||||
// entity types and identity in an extracted state, and the coverage summarizing it, always are.
|
||||
parent.confidence === "inferred" ||
|
||||
result.kind === "environment-state" ||
|
||||
result.kind === "environment-coverage" ||
|
||||
((result.kind === "api-surface" ||
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
environmentStateCaptureFixture,
|
||||
} from "../../fixtures/analysisView.js";
|
||||
import { binaryLayoutFixture } from "../../fixtures/binaryDiagnostics/layout.js";
|
||||
import { evmInterfaceFixture } from "../../fixtures/evm/interface.js";
|
||||
import {
|
||||
createEvidence,
|
||||
parseEvidence,
|
||||
@@ -854,6 +855,48 @@ cliTest(
|
||||
},
|
||||
);
|
||||
|
||||
cliTest(
|
||||
"keeps an inferred parent's confidence on every view through the compiled CLI",
|
||||
async ({ cli }) => {
|
||||
const value = evmInterfaceFixture();
|
||||
const parent = createEvidence(
|
||||
{
|
||||
path: value.artifact.path,
|
||||
sha256: value.artifact.sha256,
|
||||
format: "file",
|
||||
},
|
||||
{ id: "fixture-evm", name: "EVMole", version: "1" },
|
||||
{
|
||||
operation: "inspect_evm_interface",
|
||||
parameters: { path: value.artifact.path, encoding: "hex" },
|
||||
result: value,
|
||||
confidence: "inferred",
|
||||
},
|
||||
);
|
||||
for (const view of [
|
||||
{ kind: "summary" },
|
||||
{ kind: "evm", facet: "bytecode_hex", offset: 0, limit: 4 },
|
||||
]) {
|
||||
const result = await cli.run({
|
||||
arguments: [
|
||||
"inspect-analysis-view",
|
||||
JSON.stringify({
|
||||
source: { kind: "inline", evidence: parent },
|
||||
view,
|
||||
}),
|
||||
"--json",
|
||||
],
|
||||
});
|
||||
expect(result.exitCode, result.stdout).toBe(0);
|
||||
expect(parseEvidence(result.json)).toMatchObject({
|
||||
confidence: "inferred",
|
||||
authority: parent.authority,
|
||||
evidence_links: [parent.evidence_id],
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
cliTest("rejects malformed JSON before projection", async ({ cli }) => {
|
||||
const root = await createTestTempDirectory("rea-analysis-view-cli-bad-");
|
||||
const result = await cli.run({
|
||||
|
||||
@@ -398,7 +398,9 @@ it("returns the exact inferred interface summary while preserving complete defau
|
||||
);
|
||||
expect(derived.evidence_id).not.toBe(original.evidence_id);
|
||||
expect(derived.evidence_links).toEqual([original.evidence_id]);
|
||||
expect(derived.confidence).toBe("derived");
|
||||
// Retention and paging do not make inferred candidates more certain.
|
||||
expect(original.confidence).toBe("inferred");
|
||||
expect(derived.confidence).toBe("inferred");
|
||||
const { hex: _hex, ...bytecode } = value.bytecode;
|
||||
expect(view).toMatchObject({
|
||||
parent_operation: "inspect_evm_interface",
|
||||
@@ -517,6 +519,7 @@ it.each(["valid", "wrong-subject", "no-subject"])(
|
||||
exhausted: true,
|
||||
});
|
||||
expect(derived.evidence_links).toEqual([source.evidence_id]);
|
||||
expect(derived.confidence).toBe("inferred");
|
||||
} else
|
||||
expect(parseMcpToolError(response).error.code).toBe("invalid_request");
|
||||
expect(inspect).not.toHaveBeenCalled();
|
||||
|
||||
@@ -22,6 +22,8 @@ it.each(["full", "compact"] as const)(
|
||||
parent.normalized_result,
|
||||
);
|
||||
expect(full.summary).toMatchObject({ split: 1, unknown: 2 });
|
||||
// Static source-to-bundle mappings are inferences; no view may strengthen them.
|
||||
expect(parent.confidence).toBe("inferred");
|
||||
const fileParent = await call("compare_source_to_bundle", {
|
||||
...inputs,
|
||||
reference: { kind: "file", path },
|
||||
@@ -45,6 +47,7 @@ it.each(["full", "compact"] as const)(
|
||||
...inputs,
|
||||
view: { kind: "summary" },
|
||||
});
|
||||
expect(summary.confidence).toBe("inferred");
|
||||
expect(summary.normalized_result).toMatchObject({
|
||||
kind: "summary",
|
||||
parent_evidence_id: parent.evidence_id,
|
||||
@@ -80,6 +83,7 @@ it.each(["full", "compact"] as const)(
|
||||
});
|
||||
const outputSchema: Record<string, unknown> = comparisonTool.outputSchema!;
|
||||
expect(ajv.compile(outputSchema)(selected)).toBe(true);
|
||||
expect(selected.confidence).toBe("inferred");
|
||||
const page = analysisViewResultSchema.parse(selected.normalized_result);
|
||||
expect(page).toMatchObject({
|
||||
kind: "source-to-bundle",
|
||||
@@ -101,6 +105,10 @@ it.each(["full", "compact"] as const)(
|
||||
kind: "retained-evidence",
|
||||
evidence_id: fileParent.evidence_id,
|
||||
};
|
||||
for (const view of [{ kind: "summary" }, request])
|
||||
expect(
|
||||
(await call("inspect_analysis_view", { source, view })).confidence,
|
||||
).toBe("inferred");
|
||||
await assertRetainedPages(call, source, request, full);
|
||||
await writeFile(
|
||||
path,
|
||||
|
||||
Reference in new issue
Block a user