Skip to content

feat(connectors): make every connector installable into any workspace#376

Merged
mgoldsborough merged 4 commits into
mainfrom
feat/connectors-installable-anywhere
Jun 3, 2026
Merged

feat(connectors): make every connector installable into any workspace#376
mgoldsborough merged 4 commits into
mainfrom
feat/connectors-installable-anywhere

Conversation

@mgoldsborough
Copy link
Copy Markdown
Contributor

Summary

Makes every connector installable into any workspace (personal or shared), as requested. Removes defaultBinding and the Composio personal-workspace install guard. Binding scope is now a property of the target workspace, never the catalog entry — install targets whichever workspace the request is for (ctx.getWorkspaceId(), derived from the /w/<slug> route).

Why removing the guard is safe (not just deleting a check)

The guard claimed personal-workspace Composio installs would orphan the upstream Composio account on disconnect. That footgun doesn't exist in the current code:

  • Install (connector-tools.ts) already keys purely on the target wsId and treats personal vs shared identically — same BundleRef shape, same credentials/composio/<connectorId>/ layout.
  • Disconnect cleanup (lifecycle.tscleanupComposioBundle) is keyed on { wsId, connectorId } with no scope discrimination. The guard's comment cited a lifecycle.disconnect isWorkspaceScope gate that no longer exists in the file.

So the guard was stale caution from before the install/disconnect pipelines were unified.

On "stop deriving the target from anything but the current workspace"

Already true for install: the dispatcher defaults the target to the request's active workspace and never consults defaultBinding. The defaultBinding-selects-the-target comments were stale; this PR deletes both the comments and the field.

Changes

  • Drop the isPersonal === true Composio guard + stale comments (connector-tools.ts).
  • Remove defaultBinding from the connector meta schema (server-detail.ts), projection (projection.ts), registry/web types (registries/types.ts, web/src/api/client.ts), the catalog YAML, and the two synthesized DirectoryEntry call sites (OperatorOAuthSection, ConnectorStatusHero).
  • Collapse the Browse page to one unified list (no personal/workspace split); drop the unused mode prop and its route arg.
  • Tests: drop defaultBinding fixtures/assertions; reframe the personal-workspace install test around the unified path.

Verification

  • tsc --noEmit clean (backend + web)
  • biome format + lint clean
  • check:catalog passes (11/11 DCR entries)
  • Connector unit (127), web component (21), and integration install (5) suites all green

Follow-up (not in this PR)

The error message that pointed users at a non-existent "install dialog" picker is gone with the guard. No separate copy fix needed.

Remove `defaultBinding` and the Composio personal-workspace install
guard. Any connector — Composio, DCR, static, or mpak — now installs
into whichever workspace the request is for (personal or shared);
binding scope is a property of the target workspace, not the catalog
entry.

Why this is safe:
- Install (connector-tools.ts) already keys purely on the target `wsId`
  and treats personal vs shared identically — same BundleRef shape,
  same `credentials/composio/<connectorId>/` layout.
- Disconnect cleanup (lifecycle.ts `cleanupComposioBundle`) is also
  keyed on `{ wsId, connectorId }` with no scope discrimination, so the
  "orphan the upstream Composio account" footgun the guard claimed to
  prevent does not exist. The guard's comment cited a
  `lifecycle.disconnect` `isWorkspaceScope` gate that no longer exists.
- Install already targets the request's active workspace
  (`ctx.getWorkspaceId()`, set from the `/w/<slug>` route); nothing
  derives the target from `defaultBinding` anymore.

Changes:
- Drop the `isPersonal === true` Composio guard and its stale comments.
- Remove `defaultBinding` from the connector meta schema, projection,
  registry/web types, catalog YAML, and the two synthesized
  DirectoryEntry call sites.
- Collapse the Browse page to one unified list (no personal/workspace
  split); drop the now-unused `mode` prop.
- Update tests: drop `defaultBinding` fixtures/assertions; reframe the
  personal-workspace install test around the unified path.

Verification: backend + web tsc clean; biome format/lint clean;
check:catalog passes; connector unit + web component + integration
install suites all green.
…nvariant

QA follow-up. The reframed personal-workspace test used a dcr fixture
(Canva) — a path the removed guard never gated — so it gave false
confidence about the path this change actually unblocks. Add two tests
to the composio-install suite that exercise the composio branch:

- (g) composio install into a personal workspace persists the ref
  (succeeds where the old isPersonal guard rejected).
- (h) disconnect of a personal-workspace composio bundle runs cleanup
  keyed on that wsId — seeds a connection.json under the personal
  workspace, disconnects through the real lifecycle caller, and asserts
  it was removed. Pins the "cleanup resolves by wsId, not isPersonal"
  invariant the safety argument rests on, so a future isPersonal gate in
  a cleanup caller fails a test instead of silently orphaning upstream.

Also retune the stale bootstrap.ts isPersonal passthrough comment: it
described a T010 install-dialog preselection/picker this change removed.
QA round 2 follow-up. ConnectorBrowsePage lost its `mode` discriminator
in the earlier commit, but three sibling surfaces still carried it
despite only ever being mounted as `"workspace"`:

- ConnectorDetailPage: drop the `mode` prop. Its `canManage` ternary
  (`mode === "personal" ? true : roleAtLeast(...)`) was a dead branch
  that would grant manage unconditionally if `mode="personal"` were
  ever wired — now just `roleAtLeast(role, "ws_admin")`, which already
  covers the personal owner (sole admin of their personal workspace).
- ConnectorList, ToolPermissionsTable: both already ignored `mode`
  (`mode: _mode`); remove the prop and its mount-site args.

Also fix the install start-warning message to distinguish "your
personal workspace" vs "this workspace", matching the success path
(was a generic "the target workspace").

No behavior change — every surface was already workspace-only.
…ace")

The eager-start-warning branch already said "in this workspace"; the
no-warning branch still said "for this workspace" for the same shared-
workspace case. Use "in" across all four variants (both personal
branches already do). Cosmetic only.
@mgoldsborough mgoldsborough added the qa-reviewed QA review completed with no critical issues label Jun 3, 2026
@mgoldsborough mgoldsborough merged commit 28d1416 into main Jun 3, 2026
6 checks passed
@mgoldsborough mgoldsborough deleted the feat/connectors-installable-anywhere branch June 3, 2026 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

qa-reviewed QA review completed with no critical issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant