Chat Channel Capability Matrix

chat-channels specs/chat-channels/capability-matrix.kmd

Canonical feature surface that every Koder bot chat channel (Telegram, WhatsApp, WebChat, Signal, Google Chat, Koder Chat, and future) must declare via `Channel.Capabilities()`. The matrix in `meta/docs/stack/registries/chat-channels-parity.md` audits the current state across the implemented channels.

When this spec applies

All triggers

Specification body

Spec: Chat Channel Capability Matrix

Defines the contract every concrete chat channel adapter under core/bot-server/internal/channel/ must honor. Each channel implementation reports its real support level via the Capabilities() Capabilities method on the Channel interface.

The Koder bot-server runtime uses these capabilities to:

  • Route tools that depend on platform features (e.g. set_chat_photo only fires on channels with Admin.SetPhoto = true).
  • Skip multimodal history re-injection on channels without Receive.Image (#010).
  • Decide whether to send progress updates as edits or new messages (#015) based on Send.EditMessage.
  • Lint the actual implementations against the parity registry — drift must surface as a CI failure, not a runtime surprise.

1 — Capability categories

A channel's capabilities are grouped into seven domains. Each domain contains a small, closed list of named features. Adding a feature requires updating both this spec and the Capabilities struct in internal/channel/channel.go in the same change.

1.1 — Receive (inbound message kinds)

Feature Wire type the channel can receive from a user
Text Plain or formatted text message
Image Photo / static image
Video Video file (MP4, MOV, WebM)
Audio Audio file (MP3, M4A, AAC) — distinct from voice notes
Voice Push-to-talk voice note (Opus/OGG on Telegram)
Document Arbitrary file attachment
Location Geo-coordinates (lat/lon)
Contact vCard / shared phone number
Sticker Sticker / GIF / animated reaction sticker
ReplyThread Quote-and-reply with parent-id preserved
Edit Inbound edit of a previous message
Reaction Emoji reaction on a message

1.2 — Send (outbound rendering)

Feature What the channel can deliver back to the user
Text Plain text — every channel must support this; required
Image, Video, Audio, Voice, Document, Location, Sticker Mirror of Receive set
MarkdownFormatting Bold, italic, inline code, code block, lists
EditMessage In-place edit of a previously-sent bot message (gates real progress, #015)
DeleteMessage Delete a previously-sent bot message
Typing Transient "typing…" indicator (TypingSender interface)
Thinking Persistent "thinking…" message with cleanup (ThinkingIndicator interface)

1.3 — Admin (chat administration)

Feature Capability
SetPhoto Change chat avatar (BOTSRV-008)
SetTitle Change chat title
SetDescription Change chat description / topic
AddMember, RemoveMember Membership management
Promote, Demote Role assignment
ListMembers Enumerate participants
Leave Bot can leave the chat

1.4 — Identity (who is sending)

Feature Field exposed
UserName Display name
UserEmail Email address — Telegram = no, Google Chat = yes
UserAvatarURL Profile picture URL
ChatTitle Group/channel title
ChatType private / group / supergroup / channel
MemberCount Number of members in chat

1.5 — History (persistence semantics)

Feature Behavior
PassiveLogging Persists messages from approved chats even when the agent does not respond (#011). Required for "leia o histórico" to work
MultimodalHistory Carries attachments forward across turns when the LLM backend is multimodal (#010)

1.6 — Commands (built-in / bot-driven)

Command Status
/clear Required if Admin.DeleteMessage is true
/help Required — every channel honors
/status Required — health summary (see #013-adjacent)
/model Required when bot supports per-session model override (#013)
/keyboard Optional — channel-specific quick-reply rendering
/setavatar Optional — only when Admin.SetPhoto is true

1.7 — Gate (access control)

Feature Behavior
InviteCode First-time DM gated by invite code
ApprovedChatList Group chats explicitly opted-in by admin
OwnerOnly Restricted to a specific user-id (KODE_OWNER_USER_ID)
PublicByDefault No gate — every visitor can interact

2 — Capabilities struct

Concrete shape lives in core/bot-server/internal/channel/capabilities.go (introduced by #009 implementation). A channel returns a value with the booleans set to its real support level — the runtime never reads or assumes anything beyond what the channel declares.

type Capabilities struct {
    Receive   ReceiveCapabilities
    Send      SendCapabilities
    Admin     AdminCapabilities
    Identity  IdentityCapabilities
    History   HistoryCapabilities
    Commands  CommandsCapabilities
    Gate      GateCapabilities
}

Each domain is a struct of named bools. New features extend the struct (additive change, never reorder).

Required (every channel MUST set true):

  • Send.Text — without this the bot is silent.
  • Identity.UserName — without name, gate flow breaks.
  • Gate.PublicByDefault OR (Gate.InviteCode AND Gate.ApprovedChatList) — must declare some access model.
  • Commands./help — entry point for users who don't know what to do.

Recommended (channel SHOULD support if the platform allows):

  • Send.MarkdownFormatting, Send.EditMessage, Send.Typing
  • Receive.Image, Receive.Document, Receive.Voice
  • History.PassiveLogging (required for groups), History.MultimodalHistory (required when Receive.Image=true)

Platform-dependent (declared per-channel, no global default):

  • Admin.* — only platforms that grant bots admin privileges
  • Identity.UserEmail — only platforms with verified email
  • Receive.Reaction, Receive.Edit — only platforms that surface those events to bots

4 — Compliance audit

Audit lives in meta/docs/stack/registries/chat-channels-parity.md, one row per channel × one column per feature. CI enforcement (#009 acceptance criterion 3) runs the audit on every PR via koder-housekeep:

  1. For each channel implementation, instantiate it with mock config.
  2. Call Capabilities(), serialize to a deterministic JSON shape.
  3. Compare against the registry's recorded shape.
  4. Fail if drift detected.

This makes the registry a single source of truth that drifts visibly when a channel's Capabilities() changes — preventing the silent skew that motivated this spec (the 08/05 Telegram group test).

5 — Adding a new channel

When introducing a new platform (#016 adds Signal / Google Chat / Koder Chat), the implementer:

  1. Reads this spec + the registry to know the canonical surface.
  2. Writes the channel impl honoring Channel.Capabilities().
  3. Adds a row to the registry with the supported features.
  4. Runs the housekeep audit; CI must be green.
  5. Documents auth setup in meta/docs/stack/modules/core-bot-server.md.

A channel may declare a feature false and ship — that is honest. What a channel must not do is declare a feature true while the implementation either crashes or silently no-ops on that path. The audit catches mismatches between declaration and behavior via the test fixtures referenced in Capabilities() itself.

References