feat(evidence): discover retained records without full payloads (#1682)

* feat(evidence): discover retained records without full payloads

* fix(evidence): guide oversized results to retained discovery

* style(tests): format retained discovery recovery assertion
This commit is contained in:
Ysopwnable authored and GitHub committed 2026-10-11 11:18:26 +08:00
1 parent c8820e03db
commit 17f704050f
15 files changed
+514 -7

No files matched your search

@@ -135,6 +135,16 @@ session to a selected path. The analysis remains complete in the ledger; avoid
repeating it merely to request the same oversized response. Complete CLI JSON
output also streams without a single MCP frame.
When the retained parent ID is unknown, use `get_evidence_bundle` with
`detail: "summary"` and exact filters for operation, target SHA-256,
analysis-profile digest or canonical procedure address. Select its returned
Evidence ID for the next view instead of requesting the complete bundle.
`retention: "complete-record"` means the whole record is stored; native
value-flow truncation and unknown metadata remain separate. For an exported
portable bundle, use `rea inspect-evidence-bundle` with JSON containing `path`,
`detail: "summary"` and the same `filters`. Reading that file still requires
validating its full contents within the local memory budget.
A client can also truncate a successful tool result without a REA transport
error. Treat that preview as incomplete; use the retained parent ID for smaller
views or export when the full record is needed. Do not parse the preview as
+17
View File
@@ -308,6 +308,23 @@ that reference for a summary, one section/module, or a stable page, or call
bundle exports stream canonical JSON into an atomically published file. A broad
follow-up or `get_evidence_bundle` can also exceed the response budget; exporting
preserves the complete session without sending it through a single MCP frame.
To discover existing records without their large payloads, call
`get_evidence_bundle` with `detail: "summary"`. Its `result.records` contain
Evidence IDs, artifact/provider identities, analysis-profile digests and native
procedure metadata. Optional `filters` match exact `evidence_id`, `operation`,
`target_sha256`, `analysis_profile_digest` or `procedure_address`. Use a discovered
ID with `inspect_analysis_view`; neither discovery nor retained selection starts
a provider. `retention: "complete-record"` describes stored Evidence, while
`native_dossier.value_flow` independently reports truncation, unavailable or
unknown metadata. Missing omission counts remain null. Summary output carries
every matching record; actual transport admission still applies. Omitted
`detail` preserves complete canonical bundle delivery; filters require summary.
For a portable exported bundle, the CLI shares this projection:
`rea inspect-evidence-bundle '{"path":"/tmp/evidence.json","detail":"summary","filters":{"operation":"analyze_function"}}' --format json`.
The CLI validates and materializes that local bundle under its existing memory
budget; summary delivery does not make arbitrary large files cheap to import.
Cancelling an export stops further serialization and removes its staging file.
The destination changes only when a complete export is atomically published.
@@ -0,0 +1,35 @@
import type { EvidenceBundle } from "../../domain/evidenceBundle.js";
import { AnalysisInputError } from "../../domain/analysisErrorCore.js";
import { err, ok, type Result } from "../../domain/result.js";
import {
summarizeEvidenceBundle,
type EvidenceBundleSummary,
type EvidenceRecordFilters,
} from "../../domain/evidenceRecordSummary.js";
/** Select complete bundle delivery or discovery metadata without analysis payloads. */
export const inspectEvidenceBundle = (
bundle: EvidenceBundle,
options: {
readonly detail: "complete" | "summary";
readonly filters?: EvidenceRecordFilters | undefined;
},
): Result<EvidenceBundle | EvidenceBundleSummary, AnalysisInputError> => {
if (options.detail === "complete" && options.filters !== undefined)
return err(
new AnalysisInputError("get_evidence_bundle", undefined, [
{
path: ["filters"],
reason: "invalid_value",
message:
"Record filters require detail: summary; complete returns the canonical bundle.",
expected: "detail: summary",
},
]),
);
return ok(
options.detail === "summary"
? summarizeEvidenceBundle(bundle, options.filters)
: bundle,
);
};
+53
View File
@@ -1,6 +1,12 @@
import { z } from "incur";
import { inspectAnalysisViewValidated } from "../application/analysisView/AnalysisViewService.js";
import { readEvidenceBundle } from "../application/EvidenceBundleFiles.js";
import { inspectEvidenceBundle } from "../application/investigation/InspectEvidenceBundle.js";
import {
getEvidenceBundleInputSchema,
importEvidenceBundleInputSchema,
} from "../contracts/sessionToolSchemas.js";
import { CLI_COMMANDS } from "../cliCommandNames.js";
import { parseCliJsonInput } from "../cliJsonInput.js";
import { logCliCommand } from "../cliLogging.js";
@@ -16,6 +22,53 @@ export const registerAnalysisViewCommands = (
cli: CliInstance,
logger: Logger,
): void => {
cli.command(CLI_COMMANDS.inspectEvidenceBundle, {
description:
"Inspect complete or selected discovery metadata from a local Evidence bundle",
args: z.object({
inputJson: z
.string()
.describe(
"JSON or JSON file with absolute path, detail and optional record filters",
),
}),
run: ({ args }) =>
logCliCommand(logger, CLI_COMMANDS.inspectEvidenceBundle, async () => {
const input = await parseCliJsonInput(
args.inputJson,
CLI_COMMANDS.inspectEvidenceBundle,
);
if (!input.ok) return input.error;
const schema = getEvidenceBundleInputSchema.safeExtend({
path: importEvidenceBundleInputSchema.shape.path,
});
const parsed = schema.safeParse(input.value);
if (!parsed.success)
return {
error: "Evidence bundle inspection failed",
...projectAnalysisError(
analysisInputErrorFromIssues(
CLI_COMMANDS.inspectEvidenceBundle,
parsed.error.issues,
input.value,
),
),
};
const loaded = await readEvidenceBundle(parsed.data.path);
if (!loaded.ok)
return {
error: "Evidence bundle inspection failed",
...projectAnalysisError(loaded.error),
};
const result = inspectEvidenceBundle(loaded.value, parsed.data);
return result.ok
? result.value
: {
error: "Evidence bundle inspection failed",
...projectAnalysisError(result.error),
};
}),
});
cli.command(CLI_COMMANDS.inspectAnalysisView, {
description:
"Project a selected view of completed layout, JavaScript application or native function Evidence JSON",
+1
View File
@@ -43,6 +43,7 @@ export const CLI_COMMANDS = Object.freeze({
inspectBinaryLayout: "inspect-binary-layout",
inspectPeResources: "inspect-pe-resources",
inspectAnalysisView: "inspect-analysis-view",
inspectEvidenceBundle: "inspect-evidence-bundle",
inspectFirmwareRegions: "inspect-firmware-regions",
extractFirmware: "extract-firmware",
inspectAndroidPackage: "inspect-android-package",
+1 -1
View File
@@ -159,7 +159,7 @@ export const SESSION_TOOL_CONTRACTS = [
),
session(
"get_evidence_bundle",
"Return every Evidence record and residual unknown currently retained by this session as one inline bundle for direct inspection or follow-up workflows.",
"Return every retained Evidence record and residual unknown as a complete canonical bundle by default. detail=summary discovers retained records without copying or transferring their payloads; optionally filter by exact Evidence ID, operation, target SHA-256, analysis-profile digest or canonical native procedure address. Metadata distinguishes complete record retention from native value-flow truncation or unavailable/unknown coverage. Use a discovered evidence_id with inspect_analysis_view without restarting a provider. Filters apply only to summary delivery.",
getEvidenceBundleInputSchema,
),
session(
+6 -2
View File
@@ -6,9 +6,13 @@ import { localPathStringSchema } from "../domain/localPath.js";
import { processTraceSpecificationSchema } from "../domain/process/processTraceSpecification.js";
import { evidenceSchema } from "../domain/evidence.js";
import { prefixedDigestSchema } from "./../domain/digests.js";
import { evidenceRecordFiltersSchema } from "../domain/evidenceRecordSummary.js";
/** Return the current canonical Evidence bundle inline. */
export const getEvidenceBundleInputSchema = z.strictObject({});
/** Complete retained Evidence, or selected discovery metadata without payloads. */
export const getEvidenceBundleInputSchema = z.strictObject({
detail: z.enum(["complete", "summary"]).default("complete"),
filters: evidenceRecordFiltersSchema.optional(),
});
/** Optional document selection for volatile navigation context. */
export const navigationContextInputSchema = z.strictObject({
+4
View File
@@ -53,6 +53,10 @@ export const TOOL_EXAMPLE_OVERRIDES: Readonly<
trace_native_ui_action: { action: "buildTapped:" },
open_binary: { path: "/tmp/fixture" },
export_evidence_bundle: { path: "/tmp/evidence.json" },
get_evidence_bundle: {
detail: "summary",
filters: { operation: "analyze_function", procedure_address: "0x1000" },
},
inspect_address_context: { address: "0x1000" },
import_evidence_bundle: { path: "/tmp/evidence.json" },
capture_process_scenario: {
+4 -1
View File
@@ -1,4 +1,5 @@
import { z } from "zod";
import { evidenceBundleSummarySchema } from "../domain/evidenceRecordSummary.js";
import { BINARY_ARCHITECTURES } from "../domain/binaryTargetTypes.js";
import { nativeFunctionAnnotationsSchema } from "../domain/native/nativeFunctionAnnotations.js";
import { nativeLoadImageSchema } from "../domain/native/nativeLoadImage.js";
@@ -470,7 +471,9 @@ export const sessionOutputSchemas = {
unknowns: z.number().int().min(0),
}),
),
get_evidence_bundle: lifecycleResultOf(evidenceBundleSchema),
get_evidence_bundle: lifecycleResultOf(
z.union([evidenceBundleSchema, evidenceBundleSummarySchema]),
),
get_navigation_context: lifecycleResultOf(
z.object({
document: z.string(),
+1 -1
View File
@@ -47,7 +47,7 @@ export const analysisErrorRemediationAction = (
return (
error.remediationAction ??
(error.resource === "transport"
? "Export retained session evidence through export_evidence_bundle to a caller-selected path, or use complete CLI JSON output. Tools that accept retained-evidence references can inspect compatible records. The connection remains usable."
? "Discover retained records with get_evidence_bundle and detail: summary, then inspect compatible records through inspect_analysis_view using an exact Evidence reference. Export complete retained session evidence through export_evidence_bundle to a caller-selected path, or use complete CLI JSON output. The connection remains usable."
: error.resource === "cpu"
? "Review the reported worker CPU limits and observed signal. Retry with sufficient CPU time or a smaller artifact; REA retains tighter inherited limits."
: error.resource === "file-size"
+55
View File
@@ -0,0 +1,55 @@
import { expect, it } from "vitest";
import { createEvidence } from "./evidence.js";
import { createEvidenceBundle } from "./evidenceBundle.js";
import { summarizeEvidenceBundle } from "./evidenceRecordSummary.js";
it("preserves native identity independently of missing or incompatible value-flow metadata", () => {
const records = [
undefined,
{ available: true, truncated: true },
{ unexpected: "legacy" },
{ available: false, reason: "Decompiler unavailable" },
].map((flow) =>
createEvidence(
{ path: "/fixtures/native.exe", format: "pe", sha256: "a".repeat(64) },
{ id: "provider", name: "Provider", version: "1" },
{
operation: "analyze_function",
parameters: {},
result: {
procedure: { address: "space:0x10", name: "entry" },
...(flow === undefined ? {} : { native_value_flow: flow }),
},
},
),
);
const summary = summarizeEvidenceBundle(createEvidenceBundle(records), {
procedure_address: "space:0x10",
});
expect(summary.matching_records).toBe(4);
const native = summary.records.map((r) => r.native_dossier);
expect(
native.every((r) => r.available && r.procedure_address === "space:0x10"),
).toBe(true);
expect(native).toEqual(
expect.arrayContaining([
expect.objectContaining({ value_flow: { status: "not-recorded" } }),
expect.objectContaining({
value_flow: {
status: "observed",
truncated: true,
omitted_operations_lower_bound: null,
known_omitted_inputs: null,
known_omitted_edges: null,
},
}),
expect.objectContaining({
value_flow: { status: "unknown", reason: expect.any(String) },
}),
expect.objectContaining({
value_flow: { status: "unavailable", reason: "Decompiler unavailable" },
}),
]),
);
});
+166
View File
@@ -0,0 +1,166 @@
import { z } from "zod";
import { digestSchema, prefixedDigestSchema } from "./digests.js";
import { evidenceSchema, type Evidence } from "./evidence.js";
import type { EvidenceBundle } from "./evidenceBundle.js";
/** Exact retained-record selectors; selection never resolves a provider address. */
export const evidenceRecordFiltersSchema = z.strictObject({
evidence_id: prefixedDigestSchema("ev").optional(),
operation: z.string().min(1).optional(),
target_sha256: digestSchema.optional(),
analysis_profile_digest: digestSchema.optional(),
procedure_address: z.string().min(1).optional(),
});
export type EvidenceRecordFilters = z.infer<typeof evidenceRecordFiltersSchema>;
const countSchema = z.number().int().nonnegative();
const nativeMetadataSchema = z.object({
procedure: z.object({ address: z.string(), name: z.string().nullish() }),
native_value_flow: z.unknown().optional(),
});
const valueFlowMetadataSchema = z.union([
z.object({
available: z.literal(true),
truncated: z.boolean(),
omitted_operations_lower_bound: countSchema.optional(),
known_omitted_inputs: countSchema.optional(),
known_omitted_edges: countSchema.optional(),
}),
z.object({ available: z.literal(false), reason: z.string() }),
]);
const nativeSummarySchema = z.discriminatedUnion("available", [
z.strictObject({
available: z.literal(true),
procedure_address: z.string(),
procedure_name: z.string().nullable(),
value_flow: z.discriminatedUnion("status", [
z.strictObject({
status: z.literal("observed"),
truncated: z.boolean(),
omitted_operations_lower_bound: countSchema.nullable(),
known_omitted_inputs: countSchema.nullable(),
known_omitted_edges: countSchema.nullable(),
}),
z.strictObject({ status: z.literal("unavailable"), reason: z.string() }),
z.strictObject({ status: z.literal("not-recorded") }),
z.strictObject({ status: z.literal("unknown"), reason: z.string() }),
]),
}),
z.strictObject({
available: z.literal(false),
reason: z.enum(["different-operation", "not-recorded-or-incompatible"]),
}),
]);
/** Metadata for complete retained records, independent of analysis completeness. */
export const evidenceBundleSummarySchema = z.strictObject({
kind: z.literal("evidence-bundle-summary"),
filters: z.record(z.string(), z.string()),
total_retained_records: countSchema,
matching_records: countSchema,
unknown_revisions: countSchema,
records: z.array(
z.strictObject({
evidence_id: evidenceSchema.shape.evidence_id,
subject: evidenceSchema.shape.subject,
provider: evidenceSchema.shape.provider,
operation: evidenceSchema.shape.operation,
confidence: evidenceSchema.shape.confidence,
authority: evidenceSchema.shape.authority,
analysis_profile_digest: digestSchema.nullable(),
retention: z.literal("complete-record"),
limitation_count: countSchema,
native_dossier: nativeSummarySchema,
}),
),
});
export type EvidenceBundleSummary = z.infer<typeof evidenceBundleSummarySchema>;
const nativeSummary = (
evidence: Evidence,
): z.infer<typeof nativeSummarySchema> => {
if (evidence.operation !== "analyze_function")
return { available: false, reason: "different-operation" };
// Read only producer metadata. Parsing the full dossier would allocate every
// large collection merely to discover its already authenticated parent ID.
const parsed = nativeMetadataSchema.safeParse(evidence.normalized_result);
if (!parsed.success)
return { available: false, reason: "not-recorded-or-incompatible" };
const flow = valueFlowMetadataSchema.safeParse(parsed.data.native_value_flow);
return {
available: true,
procedure_address: parsed.data.procedure.address,
procedure_name: parsed.data.procedure.name ?? null,
value_flow:
parsed.data.native_value_flow == null
? { status: "not-recorded" }
: !flow.success
? {
status: "unknown",
reason: "Value-flow metadata has an incompatible representation.",
}
: flow.data.available
? {
status: "observed",
truncated: flow.data.truncated,
omitted_operations_lower_bound:
flow.data.omitted_operations_lower_bound ?? null,
known_omitted_inputs: flow.data.known_omitted_inputs ?? null,
known_omitted_edges: flow.data.known_omitted_edges ?? null,
}
: { status: "unavailable", reason: flow.data.reason },
};
};
/** Discover retained Evidence without reading or copying its complete payloads. */
export const summarizeEvidenceBundle = (
bundle: EvidenceBundle,
filters: EvidenceRecordFilters = {},
): EvidenceBundleSummary => {
const selectedFilters: Record<string, string> = {};
for (const [field, value] of Object.entries(filters))
if (value !== undefined) selectedFilters[field] = value;
const records: EvidenceBundleSummary["records"] = [];
for (const evidence of bundle.records) {
if (
(filters.evidence_id !== undefined &&
filters.evidence_id !== evidence.evidence_id) ||
(filters.operation !== undefined &&
filters.operation !== evidence.operation) ||
(filters.target_sha256 !== undefined &&
filters.target_sha256 !== evidence.subject?.digest.sha256) ||
(filters.analysis_profile_digest !== undefined &&
filters.analysis_profile_digest !== evidence.analysis_profile?.digest)
)
continue;
const native = nativeSummary(evidence);
if (
filters.procedure_address !== undefined &&
(!native.available ||
filters.procedure_address !== native.procedure_address)
)
continue;
records.push({
evidence_id: evidence.evidence_id,
subject: evidence.subject,
provider: evidence.provider,
operation: evidence.operation,
confidence: evidence.confidence,
authority: evidence.authority,
analysis_profile_digest: evidence.analysis_profile?.digest ?? null,
retention: "complete-record",
limitation_count: evidence.limitations.length,
native_dossier: native,
});
}
return {
kind: "evidence-bundle-summary",
filters: selectedFilters,
total_retained_records: bundle.records.length,
matching_records: records.length,
unknown_revisions: bundle.unknowns.length,
records,
};
};
+3 -2
View File
@@ -8,6 +8,7 @@ import {
import { toolContract } from "../contracts/toolContracts.js";
import type { EvidenceBundle } from "../domain/evidenceBundle.js";
import { ok } from "../domain/result.js";
import { inspectEvidenceBundle } from "../application/investigation/InspectEvidenceBundle.js";
import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
interface EvidenceToolRegistration {
@@ -72,9 +73,9 @@ const registerSnapshotEvidenceTool = ({
server.registerTool(
snapshotContract.name,
toolRegistrationOptions(snapshotContract),
() =>
(input) =>
server.delivery.toCallToolResult(
ok(bundleForSerialization(session)),
inspectEvidenceBundle(bundleForSerialization(session), input),
snapshotContract,
),
);
@@ -9,6 +9,53 @@ import { cliTest } from "../../support/cli/cliFixture.js";
import { analysisViewLayoutEvidence } from "../../fixtures/analysisView.js";
import { createEvidence } from "../../../src/domain/evidence.js";
import { ghidraFunctionDossier } from "../../../src/domain/ghidraValues.fixture.js";
import { createEvidenceBundle } from "../../../src/domain/evidenceBundle.js";
import { inspectEvidenceBundle } from "../../../src/application/investigation/InspectEvidenceBundle.js";
cliTest(
"discovers native records from a complete portable bundle using the shared workflow",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-evidence-discovery-cli-");
const parent = createEvidence(
{ path: "/fixtures/native.exe", format: "pe", sha256: "a".repeat(64) },
{ id: "ghidra", name: "Ghidra", version: "12.1.4" },
{
operation: "analyze_function",
parameters: {},
result: ghidraFunctionDossier(),
},
);
const bundle = createEvidenceBundle([parent]);
const path = join(root, "evidence.json");
await writeFile(path, JSON.stringify(bundle));
const options = {
detail: "summary" as const,
filters: { evidence_id: parent.evidence_id },
};
const result = await cli.run({
arguments: [
"inspect-evidence-bundle",
JSON.stringify({ path, ...options }),
"--json",
],
environment: { HOME: root, XDG_CONFIG_HOME: root, XDG_CACHE_HOME: root },
});
expect(result.exitCode).toBe(0);
const expected = inspectEvidenceBundle(bundle, options);
if (!expected.ok) throw expected.error;
expect(result.json).toEqual(expected.value);
const complete = await cli.run({
arguments: [
"inspect-evidence-bundle",
JSON.stringify({ path }),
"--json",
],
environment: { HOME: root, XDG_CONFIG_HOME: root, XDG_CACHE_HOME: root },
});
expect(complete.exitCode).toBe(0);
expect(complete.json).toEqual(bundle);
},
);
cliTest(
"projects one section from a JSON file of inline layout Evidence",
async ({ cli }) => {
+111
View File
@@ -7,6 +7,8 @@ import { z } from "zod";
import { BinaryLayoutService } from "../../../src/application/binaryDiagnostics/BinaryLayoutService.js";
import { toolContract } from "../../../src/contracts/toolContracts.js";
import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js";
import { createAnalysisProfile } from "../../../src/domain/analysisProfile.js";
import { jsonObjectSchema } from "../../../src/domain/jsonValue.js";
import { functionDossierSchema } from "../../../src/domain/hopperValues.js";
import { ghidraFunctionDossier } from "../../../src/domain/ghidraValues.fixture.js";
import { ok } from "../../../src/domain/result.js";
@@ -46,6 +48,115 @@ const connect = async (binaryLayout?: BinaryLayoutService) => {
return { client, session };
};
it("discovers an oversized native dossier by exact metadata and reads its retained view", async () => {
const { client, session } = await connect();
const provider = { id: "ghidra", name: "Ghidra", version: "12.1.4" };
const profile = createAnalysisProfile(provider, { language: "fixture" });
const parent = createEvidence(
{ path: "/fixtures/large.exe", format: "pe", sha256: "a".repeat(64) },
provider,
{
operation: "analyze_function",
parameters: { procedure: "0x401000" },
analysisProfile: profile,
result: {
...jsonObjectSchema.parse(ghidraFunctionDossier()),
pseudocode: "x".repeat(6 * 1024 * 1024),
},
rawResult: null,
},
);
const other = createEvidence(
{ path: "/fixtures/other.exe", format: "pe", sha256: "b".repeat(64) },
provider,
{
operation: "analyze_function",
parameters: {},
result: ghidraFunctionDossier(),
},
);
for (const record of [parent, other]) {
const retained = session.recordEvidence(record);
if (!retained.ok) throw retained.error;
}
const broad = await client.callTool({
name: "get_evidence_bundle",
arguments: {},
});
const broadError = parseMcpToolError(broad).error;
expect(broadError.code).toBe("resource_constraint");
expect(broadError.remediation?.action).toContain(
"get_evidence_bundle and detail: summary",
);
const response = await client.callTool({
name: "get_evidence_bundle",
arguments: {
detail: "summary",
filters: {
operation: "analyze_function",
target_sha256: "a".repeat(64),
analysis_profile_digest: profile.digest,
procedure_address: "0x401000",
},
},
});
expect(response.isError).not.toBe(true);
const result = toolContract("get_evidence_bundle").outputSchema.parse(
response.structuredContent,
).result;
expect(result).toMatchObject({
kind: "evidence-bundle-summary",
total_retained_records: 2,
matching_records: 1,
records: [
{
evidence_id: parent.evidence_id,
analysis_profile_digest: profile.digest,
retention: "complete-record",
native_dossier: { available: true, procedure_address: "0x401000" },
},
],
});
expect(JSON.stringify(response)).not.toContain("pseudocode");
expect(
JSON.parse(response.content.find((c) => c.type === "text")?.text ?? "null"),
).toEqual(response.structuredContent);
const page = await client.callTool({
name: "inspect_analysis_view",
arguments: {
source: { kind: "retained-evidence", evidence_id: parent.evidence_id },
view: { kind: "native", facet: "pseudocode", offset: 0, limit: 32 },
},
});
expect(page.isError).not.toBe(true);
expect(
toolContract("inspect_analysis_view").outputSchema.parse(
page.structuredContent,
).normalized_result,
).toMatchObject({
item: { text: "x".repeat(32) },
coverage: { total: 6 * 1024 * 1024, exhausted: false },
});
const empty = await client.callTool({
name: "get_evidence_bundle",
arguments: {
detail: "summary",
filters: { evidence_id: `ev_${"e".repeat(64)}` },
},
});
expect(
toolContract("get_evidence_bundle").outputSchema.parse(
empty.structuredContent,
).result,
).toMatchObject({ matching_records: 0, records: [] });
await client.ping();
const invalid = await client.callTool({
name: "get_evidence_bundle",
arguments: { filters: { operation: "analyze_function" } },
});
expect(parseMcpToolError(invalid).error.code).toBe("invalid_request");
});
it("advertises exact schemas and projects one layout section from retained Evidence", async () => {
const layout = analysisViewLayoutFixture();
const service = new BinaryLayoutService({