






















@@ -314,6 +314,178 @@ Output artifacts:
314314- `slack-qa-summary.json`
315315- `slack-qa-observed-messages.json` — bodies redacted unless `OPENCLAW_QA_SLACK_CAPTURE_CONTENT=1`.
316316317+#### Setting up the Slack workspace
318+319+The lane needs two distinct Slack apps in one workspace, plus a channel both bots are members of:
320+321+- `channelId` — the `Cxxxxxxxxxx` id of a channel both bots have been invited to. Use a dedicated channel; the lane posts on every run.
322+- `driverBotToken` — bot token (`xoxb-...`) of the **Driver** app.
323+- `sutBotToken` — bot token (`xoxb-...`) of the **SUT** app, which must be a separate Slack app from the driver so its bot user id is distinct.
324+- `sutAppToken` — app-level token (`xapp-...`) of the SUT app with `connections:write`, used by Socket Mode so the SUT app can receive events.
325+326+Prefer a Slack workspace dedicated to QA over reusing a production workspace.
327+328+**1. Create the Driver app**
329+330+Go to [api.slack.com/apps](https://api.slack.com/apps) → _Create New App_ → _From a manifest_ → pick the QA workspace, paste the following manifest, then _Install to Workspace_:
331+332+```json
333+{
334+"display_information": {
335+"name": "OpenClaw QA Driver",
336+"description": "Test driver bot for OpenClaw QA Slack live lane"
337+ },
338+"features": {
339+"bot_user": {
340+"display_name": "OpenClaw QA Driver",
341+"always_online": true
342+ }
343+ },
344+"oauth_config": {
345+"scopes": {
346+"bot": ["chat:write", "channels:history", "groups:history", "users:read"]
347+ }
348+ },
349+"settings": {
350+"socket_mode_enabled": false
351+ }
352+}
353+```
354+355+Copy the _Bot User OAuth Token_ (`xoxb-...`) — that becomes `driverBotToken`. The driver only needs to post messages and identify itself; no events, no Socket Mode.
356+357+**2. Create the SUT app**
358+359+Repeat _Create New App → From a manifest_ in the same workspace. The scope set mirrors the bundled Slack plugin's production install (`extensions/slack/src/setup-shared.ts:10`):
360+361+```json
362+{
363+"display_information": {
364+"name": "OpenClaw QA SUT",
365+"description": "OpenClaw QA SUT connector for OpenClaw"
366+ },
367+"features": {
368+"bot_user": {
369+"display_name": "OpenClaw QA SUT",
370+"always_online": true
371+ },
372+"app_home": {
373+"home_tab_enabled": true,
374+"messages_tab_enabled": true,
375+"messages_tab_read_only_enabled": false
376+ }
377+ },
378+"oauth_config": {
379+"scopes": {
380+"bot": [
381+"app_mentions:read",
382+"assistant:write",
383+"channels:history",
384+"channels:read",
385+"chat:write",
386+"commands",
387+"emoji:read",
388+"files:read",
389+"files:write",
390+"groups:history",
391+"groups:read",
392+"im:history",
393+"im:read",
394+"im:write",
395+"mpim:history",
396+"mpim:read",
397+"mpim:write",
398+"pins:read",
399+"pins:write",
400+"reactions:read",
401+"reactions:write",
402+"usergroups:read",
403+"users:read"
404+ ]
405+ }
406+ },
407+"settings": {
408+"socket_mode_enabled": true,
409+"event_subscriptions": {
410+"bot_events": [
411+"app_home_opened",
412+"app_mention",
413+"channel_rename",
414+"member_joined_channel",
415+"member_left_channel",
416+"message.channels",
417+"message.groups",
418+"message.im",
419+"message.mpim",
420+"pin_added",
421+"pin_removed",
422+"reaction_added",
423+"reaction_removed"
424+ ]
425+ }
426+ }
427+}
428+```
429+430+After Slack creates the app, do two things on its settings page:
431+432+- _Install to Workspace_ → copy the _Bot User OAuth Token_ → that becomes `sutBotToken`.
433+- _Basic Information → App-Level Tokens → Generate Token and Scopes_ → add scope `connections:write` → save → copy the `xapp-...` value → that becomes `sutAppToken`.
434+435+Verify the two bots have distinct user ids by calling `auth.test` on each token. The runtime distinguishes driver and SUT by user id; reusing one app for both will fail mention-gating immediately.
436+437+**3. Create the channel**
438+439+In the QA workspace, create a channel (e.g. `#openclaw-qa`) and invite both bots from inside the channel:
440+441+```
442+/invite @OpenClaw QA Driver
443+/invite @OpenClaw QA SUT
444+```
445+446+Copy the `Cxxxxxxxxxx` id from _channel info → About → Channel ID_ — that becomes `channelId`. A public channel works; if you use a private channel both apps already have `groups:history` so the harness's history reads will still succeed.
447+448+**4. Register the credentials**
449+450+Two options. Use env vars for single-machine debugging (set the four `OPENCLAW_QA_SLACK_*` variables and pass `--credential-source env`), or seed the shared Convex pool so CI and other maintainers can lease them.
451+452+For the Convex pool, write the four fields to a JSON file:
453+454+```json
455+{
456+"channelId": "Cxxxxxxxxxx",
457+"driverBotToken": "xoxb-...",
458+"sutBotToken": "xoxb-...",
459+"sutAppToken": "xapp-..."
460+}
461+```
462+463+With `OPENCLAW_QA_CONVEX_SITE_URL` and `OPENCLAW_QA_CONVEX_SECRET_MAINTAINER` exported in your shell, register and verify:
464+465+```bash
466+pnpm openclaw qa credentials add \
467+ --kind slack \
468+ --payload-file slack-creds.json \
469+ --note "QA Slack pool seed"
470+471+pnpm openclaw qa credentials list --kind slack --status all --json
472+```
473+474+Expect `count: 1`, `status: "active"`, no `lease` field.
475+476+**5. Verify end to end**
477+478+Run the lane locally to confirm both bots can talk to each other through the broker:
479+480+```bash
481+pnpm openclaw qa slack \
482+ --credential-source convex \
483+ --credential-role maintainer \
484+ --output-dir .artifacts/qa-e2e/slack-local
485+```
486+487+A green run completes in well under 30 seconds and `slack-qa-report.md` shows both `slack-canary` and `slack-mention-gating` at status `pass`. If the lane hangs for ~90 seconds and exits with `Convex credential pool exhausted for kind "slack"`, either the pool is empty or every row is leased — `qa credentials list --kind slack --status all --json` will tell you which.
488+317489### Convex credential pool
318490319491Telegram, Discord, and Slack lanes can lease credentials from a shared Convex pool instead of reading the env vars above. Pass `--credential-source convex` (or set `OPENCLAW_QA_CREDENTIAL_SOURCE=convex`); QA Lab acquires an exclusive lease, heartbeats it for the duration of the run, and releases it on shutdown. Pool kinds are `"telegram"`, `"discord"`, and `"slack"`.
@@ -322,6 +494,7 @@ Payload shapes the broker validates on `admin/add`:
322494323495- Telegram (`kind: "telegram"`): `{ groupId: string, driverToken: string, sutToken: string }` — `groupId` must be a numeric chat-id string.
324496- Discord (`kind: "discord"`): `{ guildId: string, channelId: string, driverBotToken: string, sutBotToken: string, sutApplicationId: string }`.
497+- Slack (`kind: "slack"`): `{ channelId: string, driverBotToken: string, sutBotToken: string, sutAppToken: string }` — `channelId` must match `^[A-Z][A-Z0-9]+$` (a Slack id like `Cxxxxxxxxxx`). See [Setting up the Slack workspace](#setting-up-the-slack-workspace) for app and scope provisioning.
325498326499Operational env vars and the Convex broker endpoint contract live in [Testing → Shared Telegram credentials via Convex](/help/testing#shared-telegram-credentials-via-convex-v1) (the section name predates Discord support; the broker semantics are identical for both kinds).
327500此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。