Day 14 — 'half a handshake.'
Slack pairing was supposed to work end-to-end but had a dangling thread: the Slack adapter told the user to run `/social code` inside `werbos chat` to claim a conversation, but `/social code` wasn't actually registered as a slash command anywhere. Operator side of the loop was open. Two commits: wire `/social code` into both the CLI REPL and the server slash registry, then add `pair <CODE>` (no slash) as an alias because Slack reserves the `/`-prefix locally and intercepts it before our webhook ever sees the text. 2 commits.
“half a handshake.”
The bug yesterday’s Slack work shipped on top of: the pairing
handshake was half-built. Slack would tell the operator to run /social code in werbos chat, but typing that into werbos chat
got back “unknown command: /social” — it dropped through to
buddy’s catch-all. The store had mint_pairing_code() /
redeem_pairing_code(), the Slack adapter knew /pair <CODE>,
but nothing in between minted a code the operator could send.
/social code wired in both REPL paths
The slash command surface in werbos has two parallel paths:
- The CLI REPL (
werbos chat) matches slashes in_bare_human_turn()and dispatches to_handle_X_slash()functions — each with its own SQLite store, returning a(print_msg, send_text)tuple. - The server slash registry (
werbos/server/slash.py) handles HTTPPOST /api/chats/{id}/messagesslashes through aSlashRegistry+SlashContextdiscriminator. Used by the dashboard chat composer and any HTTP client.
/social code ships in both (e0d3784). The CLI handler opens
~/werbos/social.db (override via WERBOS_SOCIAL_DB), the
server handler reads app.state.social_store. Optional
platform_hint constrains redemption to one platform. 5 new
server-side tests including an end-to-end round-trip:
mint → redeem with the same store → identity gets
paired_at. Pins the contract that the handler isn’t
accidentally hashing twice or returning the wrong value.
Then Slack ate /pair
User minted a code, DM’d @werbos /pair bh2rFF — Slackbot
intercepted with "/pair is not a valid command." Slack
treats any /-prefixed message as a slash command and drops
it before the app’s webhook sees the text. So the documented
pairing form silently failed on the very platform it was
designed for.
Two-line fix (0cd43b8):
- Accept
pair <CODE>as an alias to/pair <CODE>. Slack’s slash-command interception only fires on the leading/, so the no-slash form lands cleanly. lstrip()the inbound text before prefix matching. Slack’s own suggested workaround — “prefix the slash with a space” — now actually delivers a working pairing.- Bot’s pair-prompt template updated: tells the user to type
pair <CODE>first, with/pair <CODE>mentioned parenthetically for platforms that don’t reserve the slash (Telegram, WhatsApp, Twitter).
Two new regression tests in test_router.py (no-slash form +
leading-whitespace form). Telegram/WhatsApp/Twitter continue
to accept the legacy /pair <CODE> form unchanged — those
platforms deliver /-prefixed text verbatim.
Lesson the day taught
Every platform has its own opinions about what a slash means.
Slack reserves them. The IRC-era convention that “slashes
are how you talk to the bot” doesn’t survive the move to
modern chat platforms. werbos’s surface needs to assume
no slash as the canonical form and treat /-prefixed
shapes as opt-in aliases for platforms that allow them.