From 3c7b176a3df18b3971d9ea9ff40f7ebbd85fa40a Mon Sep 17 00:00:00 2001 From: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> Date: Sat, 10 Oct 2026 00:12:17 -0700 Subject: [PATCH] perf(artifacts): deliver the inspected artifact graph once (#1473) inspect_artifact nested the complete inventory Evidence and then restated every artifact node and occurrence as an observation and every edge as a derived relationship. No caller reads those copies; comparison and tests use the nested inventory Evidence. On Transmission.app the result shrinks from 2.46 MB to 1.10 MB. Observations keep the root manifest and integrity findings. Co-authored-by: Claude Opus 5.5 --- src/contracts/artifactToolContracts.ts | 2 +- src/domain/artifactInspection.ts | 59 +------------------ .../artifactProvider.archiveInventory.test.ts | 9 +++ 3 files changed, 13 insertions(+), 57 deletions(-) diff --git a/src/contracts/artifactToolContracts.ts b/src/contracts/artifactToolContracts.ts index a6926a19f..744ea34c3 100644 --- a/src/contracts/artifactToolContracts.ts +++ b/src/contracts/artifactToolContracts.ts @@ -63,7 +63,7 @@ const artifact = < export const ARTIFACT_TOOL_CONTRACTS = [ artifact( "inspect_artifact", - "Inspect an active archive or application package in one call. Returns the complete content-addressed artifact graph, source Evidence, observations, relationships, integrity contradictions, hypotheses, unexplored branches, limitations, and next probes inline. Read-only DMG mounting is used automatically on supported macOS hosts. Integrity mismatches fail by default; record-and-continue keeps mismatches explicitly untrusted. It does not extract files; use extract_artifact to materialize its regular contents.", + "Inspect an active archive or application package in one call. Returns the complete content-addressed artifact graph once, as the nested inventory Evidence, with root and integrity observations, integrity contradictions, hypotheses, unexplored branches, limitations, and next probes inline. Read-only DMG mounting is used automatically on supported macOS hosts. Integrity mismatches fail by default; record-and-continue keeps mismatches explicitly untrusted. It does not extract files; use extract_artifact to materialize its regular contents.", artifactInventoryInputSchema, ), artifact( diff --git a/src/domain/artifactInspection.ts b/src/domain/artifactInspection.ts index f4c197b9c..19ccb9b8f 100644 --- a/src/domain/artifactInspection.ts +++ b/src/domain/artifactInspection.ts @@ -16,30 +16,12 @@ const artifactIdSchema = prefixedDigestSchema("art"); const inspectionObservationSchema = z.strictObject({ observation_id: prefixedDigestSchema("aio"), - kind: z.enum(["root-manifest", "artifact", "occurrence", "integrity"]), + kind: z.enum(["root-manifest", "integrity"]), subject: textSchema, value: jsonValueSchema, evidence_id: evidenceIdSchema, }); -const inspectionRelationshipSchema = z.strictObject({ - relationship_id: prefixedDigestSchema("air"), - relation: z.enum([ - "contains", - "extracts", - "slice-of", - "embeds", - "loads", - "maps-source", - "derived-from", - ]), - source_artifact_id: artifactIdSchema, - target_artifact_id: artifactIdSchema, - occurrence_id: prefixedDigestSchema("occ"), - logical_path: textSchema.nullable(), - evidence_id: evidenceIdSchema, -}); - const inspectionHypothesisSchema = z.strictObject({ hypothesis_id: prefixedDigestSchema("aih"), statement: textSchema, @@ -97,7 +79,6 @@ export const artifactInspectionResultSchema = z.strictObject({ ) .min(1), observations: z.array(inspectionObservationSchema), - derived_relationships: z.array(inspectionRelationshipSchema), hypotheses: z.array(inspectionHypothesisSchema), contradictions: z.array(inspectionContradictionSchema), unexplored_branches: z.array(unexploredBranchSchema), @@ -107,8 +88,6 @@ export const artifactInspectionResultSchema = z.strictObject({ substeps_completed: z.literal(1), observations_retained: z.number().int().min(0), observations_omitted: z.number().int().min(0), - relationships_retained: z.number().int().min(0), - relationships_omitted: z.number().int().min(0), hypotheses_retained: z.number().int().min(0), hypotheses_omitted: z.number().int().min(0), unexplored_branches_retained: z.number().int().min(0), @@ -136,10 +115,6 @@ export const createArtifactInspection = ( inventory, inventoryEvidence.evidence_id, ); - const relationships = allRelationships( - inventory, - inventoryEvidence.evidence_id, - ); const hypotheses = allHypotheses(inventory, inventoryEvidence.evidence_id); const nextProbes = probesFor(inventory); const branches = allUnexploredBranches( @@ -149,14 +124,12 @@ export const createArtifactInspection = ( ); const retained = { observations, - relationships, hypotheses, branches, probes: nextProbes, }; const omissions = { observations: 0, - relationships: 0, hypotheses: 0, branches: 0, probes: 0, @@ -184,7 +157,6 @@ export const createArtifactInspection = ( }, ], observations: retained.observations, - derived_relationships: retained.relationships, hypotheses: retained.hypotheses, contradictions: contradictions(inventory, inventoryEvidence.evidence_id), unexplored_branches: retained.branches, @@ -194,8 +166,6 @@ export const createArtifactInspection = ( substeps_completed: 1 as const, observations_retained: retained.observations.length, observations_omitted: omissions.observations, - relationships_retained: retained.relationships.length, - relationships_omitted: omissions.relationships, hypotheses_retained: retained.hypotheses.length, hypotheses_omitted: omissions.hypotheses, unexplored_branches_retained: retained.branches.length, @@ -224,6 +194,8 @@ const parseInventoryEvidence = (input: unknown): Evidence => { return evidence; }; +// Artifact, occurrence, and edge records stay in the nested inventory Evidence; +// restating each one here would repeat the complete graph in every result. const allObservations = ( inventory: ArtifactInventoryResult, evidenceId: string, @@ -234,12 +206,6 @@ const allObservations = ( inventory.manifest, evidenceId, ), - ...inventory.nodes.map((node) => - observation("artifact", node.artifact_id, node, evidenceId), - ), - ...inventory.occurrences.map((occurrence) => - observation("occurrence", occurrence.occurrence_id, occurrence, evidenceId), - ), ...inventory.integrity_contradictions.map((value) => observation("integrity", value.contradiction_id, value, evidenceId), ), @@ -263,25 +229,6 @@ const observation = ( }; }; -const allRelationships = ( - inventory: ArtifactInventoryResult, - evidenceId: string, -): ArtifactInspectionResult["derived_relationships"] => - inventory.edges.map((edge) => { - const semantic = { - relation: edge.relation, - source_artifact_id: edge.parent_artifact_id, - target_artifact_id: edge.child_artifact_id, - occurrence_id: edge.occurrence_id, - logical_path: edge.logical_path, - evidence_id: evidenceId, - }; - return { - relationship_id: `air_${digestCanonicalValue(semantic, "Artifact inspection")}`, - ...semantic, - }; - }); - const allHypotheses = ( inventory: ArtifactInventoryResult, evidenceId: string, diff --git a/tests/boundary/providers/native/artifactProvider.archiveInventory.test.ts b/tests/boundary/providers/native/artifactProvider.archiveInventory.test.ts index 06fe70527..2da4a98f4 100644 --- a/tests/boundary/providers/native/artifactProvider.archiveInventory.test.ts +++ b/tests/boundary/providers/native/artifactProvider.archiveInventory.test.ts @@ -60,6 +60,15 @@ describe("artifact archive inventory", () => { expect(inspectionEvidence.evidence_links).toEqual( inspection.evidence_links, ); + // The graph is delivered once, in the nested inventory Evidence. + expect( + artifactInventoryResultSchema.parse( + inspection.substeps[0]?.evidence.normalized_result, + ).occurrences, + ).toHaveLength(3); + expect(inspection.observations.map(({ kind }) => kind)).toEqual([ + "root-manifest", + ]); const cancellation = new AbortController(); cancellation.abort(); await expect(