fix(macho): preserve import identity in retained views

Use distinct row contracts for imports and exports, retaining ambiguous names and historical import defaults without adding import-only fields to export pages. Verify the SDK-advertised schemas and both facets through MCP.
This commit is contained in:
morluto committed 2026-10-23 19:29:41 +08:00
1 parent aa357d6d91
commit 23e68cbdfb
3 files changed
+124 -1

No files matched your search

+23
View File
@@ -107,6 +107,29 @@ const parent = (
limitations: ["Parent limitation."],
});
it("preserves ambiguous import facts without adding import fields to exports", () => {
const imported = {
...symbol("unused"),
name: null,
raw_name: "_value+0x4",
name_status: "ambiguous",
addend: null,
};
const observed = { ...report, imports: coverage([imported], false) };
for (const facet of ["imports", "exports"] as const) {
const projected = projectAnalysisView(parent(observed), {
kind: "macho",
facet,
offset: 0,
limit: 64,
});
if (!projected.ok) throw projected.error;
expect(projected.value.kind === "macho" && projected.value.item).toEqual(
observed[facet].items,
);
}
});
describe("Mach-O analysis views", () => {
it("summarizes slice identity and collection counts without rows", () => {
const projected = projectAnalysisView(parent(), { kind: "summary" });
+2 -1
View File
@@ -51,7 +51,8 @@ export const machoViewItemSchema = z.union([
exactRows(shape.build_metadata.shape.items),
exactRows(shape.load_commands.shape.items),
exactRows(shape.dependencies.shape.items),
// Imports and exports share one row shape.
// Exports must precede imports, whose historical defaults also accept export keys.
exactRows(shape.exports.shape.items),
exactRows(shape.imports.shape.items),
exactRows(shape.segments.shape.items),
exactRows(shape.provenance),
@@ -14,6 +14,8 @@ import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { mcpTest } from "../../support/mcp/mcpFixture.js";
import { openProductionTarget } from "../../support/mcp/productionSession.js";
import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js";
import { advertisedMcpToolSchemas } from "../../fixtures/advertisedMcpTool.js";
const evidenceOf = <Schema extends z.ZodType>(schema: Schema) =>
z.object({
@@ -41,6 +43,103 @@ const pageSchema = z.object({
coverage: z.object({ next_offset: z.number().nullable() }),
});
mcpTest(
"pages ambiguous imports and exact exports under the advertised MCP schema",
async ({ mcp, onTestFinished }) => {
const empty = { items: [], total: 0, exhaustive: true, limitations: [] };
const imported = {
name: null,
raw_name: "_value+0x4",
name_status: "ambiguous",
addend: null,
address: null,
weak: null,
reexport: null,
source: "libFixture",
};
const exported = {
name: "_value",
address: "0x1000",
weak: null,
reexport: null,
source: null,
};
const report = inspectMachoSchema.parse({
format: "mach-o",
selected_architecture: { name: "arm64", container_file_offset: 0 },
endian: "little",
word_size: 64,
file_type: "MH_DYLIB",
flags: [],
uuid: null,
entrypoints: empty,
architectures: empty,
build_metadata: empty,
load_commands: empty,
dependencies: empty,
imports: { ...empty, items: [imported], total: 1 },
exports: { ...empty, items: [exported], total: 1 },
segments: empty,
provenance: [],
limitations: [],
});
const source = createEvidence(
undefined,
{ id: "fixture", name: "Fixture", version: "1" },
{
operation: "inspect_macho",
parameters: {},
result: report,
},
);
const session = createTestBinarySession(() => {
throw new Error("Imported views must not start a provider");
});
onTestFinished(async () => {
await session.close();
});
const client = await mcp.connect(
createServer(
{ kind: "session", session },
{
environment: { ...process.env, REA_MCP_INPUT_SCHEMA_PROFILE: "full" },
},
),
);
const schemas = await advertisedMcpToolSchemas(
client,
"inspect_analysis_view",
);
for (const [facet, expected] of [
["imports", imported],
["exports", exported],
] as const) {
const request = {
source: { kind: "inline", evidence: source },
view: { kind: "macho", facet },
};
expect(
schemas.ajv.validate(schemas.inputSchema, request),
JSON.stringify(schemas.ajv.errors),
).toBe(true);
const reply = await client.callTool({
name: "inspect_analysis_view",
arguments: request,
});
expect(reply.isError, JSON.stringify(reply.content)).not.toBe(true);
expect(
schemas.ajv.validate(schemas.outputSchema, reply.structuredContent),
JSON.stringify(schemas.ajv.errors),
).toBe(true);
expect(
pageSchema.parse(
parseEvidence(reply.structuredContent).normalized_result,
).item,
).toEqual([expected]);
}
},
);
describe.skipIf(process.platform !== "darwin")(
"Mach-O summary delivery through MCP",
() => {