OpenClaw’s Slack member-info Fix Turns Sender Identity Into a Scoped Runtime Contract

“Default to the current user” is one of those product conveniences that sounds harmless until you ask the only question that matters: current according to whom?

That is the identity lesson in OpenClaw PR #89236, a Slack member-info fix merged June 19. The immediate bug is simple. The Slack tool schema made userId optional, but dispatch still required it unconditionally. Users in a same-account Slack conversation could not omit their own user ID even though the schema said they could. The fix lets a same-account Slack turn default to the trusted inbound sender, while cross-channel and cross-account calls still require an explicit userId.

That sounds like ergonomics. It is actually boundary work. Sender IDs are not universal identities. A Slack user ID is scoped to a workspace and token context. A Discord user ID is different again. A Gateway-owned dispatch path may carry the message somewhere the original plugin context is no longer obvious. If the runtime has only a string that looks like a user ID, it does not have enough proof to default safely.

The PR is large because the real fix is not “make the field optional.” It carries trusted requester account identity through local and Gateway-owned message action dispatch via an additive optional requesterAccountId field, preserves explicit userId precedence, and only defaults to the inbound sender when the source provider and canonical Slack account match. Verification listed by the PR includes targeted Vitest coverage across Slack dispatch, plugin dispatch, Gateway send, and message-tool tests — 275 tests passed. A later land-ready comment adds generated protocol proof with 177 tests passed, Swift compatibility, and changed-surface validation/build coverage.

Optional fields need runtime proof

Tool schemas can lie by omission. A parameter marked optional is not automatically safe to omit. It is optional only if the runtime has a valid way to derive it. For Slack member-info, the missing value can be derived from the inbound sender only inside a verified Slack provider/account context. Outside that context, omission is ambiguity.

This distinction matters because agent tools are increasingly invoked through layers: local clients, Gateway dispatch, plugins, generated protocol clients, mobile wrappers, background sessions, and sometimes other agents. Context that is obvious at the UI edge may disappear by the time a tool executes. The user said something in Slack. The tool runs in a shared dispatch layer. Which Slack account? Which workspace token? Which inbound identity? Was the call initiated by the user, by an agent acting on behalf of the user, or by another channel trying to inspect a Slack member?

The fix’s provider/account match is the right shape. It keeps the convenient behavior where it is justified and forces explicit input at the boundary. That is a pattern agent platforms should use broadly. Defaults are not bad. Ambient defaults are bad. A scoped default says, “inside this proven context, we can infer the value.” An ambient default says, “we found a plausible string and hope it means the same thing everywhere.” One is product polish. The other is a future incident report.

Identity bugs start small

A member lookup is not the scariest possible tool. It reads profile information through Slack’s users.info method, which Slack documents as returning member information for a workspace and requiring a user argument. But low-risk identity bugs are useful because they reveal design habits before those habits reach high-risk tools.

If a runtime mis-scopes a member-info default, the same mental model may appear in send-message, delete-message, pin, invite-user, admin operations, repo writes, calendar changes, or cloud actions. The severity changes, but the class is the same: a tool assumes “current” without carrying enough metadata to prove currentness.

Multi-agent systems make this sharper. Agents often bridge between reasoning contexts and real workspace identities. A support bot might receive a Slack DM, delegate a coding task, call a plugin, and then send a result back through Gateway-owned dispatch. If identity metadata is treated as informal ambient state, it will eventually be wrong. The correct approach is typed, propagated, auditable requester context: provider, canonical account, sender, conversation, and authorization scope.

Convenience is allowed; crossing accounts is not

The PR’s behavior threads a useful needle. Requiring a Slack user to provide their own user ID in the current Slack conversation is pointless friction. People should not have to know that Slack calls it U123... to ask “who am I?” or “show my profile.” But letting an omitted userId default globally would be reckless. A workspace-local ID from one account token should not be silently reused with another token. A non-Slack channel should not be able to trigger Slack member lookup without saying which user it means. A generated client should not fill in identity from stale process state.

This is the kind of boundary that separates an agent platform from a pile of integrations. Integrations often optimize for the happy path: make the call work when the user is standing right next to the tool. Platforms have to preserve meaning after the call moves through dispatchers, protocol generation, plugins, restarts, and mixed-version clients. The additional requesterAccountId field is not exciting, but it is the kind of connective tissue that lets a default remain trustworthy across those layers.

The review thread also notes likely Discord parity debt. That is the correct follow-up. Once you find a schema-vs-dispatch mismatch in one channel tool, audit the rest. Look for every tool that says userId, channelId, conversation, target, or “current.” Then ask whether the runtime carries the provider/account proof needed to infer it. If not, either make the parameter required or propagate the missing context explicitly.

What teams should audit

Builders should start with a simple inventory. List every tool that defaults to current user, current channel, current workspace, current account, or current conversation. For each one, identify the metadata that proves the default. Provider alone is not enough. Sender ID alone is not enough. Account ID alone may not be enough if the channel supports multiple workspaces or bot tokens. The proof needs to match the action’s authority boundary.

Then test the negative cases. Invoke the tool from the same provider but a different account. Invoke it from another channel. Invoke it through Gateway dispatch. Invoke it through a generated client that does not know the new optional field. Invoke it with an explicit target and confirm explicit input wins. The useful test is not “does the default work?” It is “does the default refuse to work when it lacks proof?”

OpenClaw’s Slack fix is small in user-visible terms, but it points at a mature rule for agent tooling: convenience must be scoped by identity, not vibes. The current user is not a string. It is a relationship between provider, account, conversation, sender, token, and request path. Carry that relationship through the runtime, or make the user be explicit.

Sources: OpenClaw PR #89236, Slack users.info documentation, Discord parity review comment, land-ready verification comment