ForcePilot/backend/package/yuxi/channels/plugins
Kris b88c0ae29e feat(channels): 批量新增多渠道网关限界上下文基础代码与契约
新增完整的 channels 限界上下文模块,包含契约层、领域核心层、应用服务、管道编排、插件体系、基础设施组合根等全层级代码,新增飞书与微信 iLink 渠道插件基础结构,补充各类 DTO、端口协议与领域服务实现。
2026-07-02 03:22:12 +08:00
..
feishu feat(channels): 批量新增多渠道网关限界上下文基础代码与契约 2026-07-02 03:22:12 +08:00
wechat_ilink feat(channels): 批量新增多渠道网关限界上下文基础代码与契约 2026-07-02 03:22:12 +08:00
__init__.py feat(channels): 批量新增多渠道网关限界上下文基础代码与契约 2026-07-02 03:22:12 +08:00
README.md feat(channels): 批量新增多渠道网关限界上下文基础代码与契约 2026-07-02 03:22:12 +08:00

渠道插件包

本目录存放各渠道插件实现。每个渠道插件以独立子目录形式装配,通过 CHANNEL_ENTRY 契约注册到宿主。

最高原则:渠道开发 不得 污染框架层。框架层包括 yuxi.channels 包内除 plugins/ 之外的所有层(契约层、领域核心、应用服务、组合根、被驱动适配器)。若渠道开发需要框架层变更支持,必须 走 §2 的变更申请流程,不得 自行修改。


1. 边界红线:渠道开发不得污染框架层

1.1 框架层范围

路径 插件可执行操作
契约层 yuxi.channels.contract.* 只读 import(不得新增/修改文件)
领域核心层 yuxi.channels.core.* 禁止 import
应用服务层 yuxi.channels.application.* 禁止 import
组合根层 yuxi.channels.infrastructure.* 禁止 import
被驱动适配器层 yuxi.channels.adapters.* 禁止 import
插件层 yuxi.channels.plugins.<channel>.* 可自由编写

契约层文件的增删改属于框架层变更,必须 由架构角色评审后实施,渠道开发者 不得 直接修改(见 §2

1.2 依赖方向铁律INV-1 / INV-6

依据 演进式六边形-管道-插件架构规范.md §5.1

  • 所有依赖 必须 指向契约方向(外 → 内),内圈 不得 引用外圈类型。
  • 插件 不得 直接引用宿主内部实现, 依赖 yuxi.channels.contract.*
  • 跨边界共享的数据结构 必须 定义在契约层,由内外圈共同引用。
  • 依赖方向 不得 因"便利"而绕过,任何绕过都 必须 在评审中记录理由。

1.3 插件可依赖的契约入口

插件 只能 import 以下契约层符号(适配器协议导出见 contract/plugin/adapters/__init__.py,其余符号见 contract/plugin/__init__.py

契约模块 内容
yuxi.channels.contract.plugin.entry CHANNEL_ENTRY / PluginHost
yuxi.channels.contract.plugin.manifest PluginManifest / ChannelManifest / ResourceQuota / PluginDependency / ConfigField / FailurePolicy
yuxi.channels.contract.plugin.adapters 23 个适配器 ProtocolInboundAdapter / OutboundAdapter / SessionAdapter / RichMessageAdapter / StreamingAdapter / DirectoryAdapter / CommandAdapter / WizardAdapter / DoctorAdapter / WhitelistAdapter / ToolsAdapter / MessageOpsAdapter / StatusAdapter / MentionAdapter / IdentityResolverAdapter / LifecycleAdapter / LoginAdapter / ProbeableAdapter / ContentModerationAdapter / PullerAdapter / StreamConnectorAdapter / AttachmentUploadable / WebhookTestable
yuxi.channels.contract.plugin.capability CapabilityDeclaration / CapabilityProof / CapabilityProver
yuxi.channels.contract.plugin.lifecycle LifecycleState / LifecycleHook / LifecycleHookHandler
yuxi.channels.contract.plugin.extension_point StageSlot / EventSubscription / ConfigSource / Stage / ConflictStrategy / FailureStrategy
yuxi.channels.contract.dtos.* 跨边界共享 DTO
yuxi.channels.contract.errors.* 统一错误类型层级
yuxi.channels.contract.ports.driven.* / yuxi.channels.contract.ports.driving.* 端口定义(仅供类型标注,实现由宿主注入)

适配器协议方法风格:所有适配器 Protocol 使用 @runtime_checkable 装饰,方法签名统一为 async def。方法体区分两种风格:必选方法为 raise NotImplementedError(实现类必须重写),可选方法为 return None / return False(实现类按需重写,默认不操作)。例如 InboundAdapter.normalizeInbound 为必选,InboundAdapter.downloadAttachmentFR-49 媒体下载,可选)默认 return None 表示未实现,由 MediaFetchStage 据此剔除附件并降级。LifecycleAdapterLoginAdapter 的全部方法均为必选(raise NotImplementedError),声明对应能力的插件 必须 实现全部方法。

1.4 资源访问约束INV-I7 / INV-6 / FR-32

插件 不得 直接访问以下宿主资源,必须 通过 PluginHost 提供的端口获取方法与扩展点注册方法获取依赖:

禁止直接访问 替代方式
宿主 settings host.getConfigPort()
宿主 logger host.getLoggerPort()
数据库连接池 host.getPersistencePort()
Redis 客户端 host.getCachePort()
追踪器 host.getTracerPort()
队列 host.getQueuePort()
会话存储 host.getConversationPort()
Agent 运行时 host.getAgentRunPort()
身份解析器 host.getIdentityResolverPort()
宿主中间件链 / 路由表 / 事件订阅器 host.registerStageSlot() / host.registerEventSubscription() / host.registerConfigSource() / host.registerMatchTier() / host.registerMatcher()

端口访问前置声明(关键):插件在 CHANNEL_ENTRY 中调用任何 getXxxPort() 之前必须 先调用 host.declareAccessiblePorts(...) 声明将访问的端口名称元组。宿主在 PluginHostImpl._checkPortAccess 中校验每次端口获取是否在已声明集合内,未声明时抛 PermissionDeniedError

可声明的端口名称17 个,为 PluginCapabilityChecker.ALLOWED_PORTS 的子集):

