Krahnie — System Overview

A high-level map of how Krahnie is put together: the Slack-native delivery-ops assistant for Krahnborn's Salesforce work.

1 · What Krahnie is

Krahnie is a Slack-native assistant for Krahnborn's Salesforce delivery work. The team talks to it in Slack (DMs, or by @-mention in channels); it routes each message to one of ~30 tools that read/write Salesforce and reply with Block Kit cards. It also runs proactive work (digests, reminders), captures feedback, hands engineering cards off to coding agents, and cross-syncs with clients' own ticketing systems.

It runs as a single Python service connected to Slack over Socket Mode and to Salesforce over the REST API.

2 · Architecture

Slack ──Socket Mode──▶ Gateway ──▶ Orchestrator ──▶ Tools ──▶ Salesforce REST (events, slack_gateway.py agent.py (Pydantic-AI, tools/*.py salesforce.py (multi-org) buttons, · fast-ack + route Haiku router) · @tool functions ──▶ GitHub modals, · mention-gating · per-convo history · post Block Kit ──▶ Coding agents reactions) · easter-egg shortcuts · prompt caching · ctx.posted signal ──▶ Client orgs (sync)

Four layers, each with a clear seam:

Why Pydantic-AI + Haiku? The orchestrator is a cheap, fast router. Heavier reasoning (card enrichment, summaries) is delegated to Claude Sonnet inside specific tools, so most turns cost a fraction of a cent.

3 · Request lifecycle

A chat message

Slack event ─▶ @app.event("message"|"app_mention") → fast-ack, work runs in background _handle_message: 1. drop bot msgs / edits / empty / non-allowlisted users 2. CHANNEL GATE: if not a DM and not @-mentioned → ignore (§11) 3. strip the mention; easter-egg short-circuit (§8) 4. build RequestContext; run_turn(text, ctx) (agent.py) 5. a tool posted a card? stay silent; else post the reply text 6. log the turn cost

A button / modal / reaction

Interactivity never goes through the LLM — it's wired directly: buttons → @app.action, modal submits → @app.view, searchable selects → @app.options, slash commands → @app.command, reactions → @app.event("reaction_added"). The pattern is always ack() first, then run the work in the background.

4 · Core building blocks

RequestContext context.py

A frozen dataclass stamped per turn and read by tools: who (slack_user_id), where (channel, thread_ts), what (text), a posted flag (a tool appends to it when it posts a card → the gateway then stays silent), and a cost sink.

The tool contract tools/__init__.py

Every tool is an @tool(name, description, input_schema) function. Its description is the routing signal the model reads; prompts.py carries the explicit "if the user says X → call tool Y" directive. Adding a tool = write the function, register it in build_tools, add a routing line.

Confirm-before-commit pending.py

Risky writes never fire on the first message. The tool posts a Confirm/Edit/Cancel card and stashes the proposed payload under a nonce; the button handler loads it and commits. This is the spine of every "are you sure?" flow.

Block Kit blockkit.py

Pure render functions returning (blocks, fallback_text); all action ids are constants here. slackio.py is the thin Slack Web API wrapper.

5 · Salesforce & the client registry

salesforce.py talks to Salesforce over the REST API across multiple orgs, caching auth per org. clients.py reads a per-client registry (clients.yaml, refreshed per call, zero model-context cost) that resolves nicknames to accounts and holds per-client routing/defaults (channel, board, org aliases, repo, ticketing config). The model never sees this file — resolution happens deterministically in tool code.


6 · Feedback capture

The team reports what they were trying to do, what happened, and pastes the chat — so tools get hardened. NL "log feedback" or /krahnie-feedback opens a modal whose submission writes a Krahnie_Feedback__c record. feedback.py + tools/feedback.py.

7 · Coyote voice

The brand persona is a literal contract: prompts.py ends with a VOICE block — the Coyote (terse, clever-not-cute, confident, proactive). Voice lives in acks, nudges, and help; data stays neutral.

8 · Easter eggs

Handled before the agent (free + instant). Message-based: "hello friend", "ship it", team legends, :linux:×3, the konami code, "am I cooked", "coffee". Reaction-based: :linux: → a Linux fact in the thread; :man-playing-handball: → a warm fuzzy (§9).

9 · Warm fuzzies

tools/warmfuzzy.py. "@krahnie we got a warm fuzzy! shout out the SDI team" — or a :man-playing-handball: reaction — posts a celebratory shout-out to the warm-fuzzies channel, quoting the praise and crediting the team/person.

10 · Card create

Flow: tools/createcard.py → a confirm card → writes.create_card. The model extracts a description; the confirm card offers two buttons — Create (plain) and ✨ Create + AI insights (opt-in Sonnet enrichment: story points, next steps, follow-ups) — and the created card carries an ✏️ Edit button into the edit modal.

11 · Channel behavior

12 · Agent handoff — deep link Mode B

"Hand off CARD-1234 to an agent" → a Claude Code deep link the dev clicks to open Claude Code locally, pre-loaded with the card brief (title, description, triage notes). agentwork.py assembles the brief and builds the link; the human drives from there.

13 · Agent handoff — Codex first pass Mode A

An autonomous attempt that opens a draft PR for review. From the handoff card, a "⚡ First pass" button opens a confirm modal (review/edit the repo, the target sandbox, and the branch), then Krahnie runs Codex against the card brief in an isolated container, scoped to a chosen sandbox org, and opens a draft PR. Human-in-the-loop by design: it proposes, a person reviews and merges. codexwork.py + agentwork.py.

14 · Ticket sync (cross-org)

Krahnie-mediated sync between a client's own Salesforce ticket object and Krahnborn's Card__c — no middleware. ticketsync.py.

INBOUND pull(client): their tickets changed since a watermark → upsert Card__c (idempotent by External_Ticket_Id__c), request-side fields only OUTBOUND push(card): crosswalked status + a client-visible note → back to the ticket

15 · Data model

ObjectRole
Card__cthe unit of delivery work (status, due, estimate, assignee, AI insights, sync cross-refs)
Work_Log__ctime entries
Board__c / Project__c / Accountstructure: board → project → account/client
Krahnie_Feedback__cteam feedback on Krahnie

Custom metadata is source-controlled as an SFDX project. clients.yaml holds the per-client registry (name, aliases, channel, board, org aliases, repo, ticketing) plus a dev-sandboxes list.

16 · Module map

FilePurpose
slack_gateway.pySocket Mode transport: events, interactivity, mention-gating, easter eggs, handlers
agent.pyPydantic-AI orchestrator (Haiku), tool adaptation, per-convo history, prompt caching
prompts.pySystem prompt: routing directive + the Coyote voice
salesforce.pyMulti-org REST: query / create / patch / upsert, per-org token cache
clients.pyClient registry: resolve, repo, orgs, channel→client, dev sandboxes, ticketing
writes.pyWrite layer: Work_Log + Card inserts/updates, modal builders, renderers
blockkit.pyPure Block Kit renderers + action-id constants
enrich.py / refresh.py / pm.pySonnet reasoning · week digest · team rollups & metrics
agentwork.py / codexwork.pyHandoff core (brief, deep link, modal) · Codex first pass
ticketsync.pyCross-org ticket sync (pull/push, crosswalk)
scheduler.py / state.py / usage.pyDurable scheduler · snooze state · cost/usage log
tools/*.py~30 tools — week, cards, logtime, createcard, editcard, cost, metrics, pm, research, reminders, leaderboard, help, feedback, handoff, warmfuzzy…

17 · How to extend

Add a tool

  1. New tools/foo.py: make_foo_tool(get_ctx) returning an @tool(...) function that posts via slackio and marks ctx.posted.
  2. Register it in tools/__init__.py.
  3. Add a routing line in prompts.py.

Add a confirm-card flow

  1. Render the card in blockkit.py with new action-id constants; stash the payload via pending.save.
  2. Register the action handler in slack_gateway.py that loads the nonce and commits.

Krahnie — Krahnborn's Salesforce delivery-ops assistant. Generated overview.