fix(block-kit): simplify and harden CLI validation - #119
Conversation
🦋 Changeset detectedLatest commit: fb77da2 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Thanks for the contribution! Before we can merge this, we need @wildcard to sign the Salesforce Inc. Contributor License Agreement. |
wildcard
left a comment
There was a problem hiding this comment.
Self-review guide for the narrowed CLI-validation slice. I checked the exact draft head against issue #99 items 6–10, the repository contribution rules, project-native tests, permission boundaries, and the open #85 overlap. These notes explain the key design decisions; they are not maintainer approval.
| ## Resolve Tooling | ||
|
|
||
| Before choosing the Fast Path, Modification Mode, or full workflow, use the | ||
| `slack:slack-cli` skill, **Step 1: Detect the Slack CLI**, to resolve the public |
There was a problem hiding this comment.
The preflight is deliberately capability-only and runs before every workflow path. Recording SLACK_CMD once avoids the original late-discovery failure and prevents validation and preview from repeating detection or drifting into the CLI skill's install/alias branch.
| Use the `slack:slack-cli` skill, **Step 4: Calling Web API Methods (`slack api`)**, | ||
| for the argument-passing contract. The canonical `blocks.validate` invocation | ||
| below is already known: attempt it directly without running `api --help`, | ||
| `--version`, or generic help first. |
There was a problem hiding this comment.
This is the narrow exception to generic help-driven command discovery: blocks.validate --no-auth is a documented Slack CLI contract, so the skill attempts it directly. The controlled Claude evidence showed that this removes three rejected help/version probes before validation.
|
|
||
| **Path B: curl (fallback, when the CLI isn't installed).** | ||
| If the command cannot start because the host blocks writes to the Slack CLI's | ||
| config or log path, ask for the host's normal narrowly scoped permission and |
There was a problem hiding this comment.
The fallback boundary received the closest security review. A config/log denial may retry the identical command only through normal scoped host approval; if approval is denied, validation stops rather than using curl to route around the operator's decision. Semantic API and shared network failures likewise cannot be hidden by changing transport.
| SKILL = Path("skills/block-kit/SKILL.md").read_text() | ||
|
|
||
|
|
||
| def test_tooling_is_resolved_before_every_workflow_path() -> None: |
There was a problem hiding this comment.
These focused tests intentionally assert durable policy invariants rather than transcript wording: preflight ordering/reuse, canonical calls, semantic same-transport retry, permission non-bypass, and final disclosure. Native agent runs remain supporting behavioral evidence, not unit tests.
|
Full-suite follow-up for exact draft head
The five full-suite failures were:
As a same-environment control, I ran the identical full command on pristine The change-specific regression contract remains deterministic: four unit tests |
WilliamBergamin
left a comment
There was a problem hiding this comment.
Hi @wildcard thanks for your interest and time spent on this contribution 💯 I think its a step in the right direction
Left a few comments, I don't think we need the python unit tests here
Is there any reason why your changes have a line limit shorter then the current implementation?
| Slack CLI command once. Keep the result as either `SLACK_CMD` or an explicit | ||
| reason the CLI is unavailable. This is a capability check only; do not | ||
| authenticate, validate, or write Slack CLI configuration during this preflight. | ||
| For Block Kit validation, follow that detection step's standard-path probe, the | ||
| `PATH` probe, and the alias inquiry/verification branch. Do not propose | ||
| installation unless the user independently asked to configure the Slack CLI. If | ||
| the standard path and `PATH` probes fail, ask whether the public Slack CLI is | ||
| installed under a different name or alias. If the user supplies an alias, | ||
| verify it with `<alias> _fingerprint 2>/dev/null` before setting `SLACK_CMD`. | ||
| If no alias is supplied or verified, record the CLI as unavailable and use | ||
| curl. |
There was a problem hiding this comment.
I wonder if this should be part of 🤔
`slack:slack-cli` skill, **Step 1: Detect the Slack CLI**
@wildcard any chance you tested this out?
| Determine the target surface from context. If it is genuinely ambiguous, ask | ||
| the user with the host's available user-input mechanism: |
| - "A feedback form with a text input and a category selector" (Simple Form Modal) | ||
| - "A notification message with an alert banner, description, and Approve/Reject buttons" (Notification Alert / Approval Message) | ||
| - "A dashboard home tab with a welcome header, key metrics in fields, and quick-action buttons" (Dashboard Home Tab) | ||
| - "A settings modal with dropdowns, checkboxes, and a time picker" (Settings Modal with Multiple Input Types) | ||
| - "A table of sprint tasks with status and points" (Data Table) |
| **Surface constraints to check:** | ||
|
|
||
| - Block count limit: 50 for messages, 100 for modals/home tabs | ||
| - Modal-specific: if using `input` blocks, the modal payload must include a `submit` field | ||
| - Table: only one `table` block per message | ||
| - Surface compatibility (whether a block is valid on the chosen surface) and element compatibility (whether an element is allowed inside a given block) are not always spelled out on a component's doc page. Build the layout from the docs, and let `blocks.validate` in Step 5 confirm it. It is the authoritative check. |
| @@ -0,0 +1,91 @@ | |||
| import re | |||
There was a problem hiding this comment.
I don't think we need these unit test, the skills and project might change over time and these might get in the way 🤔
Summary
Closes #99.
Fast Path and Modification Mode, including alias verification when the public
CLI is installed under a different command name
keep semantic validation errors on that transport, and disclose the final
validation transport precisely
config/log write failures, and stop unvalidated when permission is denied
itself cannot execute for a non-permission reason, with version/help captured
for that execution-failure path
list, and redundant surface/error guidance identified in the issue
Preview
This is a guidance-only change, so the preview is terminal evidence rather than
a product UI screenshot.
claude-code-review-correction-combined.mp4
edb8e35shows fingerprint followed immediately by the canonical validation callok:falseThe attached recording is prior supporting evidence from local candidate
edb8e35; it is not an exact-head recording of this branch. It demonstratesthe CLI correction that this branch keeps while also folding in issue #99 items
1-5. These runs are not claims of official client support or live Slack API
execution.
Testing
make lintmake typecheckmake test-unit— 26/26 passed on exact headclaude plugin validate .PYTEST_ADDOPTS='-k skill-block-kit-modal' make test-eval— the onechange-relevant Block Kit tool-selection scenario passed
make test— run on exact head: units passed 26/26 and evals passed 27/31slack api --helpdocuments--no-auth, andslack api blocks.validate --no-auth 'blocks=[]'returned{"ok":true}The four exact-head full-suite failures were outside this PR's Block Kit diff:
two existing scenarios expect
slack_list_channel_membersalthough the currenthosted MCP endpoint did not return that tool, and two unrelated scenarios
received transient Gemini
503 UNAVAILABLEresponses. The changed Block Kitevaluation passed.
For comparison, the same
make testcommand on pristineupstream/main77a10794under the same host and credentials passed 18/18 units and 22/31evals. Its failures reproduced the same two deterministic upstream eval
problems, six transient Gemini
503 UNAVAILABLEresponses, and one model outputusing
slack_docsinstead of the acceptedslack-docsskill name. Thiscomparison shows the current branch introduces no demonstrated failure class;
it does not make the full command green.
Notes
This changes only
skills/block-kit/SKILL.md, one focused unit contract file,and one patch changeset. It does not modify Slack CLI, MCP/OAuth, plugin
manifests, or official client-support documentation.
Open PR #85 still overlaps the old Step 2 inline inspiration list. The clean
resolution is to keep #85's three new templates in
skills/block-kit/references/common-patterns.mdwhile this branch keeps Step 2as a generic pointer to that file instead of an inline example list. A local
replay against
#85headdccb83e8completed with one textual conflict inskills/block-kit/SKILL.md; the clean resolution retained all three templates,kept the generic pointer, and passed
make lintplus24/24unit tests.Requirements
make testand the tests pass.The last requirement remains unchecked because the exact required command was
run and still exits nonzero.