diff --git a/src/domain/analysisView/gpuCodeObjectsView.test.ts b/src/domain/analysisView/gpuCodeObjectsView.test.ts index bf4f74a2e0..8bc5b9e8ae 100644 --- a/src/domain/analysisView/gpuCodeObjectsView.test.ts +++ b/src/domain/analysisView/gpuCodeObjectsView.test.ts @@ -3,7 +3,10 @@ import { createEvidence } from "../evidence.js"; import { freezeOwnedJsonTreeSnapshot } from "../immutableJson.js"; import { gpuCodeObjectsSchema } from "../gpu/gpuCodeObjects.js"; import type { AnalysisViewParent } from "./analysisView.js"; -import { projectGpuCodeObjectsView } from "./gpuCodeObjectsView.js"; +import { + gpuCodeObjectsItemsSchema, + projectGpuCodeObjectsView, +} from "./gpuCodeObjectsView.js"; const report = () => gpuCodeObjectsSchema.parse({ @@ -74,3 +77,31 @@ it("revalidates mutable GPU facts and preserves incomplete source coverage", () error: { _tag: "AnalysisInputError" }, }); }); + +it("preserves omitted AMD metadata columns instead of inventing null metadata", () => { + const items = [ + { + id: "object", + kind: "amdgpu", + target: "gfx900", + payload_target: "gfx900", + target_consistency: "single-source", + inspection: "complete", + location: { + container_id: null, + offset: 0, + size: 64, + section_index: null, + section_name: null, + }, + metadata_location: { + container_id: null, + offset: 0, + size: 64, + section_index: null, + section_name: null, + }, + }, + ]; + expect(gpuCodeObjectsItemsSchema.parse(items)).toEqual(items); +}); diff --git a/src/domain/analysisView/gpuCodeObjectsView.ts b/src/domain/analysisView/gpuCodeObjectsView.ts index f00ed3cad1..d6f360888f 100644 --- a/src/domain/analysisView/gpuCodeObjectsView.ts +++ b/src/domain/analysisView/gpuCodeObjectsView.ts @@ -58,16 +58,24 @@ export const gpuCodeObjectsSummarySchema = gpuCodeObjectsObjectSchema }); export const gpuCodeObjectsItemsSchema = z.union([ z.array( - gpuObjectSchema.partial().required({ - id: true, - kind: true, - target: true, - location: true, - metadata_location: true, - payload_target: true, - target_consistency: true, - inspection: true, - }), + gpuObjectSchema + .partial() + .required({ + id: true, + kind: true, + target: true, + location: true, + metadata_location: true, + payload_target: true, + target_consistency: true, + inspection: true, + }) + .extend({ + // Source defaults qualify historical full rows, never omitted page columns. + amd_target_metadata: gpuObjectSchema.shape.amd_target_metadata + .removeDefault() + .optional(), + }), ), z.array( gpuKernelSchema.partial().required({ diff --git a/tests/acceptance/gpuCodeObjectsCli.test.ts b/tests/acceptance/gpuCodeObjectsCli.test.ts index d8cc4a9733..f83e2f9af6 100644 --- a/tests/acceptance/gpuCodeObjectsCli.test.ts +++ b/tests/acceptance/gpuCodeObjectsCli.test.ts @@ -986,6 +986,36 @@ it("keeps unknown target discovery visible in the default MCP summary", async () } }); +it("preserves omitted AMD metadata in selected MCP object pages", async () => { + const root = await createTestTempDirectory("rea-gpu-metadata-columns-"), + path = join(root, "amd"); + await writeFile(path, amdFixture()); + const { call } = await connectLocalToolsMcp(); + const inspected = await call("inspect_gpu_code_objects", { + path, + analysis: "kernels", + view: { collection: "objects", fields: ["amd_target_metadata"] }, + }); + expect(inspected.isError, JSON.stringify(inspected)).not.toBe(true); + const full = gpuCodeObjectsPageViewResultSchema.parse( + parseEvidence(inspected.structuredContent).normalized_result, + ); + expect(full.items[0]).toHaveProperty( + "amd_target_metadata.processor", + "gfx900", + ); + const selected = await call("inspect_analysis_view", { + source: { kind: "retained-evidence", evidence_id: full.parent_evidence_id }, + view: { kind: "gpu-code-objects", collection: "objects", fields: ["kind"] }, + }); + expect(selected.isError, JSON.stringify(selected)).not.toBe(true); + const page = gpuCodeObjectsPageViewResultSchema.parse( + parseEvidence(selected.structuredContent).normalized_result, + ); + expect(page.items).toHaveLength(1); + expect(page.items[0]).not.toHaveProperty("amd_target_metadata"); +}); + it("retains selected kernel facts and complete PTX for historical MCP pages after source removal", async () => { const root = await createTestTempDirectory("rea-gpu-mcp-"), path = join(root, "application");