Promises made in Slack. Kept.
Your team makes promises to customers in shared Slack channels every day — "we'll ship the SSO fix for Acme by Friday." Those commitments then scatter across Jira, CI, and feature flags, and it's far too easy to tell the customer it's done before it actually is.
Kept tracks every one of those promises from the moment it's made in Slack, and checks it against live delivery evidence before anyone calls it done. A human confirms the promise is worth tracking. Kept then reads the systems where the work actually ships — Jira, GitHub Actions, and LaunchDarkly — assembles the proof, and a human signs the verdict before it closes the loop back in the original thread.
The connectors are where it gets sharp. Suppose the Jira ticket is Done and CI passed — but the LaunchDarkly production flag is still OFF. The feature never actually shipped. Kept catches that and keeps the promise open, evidence in view: Jira Done ✓ · GitHub Actions passed ✓ · LaunchDarkly prod flag OFF ✗ → not ready to close. Your ticketer tracks status; Kept checks whether the promise is real.
🏁 Try it yourself in 90 seconds (judges: this is where Kept wins)
Don't take this description's word for it — Kept wouldn't. In the sandbox:
- In a channel, run
/kept customer Acme, then post "we'll ship the SSO fix by Friday." - Confirm the private card (Gate 1).
- Click ✅ Mark delivered in App Home → the Proof-of-Done packet appears.
- Press Verify — and get refused (
INSUFFICIENT_EVIDENCE) if the flag is OFF. - Flip the flag ON → Verify again → it closes → reply "works now" in-thread to confirm.
You will be personally blocked from closing a promise that isn't actually live. That refusal is the product.
The concept exists. Here's how Kept improves on it.
| Pylon / Thena / ClearFeed | Kept | |
|---|---|---|
| Unit of work | a ticket / conversation | a first-class obligation with its own lifecycle |
| "Done" means | a status field flipped | proof reconciled — flag ON + merge + prod deploy, a customer confirmation, or a human attestation |
| Who acts | agent routes / auto-replies | agent assembles proof; a human signs the verdict |
| Customer safety | manual care | audience sanitizer + leak detector on every customer-facing word, by construction |
| Multi-tenant | product feature | tenant isolation as a P0 invariant — a cross-tenant read is a security bug the tests hunt for |
The improvement isn't a nicer inbox. It's a different definition of "done" — one that can't be faked by a stale ticket — enforced by code, not a prompt.
What it does
Kept maintains a human-verified, event-sourced obligation ledger for shared customer channels. One obligation's journey:
- Capture. A message lands in a shared channel. Kept's LLM layer classifies and proposes a candidate obligation. Nothing is committed.
- Gate 1 — Confirm (human). The owner gets a private card. Only on Confirm does it become tracked.
- Prove. A delivery signal arrives (webhook, or the owner clicks Mark delivered). The agent autonomously assembles Proof-of-Done — it reads the flag state (LaunchDarkly over MCP), the CI conclusion (GitHub Actions), the ticket status (Jira) — and proposes each as evidence.
- Reconcile. Ticket-Done alone is never enough. A flag that's OFF in production blocks the close even with merge + deploy in hand.
- Gate 2 — Verify (human). When proof is sufficient, the owner signs. The agent did 95%; the human signs.
- Close the loop. Kept drafts a sanitized, leak-checked closure and posts it back into the original thread — only after approval.
- Outlive the ticket. If the customer replies "still failing," the obligation reopens — even if the ticket stays Done forever.
Integrations are optional. A team with no automated proof source can still drive the full loop — the owner manually attests delivery, which is sufficient to verify unless a connected proof source contradicts it (a flag that's OFF still wins). The customer identity is anchored to the channel (/kept customer <name>), not re-guessed from each message.
The three qualifying technologies (all genuine)
- Slack AI Assistant — a conversational pane over the ledger ("what's slipping?"). The LLM routes the question into a fixed intent grammar; deterministic code runs the read.
- Model Context Protocol (MCP) — Kept is a deterministic MCP client. The money proof source, LaunchDarkly flag state, runs over real hosted MCP (
mcp.launchdarkly.com, verified live:get-flag→ flag OFF). Code picks every tool + argument; the model never selects a tool. - Real-Time Search API — Kept's Assistant calls
assistant.search.context(bot token + the eventaction_token, granularsearch:read.public— never the Marketplace-banned blanketsearch:read); results are ephemeral and nothing is persisted. Slack gates RTS to directory-published apps, so it lights up the moment Kept's Marketplace listing is approved — until then it falls back to a ledger-backed retriever, so the app works either way.
Built for organizations
- Multi-workspace OAuth, HTTP mode with
@slack/bolt+ aPostgresInstallationStore— per-tenant bot tokens. - Tenant isolation is a P0 invariant. Every read is scoped by
team_id; proof credentials are strictly per-tenant (no installer ever reads through the operator's accounts); a cross-tenant read is treated as a security bug. - Authenticated webhooks (no forged proof), gate cards that lock after one click (no double-verify, no duplicate customer post), and the hosted app never fabricates a work item.
- Minimal, granular scopes; no banned scopes.
How we built it
TypeScript (ESM, Node 20+), on Fly.io + Postgres. The keystone: the LLM proposes; code decides. An inbound message goes to proposeFromMessage, which classifies via forced tool-use with Zod validation and returns a candidate Command — nothing else. The deterministic heart is decide(): a pure (events, command, ctx) → Decision with no I/O. State is never a mutable row — an obligation is a pure fold of an append-only event log. Zero-copy: no raw Slack body, prompt, or model response is ever persisted — assertNoRawContent scans names and values before every append.

Accomplishments we're proud of
- 299 hermetic tests + a live integration suite — the engine, the MCP round-trip, the Proof-of-Done gate, tenant isolation, and the trust page, all deterministic.
- 9 adversarial rounds — the latest a multi-agent, workflow-driven sweep that found (then fixed, then locked with regression tests) two real gaps: proof adapters falling back to the operator's credentials for another tenant, and an evidence row mislabelling a live read.
- Proof-of-Done that actually blocks — the flag-OFF case isn't a slide; the engine refuses to verify with ticket Done + merge + deploy in hand, then flips to green the instant the flag goes ON.
What we learned
Trust is an architecture, not a feature. The most valuable thing an agent can do in a customer channel is refuse to act until the right conditions hold — and prove why. Human-in-the-loop is a feature, not a tax, if the agent does the assembling: "the agent does 95%; you sign."
What's next for Kept
Per-source webhook HMAC (replacing the shared-secret stand-in), the hosted Atlassian MCP for Jira (needs OAuth), Slack Connect auto-derivation of the customer, richer reconciliation lanes, and Enterprise Grid org-wide install — all derivable from the existing event log.
Built With
- fly.io
- github
- jira
- launchdarkly
- mcp
- node.js
- postgresql
- slack
- slack-ai
- slack-rts
- typescript
- vitest
- zod
Log in or sign up for Devpost to join the conversation.