--- name: slackbot-builder description: > Design or harden Slack bot transport and interaction architecture: signed Events API ingress, fast acknowledgement, idempotency, causal thread context, Block Kit actions, message rendering correctness (Markdown, rich text, links and citations), durable agent execution, multi-workspace operation, or Slack-specific delivery of typed follow-up controls and privacy-bounded execution receipts. Use for new Slack bots, broad Slack architecture audits, or changes to these Slack contracts. Do not use for deployment, standalone visual testing, unrelated copywriting or visual design, general answer quality, channel-agnostic transcript or receipt schemas, core suggestion generation, or merely operating Slack. license: MIT metadata: author: swyx version: "2.10" category: "slack" compatibility: Slack Events API, Slack Web API, serverless or long-running workers tags: "slack, bot, events-api, block-kit, modals, file-uploads, image-generation, durable-execution, workflows, cloudflare-workers, hono, kv, observability, tracing, agents" --- # Slackbot Builder Use this skill when creating or hardening Slack apps, Slack bots, agent-in-Slack workflows, or production Slack integrations. This skill has two routing modes: - **Lifecycle work:** use the maturity ladder for greenfield builds, broad audits, or upgrades that change the bot's operating level. - **Bounded capability work:** load only the reference for the named behavior and any lower-level contract the change actually touches. Do not read the whole maturity ladder for a search bug, rendering fix, or isolated tool integration. ## The one rule that survives every level Keep Slack as a thin adapter: verify and acknowledge requests, acquire Slack-scoped context, call the Slack Web API, render bounded Slack UI, and delegate application behavior to a channel-agnostic core. ## The ladder | Level | Theme | What you add | Reference | |---|---|---|---| | **L0** | Skeleton — *it responds* | `/health`, `/events`, signature verify, `url_verification`, fast `200` | [level-0-skeleton.md](level-0-skeleton.md) | | **L1** | Responsive Q&A (MVP) — *a working bot* | 3s ack + async work, `event_id` dedupe, ignore bots, strip mentions, threaded `chat.postMessage`, thin `fetch` wrappers, JSON logs + trace id | [level-1-mvp.md](level-1-mvp.md) | | **L2** | Context-aware — *feels conversational* | shared thread sessions, root + newest causally-prior tail, merged persisted state, DMs, empty-mention nudge, 👀 reaction + `assistant.threads.setStatus` | [level-2-context.md](level-2-context.md) | | **L3** | Interactive / agentic — *acts, human in the loop* | `/interactions` (signed), state-aware approvals that resolve in place, remaining-draft bulk actions, dry-run validation, stateful routing + owned-resource resolution, rich/actionable drafts, file/media controls + settings modal, live status streaming | [level-3-interactive.md](level-3-interactive.md) | | **L4** | Native agent surface — *first-class agent UX* | Agents & AI Apps container, `assistant_thread_started` greeting + suggested prompts, thread titles, native text streaming (`chat.startStream`/`appendStream`/`stopStream`) mapping your `emit` stream to a typed answer + tool-call timeline, graceful fallback to the message flow | [level-4-native-agent.md](level-4-native-agent.md) | | **L5** | Hardened — *won't page you at 2am* | per-thread durable serialization, long-running signed callbacks + durable URL, App Home prefs/dashboard, rate-limit backoff + `ok:false` handling, sanitized errors, storage TTLs, service-user attribution + audit, full observability, security/testing checklists | [level-5-hardened.md](level-5-hardened.md) | | **L6** | Multi-surface / scale — *polished platform* | one core across web/Slack/cron, multi-workspace/tenancy, queues + backpressure for heavy work, target caching, usage analytics + answer-quality feedback, break-glass/degraded config | [level-6-scale.md](level-6-scale.md) | | Specialized contract | Reference | |---|---| | Message formatting, Markdown conversion, clickable links, or citation rendering | [message-formatting.md](message-formatting.md) | | Slack search, files, PDFs, or requester-scoped retrieval | [search-and-retrieval.md](search-and-retrieval.md) | | Native charts/tables or hosted analytical artifacts | [analytical-visualizations.md](analytical-visualizations.md) | | Mixed Slack evidence plus deterministic application/provider data | Read both [search-and-retrieval.md](search-and-retrieval.md) and [analytical-visualizations.md](analytical-visualizations.md) | | Slack rendering of typed follow-up controls or execution receipts | [answer-quality-and-receipts.md](answer-quality-and-receipts.md) | | Generated images and iteration controls | [image-generation.md](image-generation.md) | | Thread persistence, ordering, resource routing, or mutations | [stateful-agent-workflows.md](stateful-agent-workflows.md) | | Cloudflare ingress, dispatch, dedupe, or durable acceptance | [cloudflare-durable-ingress.md](cloudflare-durable-ingress.md) | When diagnosing an existing bot, inspect its current code and tests before choosing a level. A production bot can have an L2 retrieval defect without the task becoming an L5 hardening project. ## Strong opinions **Universal invariants — true at every level** (the level-specific opinions live in their level files so they load only when needed): - Verify every request from the raw body before parsing, including URL verification and interactive requests. - Acknowledge within Slack's deadline. Authenticate, deduplicate, durably dispatch slow work, and guarantee a terminal answer or sanitized error. - Ignore bot-authored events and preserve event/message idempotency. - Treat workspace + channel + `thread_ts` as the conversation boundary. Load only causally prior messages, paginate before trimming, and preserve file/image-only context when the feature is multimodal. - Serialize stateful or mutating turns per thread through shared storage. - Keep application behavior and canonical state outside the Slack adapter; cards and reactions are presentation, not authority. - Construct Block Kit from validated typed data. Keep top-level message text useful for notifications and accessibility; use native table blocks rather than Markdown tables. For text rendering, choose an explicit supported format and follow [message-formatting.md](message-formatting.md); model Markdown is not interchangeable with Slack `mrkdwn`. - Disable both link and media unfurls when replies should not generate previews. - Treat cosmetic enrichment such as reactions and status updates as best effort. Fail closed when identity, authorization, or required evidence cannot be established. - Record structured safe telemetry for ingress, provider calls, fallbacks, and terminal delivery. Never silently substitute a fallback. Requester-visible receipts expose only allowlisted fields, never hidden reasoning, raw private context, or unrestricted provider payloads. **Level-specific opinions** (full rationale + war stories in the linked file): mutations require a human, inline flags configure the core, route on the raw request not the context, interactive actions ack the click *instantly* + batch ops show a started state and terminal summary (plus incremental progress when practical), artifacts get iterate-button + settings-modal affordances → [L3](level-3-interactive.md); instrument *every* model call (not just text), slow work runs in durable execution not a background promise, every entry surface protected equally, every job ends in a guaranteed result-or-error delivered by the right surface mechanism → [L5](level-5-hardened.md); image-gen specifics → [image-generation.md](image-generation.md). ## Implementation bias: start boring TypeScript; a small HTTP framework (Hono, Fastify, Express, or native route handlers); shared KV/Redis/Postgres for state; raw Slack Web API `fetch` wrappers unless an SDK is already established; a queue or platform-native background execution for work over 1–2s; a web page for long output. Add slash commands, modals, Socket Mode, or workflow steps only when the product need is clear.