类别 端口名称
基础被驱动端口9 个) ConfigPort / LoggerPort / PersistencePort / CachePort / TracerPort / QueuePort / ConversationPort / AgentRunPort / IdentityResolverPort
仓储子端口8 个,均由 PersistencePort 聚合实现) ChannelAccountRepositoryPort / ChannelSessionRepositoryPort / PairingRepositoryPort / AuditLogRepositoryPort / OutboxRepositoryPort / UserIdentityRepositoryPort / IdempotencyRepositoryPort / PersistenceHealthPort

仓储子端口在运行时返回 DrivenAdapters.persistence 聚合别名,该聚合同时实现全部 8 个细分子端口。IdentityResolverPort 默认为 None,由插件通过 registerAdapter("identity_resolver", ...) 注入后才可获取。

1.5 适配器注册FR-32

插件通过 host.registerAdapter(adapter_type, adapter) 注入渠道适配器。宿主 PluginHostImpl._ADAPTER_SETTERS 支持 24 种 adapter_type1 个单实例 + 23 个列表追加,含 23 个适配器 Protocol + 1 个渠道上下文提供者 Port

adapter_type 写入方式 对应 Protocol / Port
identity_resolver 单实例直接赋值 IdentityResolverAdapter
inbound 列表追加 InboundAdapter
outbound 列表追加 OutboundAdapter
status 列表追加 StatusAdapter
rich_message 列表追加 RichMessageAdapter
tools 列表追加 ToolsAdapter
message_ops 列表追加 MessageOpsAdapter
directory 列表追加 DirectoryAdapter
whitelist 列表追加 WhitelistAdapter
wizard 列表追加 WizardAdapter
doctor 列表追加 DoctorAdapter
command 列表追加 CommandAdapter
mention 列表追加 MentionAdapter
streaming 列表追加 StreamingAdapter
session 列表追加 SessionAdapter
channel_context_provider 列表追加 ChannelContextProviderPort(见 contract/ports/driven/channel_context_provider_port.py为 Port 而非适配器 Protocol
lifecycle 列表追加 LifecycleAdapter(见 contract/plugin/adapters/lifecycle_adapter.pyAL-01 账户生命周期介入)
login 列表追加 LoginAdapter(见 contract/plugin/adapters/login_adapter.pyQR-01 扫码登录)
probeable 列表追加 ProbeableAdapter(见 contract/plugin/adapters/probeable_adapter.pyFR-35 主动探测,可选实现)
content_moderation 列表追加 ContentModerationAdapter(见 contract/plugin/adapters/content_moderation_adapter.pyCR-01 内容预审核,可选实现)
puller 列表追加 PullerAdapter(见 contract/plugin/adapters/puller_adapter.py,传输引擎客户端型渠道轮询接入,可选实现)
stream_connector 列表追加 StreamConnectorAdapter(见 contract/plugin/adapters/stream_connector_adapter.py,传输引擎客户端型渠道长连接接入,可选实现)
attachment_upload 列表追加 AttachmentUploadable(见 contract/plugin/adapters/attachment_upload_adapter.pyMSG-ATTACH-UPLOAD 附件上传,可选实现)
webhook_test 列表追加 WebhookTestable(见 contract/plugin/adapters/webhook_test_adapter.pyWHK-TEST Webhook 测试事件发起,可选实现)

未列出的 adapter_typeRuleViolationError

传输引擎适配器说明puller / stream_connectorTransportManager 在宿主启动时通过 PluginRegistry.listPluginAdapters() 收集,驱动 per-account 传输任务生命周期。插件注册这两类适配器后,账号启用/禁用由 ChannelAccountOnline / ChannelAccountOffline 领域事件自动触发,插件 不得LifecycleAdapter 中自管理 WS 连接或轮询任务(详见 channels-transport-engine-设计方案-v1.0.md)。

1.6 能力声明与校验

能力校验分两层,插件开发者需同时满足:

加载期静态校验(由 PluginCapabilityChecker 执行):

  1. check(manifest):校验 accessible_ports / injectable_pipelines / resource_quota.max_cpu(≤ 50.0%)是否在允许范围内,违规抛 PermissionDeniedError / ValidationError

  2. verifyCapabilityConsistency(manifest, adapters):清单 capabilities 中声明为 True 的能力 必须 有对应的运行时适配器注册FR-21 契约一致性)。映射关系:

    能力字段 要求非空的适配器列表
    rich_message rich_message_adapters
    streaming streaming_adapters
    mention mention_adapters
    command command_adapters
    directory directory_adapters
    doctor doctor_adapters
    whitelist whitelist_adapters
    lifecycle lifecycle_adapters
    supports_qr_login login_adapters
    message_edit / message_recall / supports_reaction / supports_pin / supports_card_update(任一为 True message_ops_adapters
    supports_image_inbound / supports_video_inbound(任一为 TrueFR-43 inbound_adapters
    supports_image_outbound / supports_video_outbound(任一为 TrueFR-43 outbound_adapters

    typing_indicator 无专用适配器(由出站适配器承载),不在校验范围。lifecycleAL-01supports_qr_loginQR-02PluginLifecycleManager._parse_capabilitiesmanifest.json 解析,声明为 True 时触发对应适配器列表非空校验。

运行时能力证明(由插件实现 CapabilityProver,出站中间件调用):

  • proveCapability(capability_name) 返回 CapabilityProof,结果可缓存(默认 TTL 300s
  • 静态声明支持但运行时证明不支持时,必须 记录告警日志并走降级路径FR-08 增强 / FR-21

1.7 适配器职责约束INV-8

插件提供的适配器 只做协议转换与错误翻译不得 承载业务规则、事务编排、跨请求状态。业务规则归属领域核心,事务边界归属应用服务层。

1.8 错误显式化INV-7

  • 契约违反、配置错误、插件故障 必须 显式抛出 yuxi.channels.contract.errors.* 中的统一异常,禁止 用防御性回退掩盖。
  • 禁止 静默吞错(如 except Exception: pass);观测层失败允许降级,但 必须 通过 LoggerPort 记录 warning。
  • 原生异常(网络库、协议库异常)必须 在适配器内捕获并转换为统一异常,禁止 直接向外抛出。

2. 框架层变更申请流程

当渠道开发遇到以下情况时,不得 自行修改框架层,必须 提出变更申请:

  • 契约层缺少必要端口 / DTO / Protocol 方法。
  • 组合根层缺少必要能力(如新的扩展点类型、阶段插槽锚点)。
  • 管道缺少必要的阶段插槽。
  • 端口签名需要扩展(须走版本化流程,对应 INV-4 端口契约稳定性)。
  • PluginHost 缺少必要的端口获取方法或扩展点注册方法。

2.1 申请方式

  1. docs/vibe/v1.x/问题文档/渠道开发/ 下新建问题清单文档(命名 YYYY-MM-DD-<channel>-前置问题清单.md),内容包括:
    • 缺口描述:缺少什么、为什么需要、当前无法实现的用例。
    • 影响范围:契约层 / 组合根层 / 应用层,涉及的文件路径。
    • 期望方案:建议的变更方案与替代方案。
    • 紧急程度P0阻断渠道开发/ P1影响核心能力/ P2增强能力
  2. 提交架构评审,由架构角色确认方案后,由框架开发者实施 框架层变更。
  3. 变更完成后,渠道开发者同步切换到新契约,不得 在插件内保留对旧实现的绕过逻辑。

2.2 严禁的行为

  • plugins/<channel>/ 之外的目录直接修改代码以"临时"支持某渠道。
  • 在插件内通过反射、monkey-patch、import private symbol_ 前缀)、TYPE_CHECKING 之外的方式绕过契约边界。
  • yuxi.channels.core.* 中 import 任何框架类型(违反 INV-2 核心纯净性)。
  • 自行在契约层新增 DTO / Protocol 方法以"补全"渠道所需能力。

3. 标准布局

每个渠道插件遵循以下目录结构:

plugins/
└── <channel_name>/         # 如 feishu/、dingtalk/、wecom/
    ├── __init__.py
    ├── manifest.json        # 插件清单(元数据、能力声明、配置 schema
    ├── entry.py             # CHANNEL_ENTRY 入口函数
    ├── lifecycle.py         # LifecycleHookHandler 实现(如需响应生命周期)
    ├── adapters/            # 适配器实现
    │   ├── __init__.py
    │   ├── outbound_adapter.py
    │   ├── streaming_adapter.py
    │   └── ...
    └── dtos/                # 渠道特有 DTO可选仅插件内部使用
        └── __init__.py

布局约束

  • 插件 只能 在自身 plugins/<channel>/ 子目录内创建文件。
  • 跨渠道共享的 DTO 必须 通过契约层变更申请提升到 yuxi.channels.contract.dtos.*不得 在插件间互相 import。
  • 渠道特有 DTO 不得 泄漏到契约层,留在插件自身的 dtos/ 目录内。

3.1 manifest.json schema

manifest.json 由宿主在 discovered 阶段解析为 ChannelManifest。字段如下:

字段 类型 必填 默认值 说明
id string 全局唯一标识(如 com.yuxi.channels.feishu
name string 人类可读名称
version string 语义化版本
channel_type string 渠道类型枚举值(feishu / dingtalk / wecom / webchat / telegram / discord / whatsapp / custom
entry_module string 插件入口模块路径Python import 路径,如 yuxi.channels.plugins.feishu.entry
capabilities object 能力集合(见 ChannelCapabilities 字段,未声明默认 falsemax_message_length 默认 4096
config_schema array 配置项 schema每项含 key / type / required / default / hot_reloadable / constraints
provides array 提供的能力列表
lifecycle array 支持的生命周期钩子(如 ["init","start","stop","unload"]
compatibility object 兼容性信息(自由结构 dict由插件自行约定语义框架仅透传不解释
failure_policy string 失败策略(degrade / circuit_break / isolate
depends array [] 依赖的其他插件(每项含 plugin_id / version_range
resource_quota object null 资源配额(max_cpu ≤ 50.0、max_memorymax_connectionsmax_calls_per_sec
accessible_ports array [] 可访问的端口名称列表(必须为 §1.4 中 17 个端口的子集)
injectable_pipelines array [] 可注入的管道列表(inbound / outbound / control-plane 的子集)
skills array [] 提供的技能列表
env_vars array [] 环境变量声明(每项含 name / description / required / default / sensitive
critical bool false 是否为关键渠道(失败时宿主标记 unhealthy,否则标记 degradedFR-31 / FR-35
requires_dm_pairing bool true 是否要求 DM 安全配对审批(声明 false 跳过配对审批FR-31插件未注册时框架回退为 truefail-closed
requires_outbound_delivery bool true 是否要求出站投递(声明 false 无需实现 OutboundAdapter框架跳过出站投递阶段FR-31

必填字段说明provides / lifecycle / compatibility / failure_policyChannelManifest dataclass 中虽有默认值,但 PluginLifecycleManager._parse_manifestrequired_fields 校验将它们标记为 JSON 必填,缺失时抛 ValidationError

entry_module 说明:宿主通过 importlib.import_module(manifest.entry_module) 动态导入,必须为可 import 的完整模块路径。返回的 PluginManifest.manifest.id 必须manifest.jsonid 一致,否则抛 ValidationError

capabilities 解析范围PluginLifecycleManager._parse_capabilities 完整解析 ChannelCapabilities 的 20 个字段——13 个基础能力(rich_message / streaming / typing_indicator / message_edit / message_recall / mention / command / directory / doctor / whitelist / supports_reaction / supports_pin / supports_card_update、4 个媒体能力(supports_image_inbound / supports_video_inbound / supports_image_outbound / supports_video_outboundFR-43、1 个消息长度上限(max_message_lengthFR19-P0-5、2 个新增能力(lifecycle AL-01 / supports_qr_login QR-02。未声明的字段默认 False4096。能力字段声明为 True 时,加载期由 PluginCapabilityChecker.verifyCapabilityConsistency 校验对应适配器列表非空(见 §1.6)。


4. 插件加载时序

依据 演进式六边形-管道-插件架构规范.md §9插件生命周期状态机PluginStateMachine

discovered → resolved → loaded → initialized → started ⇄ paused
                                    ↓                ↓
                                  failed ←─────── (任一阶段失败)
                                    ↓
                                  resolved (重载FR-36)

started/paused → stopped → unloaded (终态)

完整加载流程(由 PluginLifecycleManager 编排):

  1. discovered:宿主扫描 plugins/ 目录下每个子目录的 manifest.json,解析为 ChannelManifest 并注册到 PluginRegistry。无 manifest.json 的子目录被跳过;解析或注册冲突记录错误日志并跳过该插件不阻塞其他插件FR-21
  2. resolvedPluginCapabilityChecker.check 校验能力边界(accessible_ports / injectable_pipelines / resource_quota)。
  3. loadedimportlib 导入 entry_module,调用 CHANNEL_ENTRY(host) 获取 PluginManifest,校验清单 ID 一致性。插件在 CHANNEL_ENTRY 内通过 host.registerAdapter 注入适配器,通过 host.registerStageSlot / registerEventSubscription / registerConfigSource / registerMatchTier / registerMatcher 注入扩展点,通过 host.registerLifecycleHandler 注册生命周期钩子。随后 verifyCapabilityConsistency 校验声明能力与运行时适配器一致性FR-21
  4. initialized:调用 LifecycleHookHandler.onInit(超时 60s
  5. started:调用 onStart(超时 60s插件开始处理请求。成功后将 manifest.version 追加写入 applied_migrations 配置键ACCOUNT 作用域FR17-P0-3标记配置已加载写入失败抛异常触发降级。
  6. paused / resumed:调用 onPause / onResume(超时 30s
  7. stopped:调用 onStop(超时 30s必须等待在途请求完成
  8. unloaded:调用 onUnload(超时 30s必须释放所有资源注销扩展点注册PluginRegistry 注销。
  9. failed(任一阶段失败):标记 FAILED 状态,发布 PluginFailed 事件,调用 onFail 钩子(超时 30s触发 DegradationManager 优雅降级FR-36发布 ChannelDegraded 事件,释放插件占用的扩展点注册与 host 实例。失败插件由后台任务(reloadLoop,默认 60s 间隔)按退避时间自动重载(FAILED → RESOLVED → … → STARTED)。

生命周期钩子完整列表(见 LifecycleHookHandler

钩子方法 触发阶段 超时 用途
onInit initialized 60s 初始化资源(连接、缓存预热)
onStart started 60s 开始处理请求
onPause paused 30s 暂停接收新请求
onResume started从 paused 30s 恢复接收请求
onStop stopped 30s 等待在途请求完成
onUnload unloaded 30s 释放所有资源
onReconfigure 配置热更新FR-37 接收新配置字典,必须 支持回滚
onFail failed 30s 失败清理(由宿主在 _handle_failure 中调用)

reloadLoop 多 Worker 保护:后台扫描任务通过 CachePort.acquireAdvisoryLock("plugin_reload_scanner", ttl_seconds=interval) 获取分布式咨询锁,锁未获取时跳过本轮,避免多 Worker 重复重载失败插件§10.2 并发控制)。正常路径在 finally 中释放锁,锁 TTL 与扫描间隔一致作为崩溃恢复安全网。

生命周期约束

  • 插件 不得started 之前对外提供服务。
  • 插件 必须 支持 stoppedunloaded 的干净退出,不得 遗留线程、连接、临时资源。
  • 插件失败 不得 拖垮宿主宿主负责按扩展点策略降级或熔断FR-36
  • 生命周期钩子超时后标记插件 FAILED 并触发降级。
  • onReconfigure 必须 支持配置回滚FR-37配置应用失败时恢复至上一次有效配置。

5. CHANNEL_ENTRY 契约

每个插件 必须 通过 CHANNEL_ENTRY 契约注册,不得 隐式全局注入FR-32

# entry.py
from yuxi.channels.contract.plugin.entry import CHANNEL_ENTRY, PluginHost
from yuxi.channels.contract.plugin.manifest import (
    ChannelManifest,
    PluginManifest,
    FailurePolicy,
    ResourceQuota,
)
from yuxi.channels.contract.dtos.capability import ChannelCapabilities
from yuxi.channels.contract.dtos.channel import ChannelType
from yuxi.channels.contract.plugin.lifecycle import LifecycleHookHandler

from .adapters.outbound_adapter import FeishuOutboundAdapter
from .adapters.streaming_adapter import FeishuStreamingAdapter
from .lifecycle import FeishuLifecycleHandler


def channel_entry(host: PluginHost) -> PluginManifest:
    """飞书渠道插件入口。"""

    # 1. 声明能力边界(必须在 getXxxPort 之前)
    host.declareAccessiblePorts((
        "ConfigPort",
        "LoggerPort",
        "PersistencePort",
        "CachePort",
        "ConversationPort",
    ))
    host.declareInjectablePipelines(("inbound", "outbound"))
    host.declareResourceQuota(ResourceQuota(max_cpu="20.0", max_connections=50))

    # 2. 注册生命周期钩子onInit/onStart/onStop/onUnload
    #    registerLifecycleHandler 当前仅在 PluginHostImpl 实现,
    #    尚未声明到 PluginHost Protocol运行时通过鸭子类型调用。
    host.registerLifecycleHandler(FeishuLifecycleHandler())

    # 3. 注册渠道适配器adapter_type 见 §1.5
    host.registerAdapter("outbound", FeishuOutboundAdapter())
    host.registerAdapter("streaming", FeishuStreamingAdapter())

    # 4. 注册扩展点(按需)
    #    host.registerStageSlot(StageSlot(pipeline="outbound", anchor="after:format", stage=...))
    #    host.registerEventSubscription(EventSubscription(event_type="ConfigChanged", handler=...))
    #    host.registerConfigSource(ConfigSource(source_id="feishu_env", loader=...))

    # 5. 返回 PluginManifest
    return PluginManifest(
        manifest=ChannelManifest(
            id="com.yuxi.channels.feishu",
            name="飞书",
            version="1.0.0",
            channel_type=ChannelType.FEISHU,
            provides=("feishu",),
            entry_module="yuxi.channels.plugins.feishu.entry",
            capabilities=ChannelCapabilities(
                rich_message=True,
                streaming=True,
                typing_indicator=True,
            ),
            config_schema=(),
            lifecycle=("init", "start", "stop", "unload"),
            compatibility={"min_host_version": "1.0.0"},
            accessible_ports=(
                "ConfigPort",
                "LoggerPort",
                "PersistencePort",
                "CachePort",
                "ConversationPort",
            ),
            injectable_pipelines=("inbound", "outbound"),
            failure_policy=FailurePolicy.DEGRADE,
        ),
        adapters=("outbound", "streaming"),
    )


CHANNEL_ENTRY = channel_entry

registerLifecycleHandler 说明PluginHost Protocolentry.py)当前未声明 registerLifecycleHandler / getLifecycleHandler 方法,但 PluginHostImpl 已实现。插件运行时可通过鸭子类型调用;如需静态类型检查支持,走 §2 变更申请流程补充 Protocol 声明。


6. 禁止事项清单Code Review Checklist

提交渠道代码前,逐项确认:

6.1 边界

  • 未修改 plugins/<channel>/ 之外的任何文件。
  • 未 import yuxi.channels.core.* / yuxi.channels.application.* / yuxi.channels.infrastructure.* / yuxi.channels.adapters.*
  • 未新增 / 修改 yuxi.channels.contract.* 中的任何文件(契约层变更走 §2 流程)。
  • 未在插件间互相 import 内部实现(跨渠道共享走契约层提升)。
  • 需要框架层支持时已走变更申请流程,未自行突破边界。

6.2 资源访问

  • 未直接访问 settings / logger / DB pool / Redis client。
  • 在调用任何 getXxxPort() 之前已通过 declareAccessiblePorts(...) 声明端口。
  • 所有被驱动依赖通过 PluginHost.getXxxPort() 获取。
  • 未修改宿主中间件链、路由表、事件订阅器(仅通过 registerXxx() 注入)。
  • accessible_ports 为 §1.4 中 17 个端口的子集,injectable_pipelinesinbound / outbound / control-plane 的子集。
  • resource_quota.max_cpu 不超过 50.0%。

6.3 契约与异常

  • 通过 CHANNEL_ENTRY 显式注册,未隐式全局注入。
  • manifest.jsonentry_module 为可 import 的完整模块路径,返回清单 ID 与声明 ID 一致。
  • manifest.json 包含全部必填字段(id / name / version / channel_type / entry_module / capabilities / config_schema / provides / lifecycle / compatibility / failure_policy)。
  • 清单 capabilities 声明为 True 的能力均有对应运行时适配器注册FR-21
  • 声明 lifecycle: true 时已注册 LifecycleAdapter,声明 supports_qr_login: true 时已注册 LoginAdapter(见 §1.6)。
  • 注册 ProbeableAdapter 时已实现 async def probe(self) -> AdapterProbeOutcome(从 yuxi.channels.contract.plugin.adapters 导入FR-35
  • 适配器只做协议转换与错误翻译不承载业务规则INV-8
  • 原生异常已转换为 yuxi.channels.contract.errors.* 统一异常。
  • 未用 try/except 静默吞错INV-7观测层降级必须有 LoggerPort warning。

6.4 生命周期

  • 通过 host.registerLifecycleHandler(handler) 注册了 LifecycleHookHandler(如需响应生命周期)。
  • 未在 started 之前对外提供服务。
  • stoppedunloaded 干净退出,无遗留线程 / 连接 / 临时资源。
  • onInit / onStart 在 60s 内完成,onPause / onResume / onStop / onUnload / onFail 在 30s 内完成。
  • onReconfigure如实现支持配置回滚FR-37
  • onFail(如实现)不抛异常,失败清理逻辑不得阻断降级流程。

7. 参考

契约层源码

  • entry.pyCHANNEL_ENTRY / PluginHost
  • manifest.pyPluginManifest / ChannelManifest / ResourceQuota / FailurePolicy
  • capability.pyCapabilityDeclaration / CapabilityProof / CapabilityProver
  • lifecycle.pyLifecycleState / LifecycleHook / LifecycleHookHandler
  • extension_point.pyStageSlot / EventSubscription / ConfigSource / Stage / ConflictStrategy / FailureStrategy
  • adapters/ — 23 个适配器 Protocol15 个基础 + LifecycleAdapter + LoginAdapter + ProbeableAdapter + ContentModerationAdapter + PullerAdapter + StreamConnectorAdapter + AttachmentUploadable + WebhookTestable;已删除 MediaAdapter / OAuthAdapter 孤儿协议)
  • contract/plugin/__init__.py — 契约层导出清单entry/manifest/capability/lifecycle/extension_point 全量 + 适配器 23 个全量导出)

应用层实现(仅供理解宿主行为,插件 不得 import

架构规范