supports_* capability is verified once, at adapter build time β an unbacked flag fails loudly before any turn instead of raising mid-conversation.
An agent runs behind a Bot; when you build that bot with a custom adapter, the contract catches a broken declaration before the first message.
Quick Start
1
Declare what you support truthfully
Set
supports_edit=True and override edit_message β the adapter passes verification.2
The verifier catches a broken promise
Remove the override and the same declaration fails at build time β not mid-turn.
How It Works
The check iteratesCAPABILITY_BACKING and asks, per declared flag, whether the backing method is overridden.
The two check entry points
Two functions read the same contract; one reports, one raises.verify_capability_contract(adapter) -> list[str] returns a list of human-readable violation strings β empty when the contract holds β and never raises. Use it to log a warning without failing startup.
enforce_capability_contract(adapter) -> None raises a single CapabilityContractError naming every violation (joined by "; "), or does nothing. It is called automatically by Bot._build_adapter and by the gatewayβs _build_channel_adapter.
Where the check runs automatically
Both build paths enforce the contract for you β there is no flag or environment variable to opt in. When you build aBot(...) with a wrapper adapter (praisonai-bot), the contract is verified right after adapter construction, guarded by a lazy import so an older core release simply skips the check. On the gateway construction path (gateway/server.py::_build_channel_adapter), a violation is caught the same way and the channel is recorded as degraded β visible in gateway status β rather than surfacing later inside a live turn.
Backward compatibility
Built-in adapters already honour the contract, so nothing needs to change. All built-in adapters that declaresupports_edit=True (Slack, Telegram, Discord) already override edit_message. No built-in adapter declares supports_delete=True, so no built-in adapter trips the contract. Capabilities left False continue to degrade cleanly, unchanged.
_resolve_capabilities prefers the typed platform_capabilities property; a legacy dict-shaped capabilities attribute is ignored so it is never misread as the transport descriptor. This is what makes wrapper bots like Slack / Telegram / Discord verify correctly.Adding a new capability flag
Extend the contract by pairing a new flag with a base method that has a default.1
Add the supports_* field
Add the field to
PlatformCapabilities (dataclass plus to_dict / from_dict).2
Pick a backing method
Choose a method the base class provides a default for.
3
Add the pair to CAPABILITY_BACKING
Register
"supports_new": "new_method" in the fail-closed map.4
Gate the base default (optional)
If the base default should raise on a declared-but-unbacked call (parity with
edit_message), gate the default on the flag and raise NotImplementedError otherwise.Best Practices
Declare only what your platform actually does
Declare only what your platform actually does
A lie in
PlatformCapabilities is now caught immediately at build time. Declare supports_edit or supports_delete only when a real implementation backs them.Prefer enforce_ in production, verify_ in dev tooling
Prefer enforce_ in production, verify_ in dev tooling
enforce_capability_contract fails startup loudly. verify_capability_contract returns violations without raising, so dev tooling can log a warning and keep going.New flag β new backing method entry
New flag β new backing method entry
Every entry in
CAPABILITY_BACKING is a fail-closed guard. Adding a flag with its backing method keeps future regressions from advertising an unimplemented capability.Related
Bot Platform Capabilities
The
PlatformCapabilities descriptor whose flags this contract verifies.Bot Platform Adapter
Build a channel β declare capabilities and back them honestly.
Bot Platform Plugins
Runtime registration and discovery of platform adapters.
Custom Gateway Channel (Zero-Code)
The gateway construction path that records a violation as a degraded channel.

