Spec PR for qbit-slack, plus the repository scaffold.
Design:docs/DESIGN.md. Owner decisions (2026-10-14): all recommendations accepted (standing approval).
Summary
A core messaging provider (slack) and a core delivery destination (slack). Callers use core only: SendMessageAction or DeliverOutputAction.
Tied to the signed-in user, and multi-tenant safe.
Users connect Slack themselves with OAuth, and tenant admins install the bot for their own organization.
Every message has an owner, and is authorized as that owner when it's queued and again when it's sent.
Membership is checked with conversations.members, and there's never a fallback to another token.
Tokens are AES-GCM encrypted, bound to their row, write-only and HIDDEN.
The spec requires a test with two tenants and two workspaces.
Durable outbox.
Claim, lease and retry, with Retry-After handling.
Exactly-once posting: each post carries Slack message metadata, so a crashed runner's post is found instead of posted again.
Idempotency keys and sendOnce serve as the jobs_effect primitive.
Public SlackApi, which mirrors EmailApi. Readiness, and a health check.
Output delivery. Process results, jobs, scheduled reports and jobs, exports, saved views and assistant tasks can deliver to a Slack DM or channel. Pickers show only what the user's own connection can see. Large outputs are uploaded as files, or sent as a link.
The core SPI is specified separately in QRun-IO/qqq (docs/specs/OUTPUT-DELIVERY.md, a follow-up PR).
Routing. The single notify call stays core QAlerts.notify. The fan-out stays in qbit-alerts, which gets a Slack column per category.
This supersedes the 2026-10-13 decision that Slack is "never per user".
PHI and PII never go to Slack. Exports are blocked, or sent without the restricted columns. There's a canary test.
Phase 2: signed interactivity (Approve and Acknowledge) that runs as the clicking user, plus the Events API for revocations.
Owner steps (section 17):
create the Slack app from docs/slack-app-manifest.yaml;
seed four write-only Infisical keys in /kof22-website-slack (a Praetor PR);
add an ExternalSecret;
click "Install for my organization".
Scaffold
Coordinates com.kingsrook.qbits:qbit-slack.
qbit-build-parent 2.1.1, with guardrails enforced.
qqq-orb 0.6.9.
QQQ 4.1.0-RC.6.
Checkstyle and the JaCoCo gate.
Community files, the wrapper and the release-rc workflow are copied from qbit-email.
./mvnw verify passes locally (guardrails G1, G3 and G4 pass).
Spec PR for qbit-slack, plus the repository scaffold.
**Design:** `docs/DESIGN.md`. Owner decisions (2026-10-14): all recommendations accepted (standing approval).
## Summary
- **A core messaging provider** (`slack`) and **a core delivery destination** (`slack`). Callers use core only: `SendMessageAction` or `DeliverOutputAction`.
- **Tied to the signed-in user, and multi-tenant safe.**
- Users connect Slack themselves with OAuth, and tenant admins install the bot for their own organization.
- Every message has an owner, and is authorized as that owner when it's queued and again when it's sent.
- Membership is checked with `conversations.members`, and there's never a fallback to another token.
- Tokens are AES-GCM encrypted, bound to their row, write-only and HIDDEN.
- The spec requires a test with two tenants and two workspaces.
- **Durable outbox.**
- Claim, lease and retry, with `Retry-After` handling.
- Exactly-once posting: each post carries Slack message metadata, so a crashed runner's post is found instead of posted again.
- Idempotency keys and `sendOnce` serve as the jobs_effect primitive.
- **Public `SlackApi`**, which mirrors `EmailApi`. Readiness, and a health check.
- **Output delivery.** Process results, jobs, scheduled reports and jobs, exports, saved views and assistant tasks can deliver to a Slack DM or channel. Pickers show only what the user's own connection can see. Large outputs are uploaded as files, or sent as a link.
- The core SPI is specified separately in QRun-IO/qqq (`docs/specs/OUTPUT-DELIVERY.md`, a follow-up PR).
- **Routing.** The single notify call stays core `QAlerts.notify`. The fan-out stays in qbit-alerts, which gets a Slack column per category.
- This supersedes the 2026-10-13 decision that Slack is "never per user".
- **PHI and PII never go to Slack.** Exports are blocked, or sent without the restricted columns. There's a canary test.
- **Phase 2:** signed interactivity (Approve and Acknowledge) that runs as the clicking user, plus the Events API for revocations.
- **Owner steps (section 17):**
- create the Slack app from `docs/slack-app-manifest.yaml`;
- seed four write-only Infisical keys in `/kof22-website-slack` (a Praetor PR);
- add an ExternalSecret;
- click "Install for my organization".
## Scaffold
- Coordinates `com.kingsrook.qbits:qbit-slack`.
- `qbit-build-parent` 2.1.1, with guardrails enforced.
- `qqq-orb` 0.6.9.
- QQQ 4.1.0-RC.6.
- Checkstyle and the JaCoCo gate.
- Community files, the wrapper and the `release-rc` workflow are copied from qbit-email.
- `./mvnw verify` passes locally (guardrails G1, G3 and G4 pass).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Spec PR for qbit-slack, plus the repository scaffold.
Design:
docs/DESIGN.md. Owner decisions (2026-10-14): all recommendations accepted (standing approval).Summary
slack) and a core delivery destination (slack). Callers use core only:SendMessageActionorDeliverOutputAction.conversations.members, and there's never a fallback to another token.Retry-Afterhandling.sendOnceserve as the jobs_effect primitive.SlackApi, which mirrorsEmailApi. Readiness, and a health check.docs/specs/OUTPUT-DELIVERY.md, a follow-up PR).QAlerts.notify. The fan-out stays in qbit-alerts, which gets a Slack column per category.docs/slack-app-manifest.yaml;/kof22-website-slack(a Praetor PR);Scaffold
com.kingsrook.qbits:qbit-slack.qbit-build-parent2.1.1, with guardrails enforced.qqq-orb0.6.9.release-rcworkflow are copied from qbit-email../mvnw verifypasses locally (guardrails G1, G3 and G4 pass).🤖 Generated with Claude Code