__tests__fix(connections): stop the add-MCP and recorder OAuth flows failing silently Review findings on the connections UI. All of them share a shape: the flow finished, nothing reported an error, and the user was left with a connection that does not do what they asked for. B1 The mandatory description was dropped for any branded server. The row was matched by raw string against the URL the user typed, but the probe echoes the input while the callback stores the driver's own URL, which for Pylon carries a trailing slash. The dialog then closed as though it had saved. Matched on a normalized resource identifier now (the same rule the server uses), against the URL frozen when the popup opened rather than the still editable field, and a genuine miss is toasted instead of swallowed. B2 A popup blocked by Safari or Firefox left isConnecting set, which disables every control on the recorder credential form: the card was inert until a page reload. Both blocked-popup paths now clear the flag, drop the pending key and say what happened. S3 Refocusing Cedar mid-flow deleted the pending record, so finishing the sign-in afterwards did nothing. The MCP branch now refetches first and keeps the record until the row actually reports connected. S4 Whether a recorder uses MCP OAuth was re-derived at focus time from live query state, which is undefined mid-refetch; an MCP recorder read as non-MCP goes to Klavis and 422s. The decision is recorded in the pending payload when the flow starts. S5 A token connection saved from the Organization tab silently became a personal one. addMcpConnection has no org variant, so those forms now say so; the manual client-app path does have one and is routed to credentialVault.upsertOrg when the scope says org. S6 Picking a provider and then pasting a different URL left the old brand name on the new server. A name this form derived is cleared with the verdict; one the user typed is kept. S8 The section's reconnect listener stood down for the whole dialog, though only the add step ever competes for the message. Ownership now follows the in-flight sign-in, reported by the step itself. Also: one ScopeSelect instead of two hand-duplicated selectors whose non-admin notes had already drifted; a non-admin who owns an org row is no longer told to ask themselves; providerName reused instead of re-derived; a redundant capabilities cast dropped; and the catalog comment no longer claims a new entry needs no server change, which a null providerId disproves. apps/mail's jest allowlist did not name modules/integrations. Added, with six tests over the two OAuth completion paths; all six fail against the code before this change. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 30, 2026, 1:06 PMadd-mcp-step.tsxfix(forms): an input is a well, and the add-MCP modal has room to be one Inputs and textareas were `bg-background` — the app canvas — while the select next to them was `bg-sunken`. Inside a dialog, whose sheet is also `background`, that made every text field the same colour as the paper behind it while the select beside it read as a control. Same for the "Popular systems" cards in the add-MCP step, which were `bg-card` (RAISED), so the thing you pick from sat on top of the sheet instead of into it. All three are one surface now. `border-input` went with them: Cedar defines `--input` as the sunken SURFACE, not a border colour as shadcn means it, so that class was drawing the border in the fill colour. The add-MCP and connections dialogs take the wider sizes. Tool permissions loses both its paragraphs — one told you to do the thing the button beside it does; the button now sits on the label's own row. Co-Authored-By: Claude Opus 5 (1M context) <<email>> Claude-Session: https://claude.ai/code/session_01DFkAfWRuLsdTkpxrFhTKYWAug 31, 2026, 12:18 AMadd-system-dialog.tsxfix(forms): an input is a well, and the add-MCP modal has room to be one Inputs and textareas were `bg-background` — the app canvas — while the select next to them was `bg-sunken`. Inside a dialog, whose sheet is also `background`, that made every text field the same colour as the paper behind it while the select beside it read as a control. Same for the "Popular systems" cards in the add-MCP step, which were `bg-card` (RAISED), so the thing you pick from sat on top of the sheet instead of into it. All three are one surface now. `border-input` went with them: Cedar defines `--input` as the sunken SURFACE, not a border colour as shadcn means it, so that class was drawing the border in the fill colour. The add-MCP and connections dialogs take the wider sizes. Tool permissions loses both its paragraphs — one told you to do the thing the button beside it does; the button now sits on the label's own row. Co-Authored-By: Claude Opus 5 (1M context) <<email>> Claude-Session: https://claude.ai/code/session_01DFkAfWRuLsdTkpxrFhTKYWAug 31, 2026, 12:18 AMcredential-form.tsxfix(connections): stop the add-MCP and recorder OAuth flows failing silently Review findings on the connections UI. All of them share a shape: the flow finished, nothing reported an error, and the user was left with a connection that does not do what they asked for. B1 The mandatory description was dropped for any branded server. The row was matched by raw string against the URL the user typed, but the probe echoes the input while the callback stores the driver's own URL, which for Pylon carries a trailing slash. The dialog then closed as though it had saved. Matched on a normalized resource identifier now (the same rule the server uses), against the URL frozen when the popup opened rather than the still editable field, and a genuine miss is toasted instead of swallowed. B2 A popup blocked by Safari or Firefox left isConnecting set, which disables every control on the recorder credential form: the card was inert until a page reload. Both blocked-popup paths now clear the flag, drop the pending key and say what happened. S3 Refocusing Cedar mid-flow deleted the pending record, so finishing the sign-in afterwards did nothing. The MCP branch now refetches first and keeps the record until the row actually reports connected. S4 Whether a recorder uses MCP OAuth was re-derived at focus time from live query state, which is undefined mid-refetch; an MCP recorder read as non-MCP goes to Klavis and 422s. The decision is recorded in the pending payload when the flow starts. S5 A token connection saved from the Organization tab silently became a personal one. addMcpConnection has no org variant, so those forms now say so; the manual client-app path does have one and is routed to credentialVault.upsertOrg when the scope says org. S6 Picking a provider and then pasting a different URL left the old brand name on the new server. A name this form derived is cleared with the verdict; one the user typed is kept. S8 The section's reconnect listener stood down for the whole dialog, though only the add step ever competes for the message. Ownership now follows the in-flight sign-in, reported by the step itself. Also: one ScopeSelect instead of two hand-duplicated selectors whose non-admin notes had already drifted; a non-admin who owns an org row is no longer told to ask themselves; providerName reused instead of re-derived; a redundant capabilities cast dropped; and the catalog comment no longer claims a new entry needs no server change, which a null providerId disproves. apps/mail's jest allowlist did not name modules/integrations. Added, with six tests over the two OAuth completion paths; all six fail against the code before this change. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 30, 2026, 1:06 PMscope-select.tsxfix(connections): stop the add-MCP and recorder OAuth flows failing silently Review findings on the connections UI. All of them share a shape: the flow finished, nothing reported an error, and the user was left with a connection that does not do what they asked for. B1 The mandatory description was dropped for any branded server. The row was matched by raw string against the URL the user typed, but the probe echoes the input while the callback stores the driver's own URL, which for Pylon carries a trailing slash. The dialog then closed as though it had saved. Matched on a normalized resource identifier now (the same rule the server uses), against the URL frozen when the popup opened rather than the still editable field, and a genuine miss is toasted instead of swallowed. B2 A popup blocked by Safari or Firefox left isConnecting set, which disables every control on the recorder credential form: the card was inert until a page reload. Both blocked-popup paths now clear the flag, drop the pending key and say what happened. S3 Refocusing Cedar mid-flow deleted the pending record, so finishing the sign-in afterwards did nothing. The MCP branch now refetches first and keeps the record until the row actually reports connected. S4 Whether a recorder uses MCP OAuth was re-derived at focus time from live query state, which is undefined mid-refetch; an MCP recorder read as non-MCP goes to Klavis and 422s. The decision is recorded in the pending payload when the flow starts. S5 A token connection saved from the Organization tab silently became a personal one. addMcpConnection has no org variant, so those forms now say so; the manual client-app path does have one and is routed to credentialVault.upsertOrg when the scope says org. S6 Picking a provider and then pasting a different URL left the old brand name on the new server. A name this form derived is cleared with the verdict; one the user typed is kept. S8 The section's reconnect listener stood down for the whole dialog, though only the add step ever competes for the message. Ownership now follows the in-flight sign-in, reported by the step itself. Also: one ScopeSelect instead of two hand-duplicated selectors whose non-admin notes had already drifted; a non-admin who owns an org row is no longer told to ask themselves; providerName reused instead of re-derived; a redundant capabilities cast dropped; and the catalog comment no longer claims a new entry needs no server change, which a null providerId disproves. apps/mail's jest allowlist did not name modules/integrations. Added, with six tests over the two OAuth completion paths; all six fail against the code before this change. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 30, 2026, 1:06 PMsystem-row.tsxMerge origin/staging into fix/dm-container-kind Both branches built org-shared MCP connections independently, so the merge picks ONE mechanism and layers the other work on top of it. KEPT (staging) `metadata.scope` / `organizationId` + callability.ts as the single visibility rule for every MCP surface. It resolves name collisions deterministically, narrows in SQL and enforces in JS, and it is what the discovery-based OAuth callback and the credential vault already write. DROPPED (ours) `metadata.orgShared` and the peer-scan in visible-connections.ts. That module survives as the agent-facing ADAPTER over findMcpConnections — `ownedByMe` and the explicit-grant name set — so nothing re-implements a cross-tenant WHERE. KEPT (ours) `toolPolicy` (the per-tool ceiling) and `requiresAgentGrant` (which of a person's agents reach a connection). Staging has no equivalent; they are a different axis from scope, and the types now say so. Settings loses mcp-integration-card.tsx to staging's Systems and credentials section, and the tool-permissions checklist is ported onto system-row.tsx so the ceiling is still editable. The agent's own picker keeps its shape and now reads staging's KNOWN_MCP_PROVIDERS, resolving a provider id through discovery at click time instead of from a second hard-coded catalogue. `probeMcpServer` became discovery, so the pre-save tools/list probe is `probeMcpServerTools`. Also reconciled outside MCP: the Slack Connect webhook scoping (staging's, which handles externally shared channels) keeping this branch's stronger workspace resolver; the agent-archive and subagent-write metadata stamps, chained rather than chosen between; buildAgentDocs' generic folder model with this branch's `listed` on top; and the conferencing-aware event popover with the extracted RSVP hook. Fixes carried by the merge: coach-meeting and coach-weekly had no folder and would have landed in Active; a duplicate source_workspace_id column; a duplicate import; the dead org-admin catalogue bypass. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 30, 2026, 4:15 PMsystems-and-credentials-section.tsxMerge origin/staging into fix/dm-container-kind Both branches built org-shared MCP connections independently, so the merge picks ONE mechanism and layers the other work on top of it. KEPT (staging) `metadata.scope` / `organizationId` + callability.ts as the single visibility rule for every MCP surface. It resolves name collisions deterministically, narrows in SQL and enforces in JS, and it is what the discovery-based OAuth callback and the credential vault already write. DROPPED (ours) `metadata.orgShared` and the peer-scan in visible-connections.ts. That module survives as the agent-facing ADAPTER over findMcpConnections — `ownedByMe` and the explicit-grant name set — so nothing re-implements a cross-tenant WHERE. KEPT (ours) `toolPolicy` (the per-tool ceiling) and `requiresAgentGrant` (which of a person's agents reach a connection). Staging has no equivalent; they are a different axis from scope, and the types now say so. Settings loses mcp-integration-card.tsx to staging's Systems and credentials section, and the tool-permissions checklist is ported onto system-row.tsx so the ceiling is still editable. The agent's own picker keeps its shape and now reads staging's KNOWN_MCP_PROVIDERS, resolving a provider id through discovery at click time instead of from a second hard-coded catalogue. `probeMcpServer` became discovery, so the pre-save tools/list probe is `probeMcpServerTools`. Also reconciled outside MCP: the Slack Connect webhook scoping (staging's, which handles externally shared channels) keeping this branch's stronger workspace resolver; the agent-archive and subagent-write metadata stamps, chained rather than chosen between; buildAgentDocs' generic folder model with this branch's `listed` on top; and the conferencing-aware event popover with the extracted RSVP hook. Fixes carried by the merge: coach-meeting and coach-weekly had no folder and would have landed in Active; a duplicate source_workspace_id column; a duplicate import; the dead org-admin catalogue bypass. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 30, 2026, 4:15 PMtypes.tsMerge origin/staging into fix/dm-container-kind Both branches built org-shared MCP connections independently, so the merge picks ONE mechanism and layers the other work on top of it. KEPT (staging) `metadata.scope` / `organizationId` + callability.ts as the single visibility rule for every MCP surface. It resolves name collisions deterministically, narrows in SQL and enforces in JS, and it is what the discovery-based OAuth callback and the credential vault already write. DROPPED (ours) `metadata.orgShared` and the peer-scan in visible-connections.ts. That module survives as the agent-facing ADAPTER over findMcpConnections — `ownedByMe` and the explicit-grant name set — so nothing re-implements a cross-tenant WHERE. KEPT (ours) `toolPolicy` (the per-tool ceiling) and `requiresAgentGrant` (which of a person's agents reach a connection). Staging has no equivalent; they are a different axis from scope, and the types now say so. Settings loses mcp-integration-card.tsx to staging's Systems and credentials section, and the tool-permissions checklist is ported onto system-row.tsx so the ceiling is still editable. The agent's own picker keeps its shape and now reads staging's KNOWN_MCP_PROVIDERS, resolving a provider id through discovery at click time instead of from a second hard-coded catalogue. `probeMcpServer` became discovery, so the pre-save tools/list probe is `probeMcpServerTools`. Also reconciled outside MCP: the Slack Connect webhook scoping (staging's, which handles externally shared channels) keeping this branch's stronger workspace resolver; the agent-archive and subagent-write metadata stamps, chained rather than chosen between; buildAgentDocs' generic folder model with this branch's `listed` on top; and the conferencing-aware event popover with the extracted RSVP hook. Fixes carried by the merge: coach-meeting and coach-weekly had no folder and would have landed in Active; a duplicate source_workspace_id column; a duplicate import; the dead org-admin catalogue bypass. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 30, 2026, 4:15 PM