Skip to content

xcode_tools_bridge_disconnect triggers listChanged resync and undermines manual disconnect semantics #343

@shaun0927

Description

@shaun0927

Summary

xcode_tools_bridge_disconnect is documented as the tool that disconnects the Xcode IDE bridge and unregisters proxied xcode_tools_* tools.

On the current latest release (v2.3.2, released 2026-03-31), the manual disconnect path still triggers the bridge invalidation callback, which schedules syncTools({ reason: "listChanged" }) again.

That means the disconnect flow can immediately fall back into the same auto-resync path that is supposed to be used for remote tool catalog changes, not for an explicit manual disconnect.

Why this looks like a bug

This seems to violate the current contract of the xcode-ide bridge tools:

  • xcode_tools_bridge_disconnect is described as: Disconnect bridge and unregister proxied xcode_tools_* tools.
  • xcode_tools_bridge_sync is described as the explicit manual retry path: One-shot connect + tools/list sync (manual retry; avoids background prompt spam).

If disconnect still allows the listChanged invalidation path to schedule a resync, those two tools no longer have clearly separated semantics.

It also appears to conflict with the repo's broader lifecycle direction in recent fixes (#273 / #274): bounded and deterministic bridge/server behavior, rather than surprising reconnects during teardown or manual disconnect flows.

Actual behavior

During local verification in my fork, I patched a minimal repro around XcodeToolsBridgeManager.disconnectTool() and observed this call order:

  1. registry.clear
  2. service.disconnect
  3. syncTools({ reason: "listChanged" })

I also added a temporary regression test asserting that a manual disconnect should not call syncTools({ reason: "listChanged" }), and the test failed because the sync path was invoked once.

Expected behavior

After xcode_tools_bridge_disconnect:

  • the bridge should remain disconnected
  • proxied xcode_tools_* tools should stay unregistered
  • no automatic listChanged resync should run as part of that manual disconnect path
  • the next re-sync should happen only through an explicit/manual bridge action such as xcode_tools_bridge_sync

Relevant code path

  • src/integrations/xcode-tools-bridge/client.ts
  • src/integrations/xcode-tools-bridge/tool-service.ts
  • src/integrations/xcode-tools-bridge/manager.ts

The critical flow appears to be:

  • disconnect() calls service.disconnect()
  • bridge close triggers onBridgeClosed
  • onBridgeClosed forwards to onToolCatalogInvalidated
  • manager handles that with void this.syncTools({ reason: "listChanged" })

Version checked

  • latest release: v2.3.2
  • also reproduced locally against current upstream/main checked out in my fork/worktree before preparing a fix

Note

I have a narrow fix plus regression test ready and can open a PR that suppresses listChanged-driven resync while a manual bridge disconnect is in progress.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions