perf(managed): validate a sealed member parent once across pages

Each managed-members page reparsed the whole parent report, about 35 ms
for an 18 MB assembly result regardless of the page size. The analysis
view service now reports whether the parent Evidence is a sealed
snapshot, and the member view reuses the validated report for that
sealed result through a weak reference. Unsealed input is still
validated on every call.

The round-trip test fixture now carries an exported type, an ldstr
literal, a field access and a call edge, and requires every collection
to be nonempty so a row schema that drops facts cannot pass on empty
arrays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822 2026-10-14 01:06:28 +00:00
parent 54e89bd5c8
commit 7c6368f932
4 changed files with 85 additions and 19 deletions

View File

@ -21,6 +21,7 @@ import {
type InspectAnalysisViewInput,
} from "../../domain/analysisView/analysisView.js";
import { jsonValueSchema } from "../../domain/jsonValue.js";
import { isImmutableJsonSnapshot } from "../../domain/immutableJson.js";
import { err, ok, type Result } from "../../domain/result.js";
import { WebNetworkApiSurfaceAccumulator } from "../../domain/analysisView/WebNetworkApiSurfaceAccumulator.js";
@ -66,6 +67,8 @@ export const inspectAnalysisViewValidated = (
evidenceId: parent.evidence_id,
operation: parent.operation,
normalizedResult: parent.normalized_result,
// Sealing an Evidence root deep-freezes it; only the root is registered.
sealedResult: isImmutableJsonSnapshot(parent),
subject: parent.subject,
limitations: parent.limitations,
parameters: parent.parameters,

View File

@ -599,6 +599,8 @@ export interface AnalysisViewParent {
readonly evidenceId: string;
readonly operation: string;
readonly normalizedResult: unknown;
/** The owner proved normalizedResult belongs to a sealed, deep-frozen Evidence snapshot. */
readonly sealedResult?: boolean;
/** Preserve the authenticated source identity, including explicit absence. */
readonly subject?: Evidence["subject"];
readonly limitations: readonly string[];

View File

@ -139,6 +139,44 @@ const collectionPage = (
}
};
// A sealed parent result cannot change, so its validated report stays exact for
// every later page. The weak reference lets memory pressure reclaim the copy;
// unsealed input is validated on each call.
const validatedReports = new WeakMap<
object,
WeakRef<ManagedMemberInspection>
>();
/** Validate the parent once per sealed result instead of once per page. */
const parseMemberReport = ({
normalizedResult,
sealedResult,
}: AnalysisViewParent): Result<ManagedMemberInspection, AnalysisError> => {
const sealed =
sealedResult === true &&
typeof normalizedResult === "object" &&
normalizedResult !== null;
const cached = sealed
? validatedReports.get(normalizedResult)?.deref()
: undefined;
if (cached !== undefined) return ok(cached);
const parsed = managedMemberInspectionSchema.safeParse(normalizedResult);
if (!parsed.success)
return err(
analysisInputErrorFromIssues(
"inspect_analysis_view",
parsed.error.issues.map((issue) => ({
...issue,
path: ["source", "normalized_result", ...issue.path],
})),
{ source: { normalized_result: normalizedResult } },
{ cause: parsed.error },
),
);
if (sealed) validatedReports.set(normalizedResult, new WeakRef(parsed.data));
return ok(parsed.data);
};
/** Everything but the member collections, which are replaced by their counts. */
const summarizeMembers = (
report: ManagedMemberInspection,
@ -189,22 +227,9 @@ export const projectManagedMemberView = (
},
]),
);
const parsed = managedMemberInspectionSchema.safeParse(
parent.normalizedResult,
);
if (!parsed.success)
return err(
analysisInputErrorFromIssues(
"inspect_analysis_view",
parsed.error.issues.map((issue) => ({
...issue,
path: ["source", "normalized_result", ...issue.path],
})),
{ source: { normalized_result: parent.normalizedResult } },
{ cause: parsed.error },
),
);
const report = parsed.data;
const parsed = parseMemberReport(parent);
if (!parsed.ok) return parsed;
const report = parsed.value;
const shared = {
parent_evidence_id: parent.evidenceId,
parent_operation: "inspect_managed_members" as const,

View File

@ -39,7 +39,43 @@ const createHarness = async () => {
it("summarizes selected members and pages every original collection after the file disappears", async () => {
const directory = await createTestTempDirectory("rea-member-summary-");
const path = join(directory, "fixture.dll");
await writeFile(path, buildManagedPeFixture({ methodRowCount: 2 }));
await writeFile(
path,
buildManagedPeFixture({
methodRowCount: 2,
// ldarg.0; ldfld field; call member ref; ldstr "ok"; pop; ret
ilBody: Buffer.from([
(18 << 2) | 2,
0x02,
0x7b,
1,
0,
0,
0x04,
0x28,
1,
0,
0,
0x0a,
0x72,
1,
0,
0,
0x70,
0x26,
0x2a,
]),
userStringHeap: Buffer.from([0, 5, 0x6f, 0, 0x6b, 0, 0]),
exportedTypes: [
{
namespace: "System",
name: "Object",
flags: 0x0020_0001,
implementationRaw: (1 << 2) | 1,
},
],
}),
);
const { client, session } = await createHarness();
const definition = (await client.listTools()).tools.find(
(x) => x.name === "inspect_managed_members",
@ -84,8 +120,6 @@ it("summarizes selected members and pages every original collection after the fi
expect(view.summary.coverage).toEqual(report.coverage);
expect(view.summary.identity_scope).toEqual(report.identity_scope);
expect(view.summary).not.toHaveProperty("methods");
expect(report.methods.length).toBeGreaterThan(0);
expect(report.member_refs.length).toBeGreaterThan(0);
for (const detail of [undefined, "complete"]) {
const complete = parseEvidence(
(
@ -106,6 +140,8 @@ it("summarizes selected members and pages every original collection after the fi
? report.user_strings?.literals
: report[collection];
if (rows == null) throw new Error(`${collection} was not examined`);
// Empty collections would compare equal even if a row schema dropped facts.
expect(rows.length, collection).toBeGreaterThan(0);
expect(view.summary.reported_counts[collection]).toBe(rows.length);
const paged: unknown[] = [];
for (let offset: number | null = 0; offset !== null;) {