Compare commits

...
Author SHA1 Message Date
Kaoru0822 95bcaa07ef Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	docs/mcp-contracts.md
#	src/server/retainedSummaryDelivery.ts
#	tests/boundary/mcp/interfaceBuilderSummaryMcp.test.ts
2026-10-14 01:29:20 +00:00
Kaoru0822 f3cd103b31 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-14 00:31:27 +00:00
morluto 94e360c11b test(artifacts): assert SDK argument rejection at its boundary 2026-10-14 02:40:06 +08:00
morluto 7b9ab778a1 fix(mcp): reject invalid delivery selections through typed preflight 2026-10-14 02:40:06 +08:00
morluto a92b960f6f fix(mcp): validate delivery detail before removing it 2026-10-14 02:40:06 +08:00
Kaoru0822andClaude Opus 5.5 da49ea2692 fix(artifacts): describe incomplete Interface Builder graphs accurately
graph.truncated is also set for invalid archives, omitted dictionary keys
and fixed decode budgets, not only caller-selected limits, so the view's
unknown now points at coverage and limitations for the cause. The MCP
test requests the summary first and reads its retained pages before any
complete call, covering the flow where complete delivery cannot fit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 02:40:06 +08:00
Kaoru0822andClaude Opus 5.5 f993d33028 perf(artifacts): add opt-in Interface Builder summary delivery and pages
decode_interface_builder returns the complete object, hierarchy and
connection graph inline. XLD.app's graph is 6.7 MB (about 1.98M o200k
tokens) and Transmission.app's 6.3 MB; over MCP the doubled text and
structured content exceed the default response budget, so an agent got
a resource_constraint error instead of any answer.

Add the established opt-in delivery: omitted or complete detail keeps the
complete result and its Evidence ID, while summary retains the complete
Evidence and returns every decoded document with node-kind,
edge-relation and coverage-status tallies (10k tokens for XLD).
inspect_analysis_view kind interface-builder pages documents, nodes,
edges or coverage; nodes and edges accept an exact decoded document path,
matched against each row's recorded evidence, and an undecoded path is
refused. Decoder truncation stays visible on every page.

Related cleanup, since the new code repeated it a ninth time:
- parseAnalysisViewResult owns parsing a view parent's normalized result
  with the operation's schema and mapping issues into the view source.
- retainedSummaryDelivery owns the preflight/summary hook pair and the
  shared registration options for artifact, native and managed tools.
- openProductionTarget owns the production MCP session test setup.
Duplication drops from 3.02% to 2.98% (threshold 3.0%).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 02:40:06 +08:00
3 changed files with 21 additions and 15 deletions

No files matched your search

@@ -103,6 +103,11 @@ architectures, build_metadata, load_commands, dependencies, imports, exports,
segments and provenance. `source_exhaustive: false` means unreturned symbols
may exist.
`decode_interface_builder` graphs are often several megabytes and can exceed the
MCP response budget. Select `detail: "summary"` for every decoded document and
node/edge counts, then page `view: {kind: "interface-builder", collection:
"edges", document: "<documents[].relative_path>", offset: 0, limit: 64}`.
To see which functions or Objective-C methods a Mach-O actually calls in one
run, use `observe_native_calls` with explicit breakpoints and a bounded
`duration_ms`/`max_events`. It launches a new process under LLDB, so the target
@@ -1,9 +1,9 @@
import { createHash } from "node:crypto";
import { z } from "zod";
import type { AnalysisError } from "../analysisErrorBase.js";
import { analysisInputErrorFromIssues } from "../inputIssueProjection.js";
import type { JsonValue } from "../jsonValue.js";
import { err, ok, type Result } from "../result.js";
import { parseAnalysisViewResult } from "./analysisViewParent.js";
import {
webNetworkCaptureRecordSchema,
webNetworkCaptureBinaryDescriptorSchema,
@@ -388,20 +388,9 @@ const preview = (record: WebNetworkCaptureRecord, format: string) => {
const parseCaptureParent = (
parent: AnalysisViewParent,
): Result<WebNetworkCapture, AnalysisError> => {
const parsed = webNetworkCaptureSchema.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 = parseAnalysisViewResult(webNetworkCaptureSchema, parent);
if (!parsed.ok) return parsed;
const report = parsed.value;
if (
report.artifact.sha256 !== null &&
(parent.subject == null ||
@@ -67,6 +67,18 @@ mcpTest(
const app = await fixtureApp();
const client = await openProductionTarget(mcp, onTestFinished, app);
const invalidDetail = await client.callTool({
name: "decode_interface_builder",
arguments: { detail: "brief" },
});
expect(invalidDetail.isError).toBe(true);
expect(invalidDetail.content).toEqual([
expect.objectContaining({
type: "text",
text: expect.stringMatching(/Input validation error:.*detail/su),
}),
]);
// Summary first: its own retained parent must serve every later page.
const summarized = await client.callTool({
name: "decode_interface_builder",