# EBA Adapter Acceptance Checklist This checklist is the architecture-level acceptance standard for every Event-Based Agents platform adapter. It is not platform-specific. Adapter migration is not complete until the adapter has a written result against this checklist. ## Evidence Levels Use these evidence levels consistently in adapter records: | Level | Meaning | Can Mark Complete | |-------|---------|-------------------| | `plugin-e2e` | Real SDK plugin running through standalone runtime, LangBot core, the migrated adapter, and a real or simulator platform endpoint. | Yes | | `adapter-live` | Direct adapter probe connected to a real or simulator platform endpoint, bypassing plugin runtime. | No, auxiliary only | | `unit` | Unit/API-shape tests with mocked platform SDK objects or mocked APIs. | No, auxiliary only | | `not-supported` | Platform protocol or SDK has no equivalent capability. Must include reason and source. | Yes, as explicitly unsupported | | `blocked` | Intended capability could not be verified because of credentials, permissions, endpoint gaps, or simulator gaps. | No | The primary acceptance path must be `plugin-e2e`. `adapter-live` and `unit` tests are useful, but they do not prove the EBA architecture path. ## Required Architecture Path Every adapter must prove this full path: ```text Real platform / simulator UI -> platform SDK native event -> adapter event converter -> unified EBA event/entity/message types -> LangBot core event dispatch -> standalone SDK runtime -> real test plugin listener -> plugin calls platform APIs through SDK -> LangBot core API dispatch -> adapter API implementation -> real platform / simulator UI ``` The test plugin must record JSONL evidence containing: - event class and `event.type` - adapter name - chat type and chat ID - sender/user/group IDs with secrets redacted - message component list for received messages - API action name, input summary, result or error - raw unsupported/blocked reason when an item is skipped ## Required Message Receive Tests For every adapter, inbound message conversion must be tested through `plugin-e2e` for each component the platform can receive. If the platform cannot create a component from the UI/simulator, record it as `blocked` with the endpoint limitation. | Component | Required Receive Assertion | |-----------|----------------------------| | `Source` | Message ID and timestamp are present and stable enough for reply/get/delete APIs. | | `Plain` | Text is preserved exactly, including spaces and multi-line content. | | `At` | Mentioned user ID is converted to common `At.target`. | | `AtAll` | Broadcast mention is converted to common `AtAll`, if platform supports it. | | `Image` | Image ID, URL, path, or base64 is represented without leaking platform-native segment shape. | | `Voice` | Voice/audio component is represented as `Voice` when the platform exposes it. | | `File` | File name, ID/URL, and size are represented as `File` when available. | | `Quote` | Reply/quote source ID and origin content are represented when the platform exposes it. | | `Face` | Native emoji/sticker/dice/rps-like components are represented as `Face` or documented as platform-specific. | | `Forward` | Merged/forwarded messages are represented as `Forward` when the platform exposes structured content. | | `Unknown` | Unsupported native segments become `Unknown` or `PlatformSpecificEvent` data, not crashes. | | Mixed chain | A message containing multiple component types preserves order. | The plugin must subscribe to `MessageReceivedEvent` and assert that `message_chain` contains common `langbot_plugin.api.entities.builtin.platform.message` components, not platform-native SDK objects. ## Required Message Send Tests For every adapter, outbound message conversion must be tested through `plugin-e2e` by having the plugin call SDK platform APIs and verifying the platform UI/simulator receives the expected message. | Component | Required Send Assertion | |-----------|-------------------------| | `Plain` | Text appears exactly on the platform. | | `At` | User mention renders as a mention or platform equivalent. | | `AtAll` | Broadcast mention renders or is explicitly unsupported. | | `Image` | URL, path, or base64 image sends and renders/downloads correctly. | | `Voice` | Voice/audio sends when supported. | | `File` | File sends with name and content/link when supported. | | `Quote` | Quoted reply points to the original message when supported. | | `Face` | Native emoji/sticker/dice/rps sends or is explicitly unsupported. | | `Forward` | Forward/merged-forward sends when supported; otherwise fallback behavior is documented. | | Mixed chain | A mixed chain preserves component order as closely as the platform allows. | If a platform supports a component only in one direction, the adapter record must say so explicitly. ## Required Event Tests The plugin must subscribe to every event declared in `manifest.yaml -> spec.supported_events` and record one of `plugin-e2e`, `not-supported`, or `blocked`. | Event | Required Assertion | |-------|--------------------| | `message.received` | Real message reaches plugin as `MessageReceivedEvent`. | | `message.edited` | Edited message reaches plugin with message ID and new content, if declared. | | `message.deleted` | Deleted/recalled message reaches plugin with message ID and operator when available, if declared. | | `message.reaction` | Reaction add/remove reaches plugin with message ID, user, reaction, and direction, if declared. | | `feedback.received` | Feedback payload reaches plugin with feedback type and message/session IDs, if declared. | | `group.member_joined` | Join event reaches plugin with group and member. | | `group.member_left` | Leave/kick event reaches plugin with group, member, and kick flag. | | `group.member_banned` | Mute/ban event reaches plugin with group, member, operator, and duration. | | `group.info_updated` | Group metadata update reaches plugin with changed fields, if declared. | | `friend.request_received` | Friend request reaches plugin with request ID and message. | | `friend.added` | Friend-added event reaches plugin. | | `friend.removed` | Friend-removed event reaches plugin, if declared. | | `bot.invited_to_group` | Bot invite/join request reaches plugin with group and inviter/request ID. | | `bot.removed_from_group` | Bot removal reaches plugin with group and operator when available. | | `bot.muted` | Bot mute reaches plugin with duration. | | `bot.unmuted` | Bot unmute reaches plugin. | | `platform.specific` | At least one unmapped native event is delivered as structured platform-specific data, if declared. | Do not declare an event in the manifest unless there is an implementation path and an acceptance entry. ## Required Common API Tests The plugin must call every common API declared in `manifest.yaml -> spec.supported_apis.required` and `optional`. Each call must be recorded with input summary and result. | API | Required Assertion | |-----|--------------------| | `send_message` | Plugin sends to private and group/channel targets where supported. | | `reply_message` | Plugin replies to the triggering message, with quoted mode tested when supported. | | `edit_message` | Plugin edits a bot-sent message, if declared. | | `delete_message` | Plugin deletes/recalls a bot-sent message, if declared and permissions allow. | | `forward_message` | Plugin forwards or emulates forwarding a real message, if declared. | | `get_message` | Plugin retrieves a real message and receives common `MessageReceivedEvent` shape. | | `get_group_info` | Plugin receives `UserGroup` with ID/name/count where available. | | `get_group_list` | Plugin receives joined groups/channels list where supported. | | `get_group_member_list` | Plugin receives list of `UserGroupMember` where supported. | | `get_group_member_info` | Plugin receives one member with role/display name where available. | | `set_group_name` | Plugin changes and restores a disposable group name, if declared. | | `mute_member` | Plugin mutes a disposable target, if declared. | | `unmute_member` | Plugin unmutes the same target, if declared. | | `kick_member` | Plugin kicks a disposable target only in destructive test mode, if declared. | | `leave_group` | Plugin leaves only in destructive test mode and only at the end, if declared. | | `get_user_info` | Plugin receives common `User` shape. | | `get_friend_list` | Plugin receives friend/contact list where supported. | | `approve_friend_request` | Plugin accepts/rejects a disposable friend request, if declared. | | `approve_group_invite` | Plugin accepts/rejects a disposable group invite, if declared. | | `upload_file` | Plugin uploads a real small file, if declared. | | `get_file_url` | Plugin resolves a real file ID to a URL, if declared. | | `call_platform_api` | Plugin calls every declared platform-specific action with safe parameters. | Destructive APIs must be opt-in and documented with the exact target used. ## Platform-Specific API Tests Every action listed in `manifest.yaml -> spec.platform_specific_apis` must have one acceptance entry: - `plugin-e2e`: called by the plugin against the live/simulator endpoint. - `not-supported`: removed from manifest or explained if the platform SDK exposes it but this adapter intentionally does not. - `blocked`: endpoint did not implement it, permissions missing, or safe fixture unavailable. Do not leave a platform-specific API in the manifest without a corresponding test record. ## Required Compatibility Tests Each migrated adapter must also prove: - Manifest supported events match `adapter.get_supported_events()`. - Manifest supported APIs match `adapter.get_supported_apis()`. - Manifest platform-specific actions match `PLATFORM_API_MAP`. - Legacy `FriendMessage` / `GroupMessage` listeners still work when the core registers them. - EBA listener dispatch prefers the most specific event class, then `EBAEvent`, then base `Event`. - Self-message filtering prevents bot echo loops without dropping edit/delete/moderation events needed for API tests. - `source_platform_object` is present for reply/debug but not required by plugins for common behavior. ## Required Documentation Per Adapter Each adapter document must include: - adapter directory and manifest name - config table - supported event table with evidence level per event - supported common API table with evidence level per API - platform-specific API table with evidence level per action - receive component table with evidence level per component - send component table with evidence level per component - exact test date - exact platform endpoint or simulator used - standalone runtime command - plugin path/name used for testing - evidence JSONL path - destructive operations performed or explicitly skipped - blocked items and reasons ## Acceptance Rule An adapter can be marked migrated only when: 1. All declared events have `plugin-e2e` or `not-supported` evidence. 2. All declared APIs have `plugin-e2e` or `not-supported` evidence. 3. All platform-supported receive/send message components have `plugin-e2e` evidence. 4. Unit tests cover conversion and API-shape boundaries. 5. The adapter document lists every blocked or skipped item honestly. If any declared capability is only covered by `adapter-live` or `unit`, the adapter status must remain partial.