Skip to content

feat(xcode-ide): Persist bridge response artifacts#396

Merged
cameroncooke merged 8 commits intomainfrom
cam/xcode-ide-structured-output-artifacts
May 6, 2026
Merged

feat(xcode-ide): Persist bridge response artifacts#396
cameroncooke merged 8 commits intomainfrom
cam/xcode-ide-structured-output-artifacts

Conversation

@cameroncooke
Copy link
Copy Markdown
Collaborator

@cameroncooke cameroncooke commented May 6, 2026

Make final tool output come from explicit structured results instead of transient streaming fragments, then apply that path to Xcode IDE bridge calls.

Previously, some final CLI/daemon/MCP output depended on progress fragments that were meant only for live rendering. That made non-streaming output harder to reason about and risked leaking large relayed bridge payloads into final text. This change keeps streaming fragments transient and moves final status, errors, summaries, and artifact references into structured results.

For Xcode IDE bridge calls, raw remote responses are now saved as transient workspace-state JSON artifacts. The public output stays concise, while callers that need the full Xcode bridge response can follow the returned artifact path. The Xcode IDE CLI commands now use the same text, JSON, and JSONL rendering path as the rest of the tool catalog.

The branch is intentionally split into reviewable commits:

  • shared rendering/runtime contract changes
  • simulator/device/macOS build-and-run structured-result migration
  • ownership-safe transient artifact cleanup
  • Xcode IDE bridge artifact persistence and schema v2 output
  • Xcode IDE snapshot coverage
  • changelog and agent guidance updates

I considered preserving fragment-derived final state as a compatibility fallback, but that would keep two sources of truth. The structured result is now the canonical final output path, and fragments remain live progress only.

Validated locally with npm run lint, npm run typecheck, npm run format:check, npm run build, and npm test.

Make final tool rendering depend on explicit structured results instead of
streaming fragments. This keeps live progress transient and gives CLI, daemon,
and MCP output paths the same source of truth for final success, errors, and
text rendering.

Add a shared structured error shape and schema coverage so failures can be
rendered consistently without scraping fragment text.
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 6, 2026

Open in StackBlitz

npm i https://pkg.pr.new/xcodebuildmcp@396

commit: b2204d0

Update simulator, device, and macOS build-and-run flows to populate final
structured results directly. This removes the need to infer final status or
artifact paths from progress fragments while preserving live progress for
interactive renderers.

Adjust resource and test helpers around the new result contract.
Expand workspace filesystem cleanup so transient XcodeBuildMCP-owned files are
removed with ownership checks and bounded retention. This keeps generated
artifacts from accumulating while preserving files that belong to active
processes or other workspaces.

Add regression coverage for cleanup ownership and retention behavior.
Save raw Xcode IDE bridge call results as transient workspace artifacts and
return concise structured summaries from the public tools. This prevents large
relayed payloads from being embedded in final text output while preserving the
full remote response for callers that need it.

Route Xcode IDE CLI commands through the generic output pipeline so text, JSON,
and JSONL modes share the same behavior as other tool commands.
Add snapshot coverage for Xcode IDE list-tools and documentation-search output
across CLI text, MCP text, and CLI JSON modes. Normalize transient artifact
paths so the fixtures verify the public contract without depending on local
workspace directories.
Update agent guidance for structured output terminology, streaming boundaries,
and workspace filesystem cleanup. Add the corresponding changelog entries for
the Xcode IDE artifact output and fragment-state removal.
@cameroncooke cameroncooke marked this pull request as ready for review May 6, 2026 15:55
@cameroncooke cameroncooke force-pushed the cam/xcode-ide-structured-output-artifacts branch from 7eb6999 to a1ac92c Compare May 6, 2026 15:55
Avoid asserting an exact wall-clock duration for swift_package_run progress.
CI can legitimately report a small nonzero duration even when local runs finish
within the same millisecond.
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createBaseRenderSession.finalize discards all collected fragments (src/rendering/render.ts:82)

The base session collects emitted fragments into the fragments array, but finalize() now passes items: [] to the hook instead of fragments. For the text and raw strategies, whose finalize hooks render input.items, this means every fragment emitted via session.emit(...) is silently dropped at finalization. Callers that build a render session, emit fragments, and then call finalize() will get output that omits all transcript content (compiler diagnostics, status messages, process lines accumulated for non-streaming consumers, etc.), leaving only structured output and next-steps in the rendered text.

Daemon runtime no longer receives thrown error from failed direct handler (src/runtime/tool-invoker.ts:599)

Previously, when opts.runtime === 'daemon' and the direct tool handler threw, the error was re-thrown so the daemon could propagate it to the client. The new code removes that throw and instead only calls emitExplicitRuntimeError. If the daemon relies on a thrown exception to mark the invocation as failed (e.g. for status/exit codes or RPC error responses), this is a silent behavioral/backwards-compatibility change that could cause failed daemon-internal invocations to appear successful to callers.

Identified by Warden code-review

@cameroncooke
Copy link
Copy Markdown
Collaborator Author

Addressed the Warden review summary findings as no-change after auditing the branch:

  1. createBaseRenderSession.finalize intentionally does not replay streamed session.emit(...) fragments into final text. The current rendering contract keeps streaming fragments transient and renders final output from structured output plus next-step metadata. renderTranscript() / renderFragments() still render explicit transcript inputs when the caller provides them, and text-render-parity.test.ts covers that streamed progress is not captured in final text.

  2. Daemon direct handler failures intentionally return terminal structured error output instead of daemon protocol errors. emitExplicitRuntimeError() writes DIRECT_HANDLER_FAILED into the daemon handler context, and daemon-server.ts returns that in the normal terminal tool result frame. tool-invoker.test.ts covers that daemon handler failures resolve with structured output rather than throwing transport/protocol errors.

No code changes were needed.

Remove fragment caching from render sessions and test results so streamed
fragments cannot be reused as final response state. Normalize xcode-ide tool
counts in snapshots to avoid environment-specific fixture churn.

Refs GH-360
Co-Authored-By: Codex <noreply@openai.com>
@cameroncooke cameroncooke merged commit e1e0859 into main May 6, 2026
49 checks passed
@cameroncooke cameroncooke deleted the cam/xcode-ide-structured-output-artifacts branch May 6, 2026 18:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant