Catch it before it ships wrong. Consensus is an ambient consistency layer for Slack — a contradiction firewall for organizational memory. It notices when your team makes a decision, remembers it with full provenance, and catches anyone about to contradict it — live, across channels, permission-aware — before the mistake ships.
Built for the Slack Agent Builder Challenge 2026 (Track 1: Best New Slack Agent).
- Ambient decision capture — no slash commands. An LLM classifier spots settled decisions in normal conversation ("we're standardizing on Postgres") and files them in a Decision Ledger with statement, rationale, decider, timestamp, and permalink. Handles meeting-notes dumps too: one message containing several decisions yields several ledger entries, and "What we decided: …" recap phrasing is captured as well.
- Live contradiction detection — messages are checked against active decisions by a scope-aware LLM judge. Catches casual-language contradictions ("lets just spin up MongoDB") with no keyword dependence, and warns the author with a private, ephemeral alert: receipt, confidence, and This is intentional — supersede / Not a conflict / Show reasoning buttons.
- Permission-aware by construction — if the conflicting decision lives in a private channel the author can't see, the alert is redacted: the conflict is flagged, but no statement, channel, or link is revealed. Membership checked per alert, fail-closed. The App Home decision log is permission-filtered per viewer too.
- Provenance on demand — ask
@Consensus why did we choose Postgres?and get the real ledger entry with the original thread as proof, augmented by Slack's Real-Time Search API on the local per-user-OAuth path ([ledger]vs[live search]results are never conflated). - Edit-sync & delete-retirement — edit a captured message and Consensus reconciles the ledger (keep / retire / add) with a quiet "✏️ ledger synced" note; delete a decision and it's retired, so no ghost rule keeps firing.
- It learns — every Not a conflict becomes persistent false-positive memory; precision is tracked on the App Home dashboard.
- Consistency audit — on demand, Consensus scans your standing decisions (up to the 60 most recent) in a two-stage sweep (LLM scan → verification by the measured judge, bidirectional) and surfaces latent contradictions — pairs of decisions that already conflict without anyone noticing. Permission-filtered per viewer, DM'd from App Home; run in a channel it reports public-decision conflicts only.
Ambient capture → cross-channel contradiction alert → proposed-vs-active governance → latent-conflict audit → dashboard.
Judges: it's live in our sandbox, and nothing below is scripted. (Self-hosting? The same steps work once your instance is running.)
- Capture — post in any channel:
Decision: all demo scripts must be reviewed before recording.→ a 📌 Decision captured card appears in-thread (~15s). - Contradict it — from a different channel — in
#random, casually type:gonna record the new demo without review, no time→ you get a private⚠️ alert quoting the original decision, with a confidence score and Show reasoning · Not a conflict · This is intentional. - Teach it — click Not a conflict on any alert → Consensus remembers and won't flag that pairing again.
- Ask for provenance —
@Consensus what have we decided about databases?→ a grounded answer with permalinks (ledger + live Real-Time Search). - Open the dashboard — Consensus in the sidebar → Home tab → the permission-filtered decision log, alerts fired, and tracked precision.
The showpiece — permission redaction. Decisions from a private channel are guarded too, but an alert about one is redacted for non-members — no statement, no channel, no link. Ask about a decision from a channel you're not a member of, and Consensus refuses. Permission-aware, fail-closed.
The contradiction judge ships with an eval harness — 58 hand-labeled cases including scope-different near-misses, sarcasm, hypotheticals, negation traps, and 9 adversarial prompt-injection attacks (including Unicode-homoglyph, HTML-entity, and zero-width/RTL-override delimiter-break attempts):
Cerebras gemma (fallback) 58/58 · Precision 1.000 · Recall 1.000
Cerebras GLM-4.7 (hosted brain) 57/58 · Precision 1.000 · Recall 0.964 (one expired-freeze time-scoping case)
Claude (local dev, Agent SDK) 56/58 · Precision 0.964 · Recall 0.964 (same time-scoping case + one FP)
0 hard-fails on all three · 9/9 adversarial injections defeated on every stack
Receipts committed for every stack — hosted EVAL-RESULTS-hosted.txt, gemma EVAL-RESULTS-hosted-gemma.txt, local Claude EVAL-RESULTS.txt — same prompts throughout. The harness hard-fails on LLM errors (parse failures count as hard errors, with one transient retry, so a dead model can never score) and reports precision as UNDEFINED with zero predicted positives. Run it: npm run eval.
The scores are high because the judge is good, not because the test is soft — read the cases yourself in consensus-core/eval/dataset.js: 20 near-misses (same technology different scope, agreeing negations, expired time windows, superseded decisions, sarcasm) sit alongside the 9 injection attacks, all chosen to break a naive matcher. Untrusted content is NFKC-normalized before delimiter-wrapping, so none of the payloads flip the verdict. A keyword bot fails these; the scope-aware judge doesn't.
- Real-Time Search API (
assistant.search.context) — live workspace search in both paths: the hosted brain augments provenance answers with live public-channel search (public-only by construction — the token is the app owner's, so restricting to workspace-public content makes it leak-proof for any asker), and the local per-user-OAuth path searches the full permission-awaresearch:read.*scope as the requesting user (demoed in the video). - Slack MCP Server — powers the agent's Slack tool-use (search / read / write) in the conversational path, where the Claude Agent SDK runs a real multi-turn tool loop over the MCP tools (load-bearing there; shown in the video). The hosted cloud brain is a single-shot completion with no tool loop by design, so it grounds on the ledger + live RTS instead.
- Slack AI / Agent & Assistant surface — conversational provenance Q&A (both paths)
flowchart TD
evt(["Slack message event"])
subgraph ambient ["Ambient pipeline"]
pre["Pre-filter<br/>dedup, length, keywords, rate guard"]
clf["Decision classifier - LLM"]
con["Contradiction engine"]
judge["Scope-aware judge - LLM"]
gate{"Permission gate<br/>fail-closed membership check"}
end
ledger[("Decision Ledger - MongoDB / SQLite")]
subgraph surfaces ["Surfaces"]
alert["Ephemeral alert<br/>full or REDACTED"]
home["App Home dashboard"]
end
audit["Consistency audit<br/>scan LLM then judge verify"]
edits(["Edits / deletes"])
evt --> pre
pre --> clf
clf --> ledger
pre --> con
con --> judge
judge --> gate
gate --> alert
ledger -. "candidates" .-> judge
ledger --> home
ledger --> audit
audit -. "two-stage verify" .-> judge
edits -- "re-sync" --> ledger
Key modules — all in consensus-core/:
| Module | Role |
|---|---|
pipeline.js |
Ambient brain: pre-filter, capture, contradiction check, alerting |
judge.js |
LLM classifier + scope-aware contradiction judge, injection-hardened (<untrusted_*> wrapping) |
ledger.js |
Decision ledger + dismissal memory + event log — MongoDB (durable, hosted) when MONGODB_URI is set, SQLite (WAL) / JSON fallback locally |
permissions.js |
Fail-closed membership gate with 5-min cache |
blocks.js |
Block Kit surfaces (cards, alerts, App Home) with mrkdwn sanitization |
rts.js |
Real-Time Search wrapper (fail-open, 3s timeout) |
eval/ |
Dataset + harness + recorded results |
npm install
slack run # installs to your sandbox and starts via Socket ModeRequires the Slack CLI and a developer sandbox. Dual model stack: local dev runs Claude via the Claude Agent SDK; the hosted deployment runs Cerebras GLM-4.7 (CEREBRAS_API_KEY), with a gemma / GEMINI_API_KEY fallback. For Real-Time Search, complete the per-user OAuth flow via node app-oauth.js. Full self-host and deploy steps are below.
Consensus is fully self-hostable — nothing is locked to a vendor, and there's no marketplace listing to wait on. Any team can stand up its own private instance in ~20 minutes:
- Clone & install — clone the repo, then
cd claude-agent-sdk && npm install. - Create your Slack app — from the included
manifest.json(Slack CLIslack create, or api.slack.com/apps → Create New App → From a manifest). Turn on Socket Mode. - Get two tokens — the Bot token (
xoxb-…, from OAuth & Permissions) and an App-level token (xapp-…, scopeconnections:write, from Basic Information → App-Level Tokens). - Bring your own keys — set
SLACK_BOT_TOKEN,SLACK_APP_TOKEN, and your own model key:CEREBRAS_API_KEY(default,zai-glm-4.7) orGEMINI_API_KEYor local Claude via the Agent SDK — the chain inconsensus-core/llm.jspicks whichever is present. SetMONGODB_URIfor durable storage, or omit it to fall back to a localnode:sqlite/ JSON file (no external service needed just to try it). - Run it —
node app.jslocally, or deploy the includedrender.yamlto Render (how our 24/7 instance runs). Then invite the bot to the channels you want it to guard.
Your instance runs entirely on your keys, your workspace, your data — nothing routes through us. A one-click install + admin dashboard (zero developer setup) is the next item on our roadmap.
- Ephemeral-first alerts — nobody is called out publicly
- Consent-first: the bot introduces itself on channel join; remove it to opt out
- Human-in-the-loop: the agent proposes, people confirm; it never silently rewrites the record
- Prompt-injection hardened: untrusted content is delimiter-wrapped and framed as data on every untrusted surface; measured against 9 adversarial injection patterns (all defeated on every model stack)
- Private-channel decision content never leaks: per-alert and per-viewer membership checks, unknown privacy treated as private. (App Home shows only workspace-wide aggregate counters — alerts fired, precision — never private decision content, and labeled as workspace-wide.)
- Hardened by adversarial review: earlier multi-model hostile passes (GPT, Gemini, and a GPT-Codex code+claims audit) were triaged — real findings fixed and remaining limitations documented openly, not claimed away — including audience-gated provenance (channel replies cite public decisions only; DMs are membership-gated), per-user sessions, precise claim scoping, and rate + queue + audit metering
- Trust model is "members are colleagues" (company workspaces, not open-invite communities): anyone can state a decision and anyone can correct the ledger — but every action is public, attributed, and event-logged, so manipulation is visible and reversible rather than silently prevented. Abuse blunting is built in: per-user dismissal memory (nobody can silence alerts for anyone else), a per-author daily capture cap (ledger flooding is throttled), and per-user/global rate guards. Full raid-resistant admin controls (member-tenure gating, role-gated corrections) are roadmap
- Admin control dashboard + one-click install — an install-to-org OAuth flow and a self-serve config surface to opt channels in/out, tune sensitivity, and manage permissions in one place, so any team can roll Consensus out in a click.
- Windowed dashboard stats (7 / 30-day) — the schema is already there.
- Member-tenure gating & role-gated corrections for open-invite communities.
- Deeper permission-aware search and cross-message decision stitching.
Built on the official bolt-js-starter-agent template (MIT). Decision engine, ledger, permission gate, eval harness, and all Consensus surfaces are original work for this hackathon.






