diff --git a/CHANGELOG.md b/CHANGELOG.md index 96651c5..0796071 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ product overview. ## Unreleased +- Fix Slack Settings rejecting the MCP, Skills and You pages with `invalid_arguments` when + their multi-select pickers contain options. + - Require a human approval card before managed Composio joins, invites or creates Slack channels, including in Auto/Admin mode and through batched actions, both identities and host runtimes. Refuse opaque connector code/API executors that could bypass membership approval; use diff --git a/FEATURES.md b/FEATURES.md index cd644b4..7866bca 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -1,5 +1,8 @@ # ChannelGate — Features +- Slack Settings renders populated Cloud MCP and skill pickers on the MCP, Skills and You pages + without a native Slack `invalid_arguments` error; selection still saves immediately. + ## Slack channel membership approvals - Managed Composio Slack join, invitation and channel-creation actions require a one-use human diff --git a/TEST-PLAN.md b/TEST-PLAN.md index 0938a33..8b91c44 100644 --- a/TEST-PLAN.md +++ b/TEST-PLAN.md @@ -1,5 +1,16 @@ # ChannelGate — Test Plan +## Slack Settings picker rendering (2026-10-15) + +- [x] Regression: the existing all-page/all-role/inline-form Block Kit validation includes a + compatibility guard for the deployed modal's live `unsupported element: multiselect` + rejection of actions-block placement (current Slack docs also list actions as supported). + Cloud MCP, channel skills and personal skills + use section accessories, retaining action IDs, block IDs, options and initial selections. +- [ ] Native retest on the deployed fix: open MCP, Skills and You with populated catalogs; + verify each page renders, selection saves and the same modal repaints. Resume the existing + Settings navigation/template live cases. Automated rendering checks alone do not close them. + ## Slack channel membership approvals (2026-10-15) - [x] Automated: `test/composio-approval.test.js`, `test/mcp-socket-server.test.js`, diff --git a/src/slack/channel-settings.js b/src/slack/channel-settings.js index 71a8864..7b2dede 100644 --- a/src/slack/channel-settings.js +++ b/src/slack/channel-settings.js @@ -818,10 +818,10 @@ function cloudRows(snapshot) { continue; } const initial = options.filter((entry) => (data.selected || []).includes(entry.value)); - blocks.push({ type: "actions", block_id: `cg_cloud_${engine}`, elements: [{ + blocks.push({ type: "section", block_id: `cg_cloud_${engine}`, text: mrkdwn("Select servers"), accessory: { type: "multi_static_select", action_id: `${CHANNEL_SETTINGS_CLOUD_SELECT_PREFIX}${engine}`, placeholder: plain("Add a server…"), options, ...(initial.length ? { initial_options: initial } : {}), - }] }); + } }); if (data.inherited?.length) blocks.push(context(`Always on from the organization: ${data.inherited.map(inlineCode).join(" ")}`)); } return blocks; @@ -902,10 +902,10 @@ function skillPicker(actionId, data = {}, placeholder) { const options = (data.options || []).slice(0, 100).map((entry) => option(entry.label || entry.key, entry.key, entry.description || "")); const initial = options.filter((entry) => (data.selected || []).includes(entry.value)); if (!options.length) return [context("_No skills available in the catalog._")]; - return [{ type: "actions", block_id: `${actionId}_row`, elements: [{ + return [{ type: "section", block_id: `${actionId}_row`, text: mrkdwn("Select skills"), accessory: { type: "multi_static_select", action_id: actionId, placeholder: plain(placeholder), options, ...(initial.length ? { initial_options: initial } : {}), - }] }, ...((data.options || []).length > 100 ? [context(`Showing 100 of ${data.options.length} skills; the admin UI lists them all.`)] : [])]; + } }, ...((data.options || []).length > 100 ? [context(`Showing 100 of ${data.options.length} skills; the admin UI lists them all.`)] : [])]; } function skillsPage(snapshot, state) { diff --git a/test/slack-settings-redesign.test.js b/test/slack-settings-redesign.test.js index c37f03a..93b0353 100644 --- a/test/slack-settings-redesign.test.js +++ b/test/slack-settings-redesign.test.js @@ -262,6 +262,10 @@ function blockKitProblems(view) { if (block.type === "context" && block.elements.length > 10) problems.push(`${where} more than 10 context elements`); const elements = [...(block.accessory ? [block.accessory] : []), ...(block.type === "actions" ? block.elements : []), ...(block.type === "input" ? [block.element] : [])]; if (block.type === "actions" && block.elements.length > 25) problems.push(`${where} more than 25 elements`); + // The deployed Slack modal rejected this placement with `unsupported element: multiselect`, + // despite current docs also listing actions blocks. Guard that observed compatibility case; + // section accessories support the same immediate block_actions callbacks. + if (block.type === "actions" && block.elements.some((element) => element.type.startsWith("multi_"))) problems.push(`${where} unsupported multiselect placement`); const actionIds = new Set(); for (const element of elements) { if (element.action_id) { @@ -276,7 +280,7 @@ function blockKitProblems(view) { return problems; } -test("every page, in every role and with every inline form open, is valid Block Kit", () => { +test("every page, in every role and with every inline form open, passes Block Kit and deployed modal compatibility checks", () => { const masked = (last4) => ({ configured: true, last4 }); const long = "X".repeat(64); const snapshot = {