ADR-0030 整份落地:交互原语改 host 注册开放 registry + 移除四个能力位(趁零 host,破坏性变更现在免费) #104

Closed
opened 2026-07-30 06:15:45 +00:00 by KumaAgent · 0 comments
Member

ADR-0030 整份从未落地。 它是 2026-07-29 那轮全 ADR 查漏的起因,却不在查漏产出的 77 条里——因为当初把它连同 MediaKind.file 一起当作"标定种子"写进扫描 prompt、明确让扫描者不要重复报告,于是它既没被报告、也没被单独开票,直到有人追问"还有待办吗"才发现。这是第三次同型漏项(另两条见 #102),机制一样:提到过、当成"已知"、然后没有归宿。

趁零 host 做:核实两个 host 仓(dxkuma-bot-ob11 / dxkuma-bot-qq)的 uv.lock 里 arise 零命中,即至今没有任何 host 集成 arise。本票包含移除四个能力位、改 ConversationInput.interaction 形状、删两个 EgressPort 方法——都是破坏性变更,现在做是免费的;ob11 接入之后就变成两边协调的迁移

现状核实(对 origin/main @ 0ea65b7

ADR-0030 决策 代码现状
四个能力位 poke/poke_send/reaction_receive/reaction_send 整体移除 capabilities.py:22-25 四个全在tools.py/runtime_loop.py 仍在按它们门控
新增 ArisePorts.interaction_renderers 全仓零命中
ConversationInput.interaction 改开放形状 conversation.py:11 仍是 InteractionKind = Literal["poke", "reaction"]
egress 侧改走工具港 EgressPort.poke_send/reaction_send 仍在,POKE_SEND/REACTION_SEND 仍是 core 工具

方向是净简化:删四个能力位 + 两个 core 工具 + 两个 EgressPort 方法,换一个注册表 + 一层固定信封。InteractionEventsrc/只有 conversation.py 一个消费点,改动面比听起来小。

ADR-0030 决策原文(逐条要落的)

  • ConversationInput.interaction 改为开放形状{kind: str, target_is_self: bool, actor_id: str, target_id: str | None, payload: dict},取代原封闭 InteractionEvent{kind: Literal["poke","reaction"], target_message_id, emoji}kind 是任意字符串,core 不理解其语义
  • target_is_self 由 host 判定并上报:host 拿原始平台事件与自己的 bot user_id 判断,core 只吃这个布尔值做路由,不参与判断。
  • 路由不变,机制变了target_is_self=True → ADR-0029 统一评估管线(事件触发、全量上下文);False → ADR-0014 的 EnvironmentSignal(复用其 actor_id/subject_id)。ADR-0023「命中本人走 reactive、旁观走环境信号」这条边界判断本身不变,只是判定者从"core 按 kind 认"改成"host 上报布尔值"。
  • 渲染改为 host 注册ArisePorts.interaction_renderers: dict[str, IngressInteractionRenderer],host 在 configure() 时按 kind 注册 render(payload: dict) -> strcore 仍拥"信封"——固定包一层
    <interaction kind="..." sender_id="..." sender_name="..." time="...">{host 渲染内容}</interaction>,与 <message> 同一套入口序列化外壳。第三方事件(target_is_self=False不需要这套渲染,直接复用 EnvironmentSignal 现有表示。
  • poke/reaction 不作为 core 内置预注册项保留:彻底清空,变成 host 自己注册的普通 registry 成员,与未来任何新增交互原语地位相同,不享有特殊待遇
  • 四个具名能力位整体移除:「registry 里有没有注册」本身就是支不支持的完整信号,避免布尔位与注册表状态不同步——与 ADR-0003 对 @all/@user/@face 的处理原则相同(那次也是"注册表状态即能力信号,不设独立能力位",issue #28 已验证过这个模式)。
  • egress 侧改走工具港(ADR-0019),不开对称的 egress registry:host 把"发一次戳一戳/回应"注册成普通自定义工具,core 全程黑盒透传。

Acceptance criteria

  • ConversationInput.interaction 改为开放形状;InteractionKind 封闭 Literal 删除。
  • ArisePorts.interaction_renderers 注册表 + core 侧固定信封渲染;kind 未注册时的行为要明确(建议沿用 tags.py 既有先例:未注册即整体剔除、不留占位,见 issue #28)。
  • target_is_self 路由:True → 统一评估管线;FalseEnvironmentSignal路由行为不得改变,只换判定者。
  • 四个能力位从 CapabilitySet 删除,tools.py/runtime_loop.py 相应门控删除。
  • EgressPort.poke_send/reaction_send 删除,POKE_SEND/REACTION_SEND core 工具删除——host 改经工具港注册。
  • 注意穷举守卫CapabilitySet 少四个字段,若有按字段枚举的测试/守卫(get_args 或字段清单式断言)会红,那正是它该做的——顺着红的地方改,不要绕开守卫
  • 测试侧:InteractionEventtests/ 有 3 处引用(runtime_loop_helpers.py/test_conversation.py/test_runtime_loop.py),加上能力位相关用例,一并适配。
  • 不要写成空转测试:验证「未注册 kind 被剔除」时,断言不能是"输出里没有那段文本"——那在渲染压根没实现时同样成立。要有反向对照(已注册 kind 必须正常渲染出来)。
  • 文档同步(docs 分支,按规则不推送):ADR-0004「决策」节至今仍列着 poke 位,而 ADR-0030 宣布移除了它——这是一处已存在的 ADR 间矛盾,本票落地后追加更新节消掉它。ADR-0030 自身追加"已实现"更新节。CONTEXT.md「交互事件」「能力探测」两条术语核对。按本仓惯例只追加 ## 更新(…) 节,不就地改写既有「决策」/「拒绝了」。

Not in scope

  • ADR-0023 不作废:ADR-0030 明写它取代的是 ADR-0023 的封闭 kind 设计,而「命中本人走 reactive、旁观走环境信号」这条边界判断保留有效。别顺手把 ADR-0023 整份标废。
  • 不新开对称的 egress registry(ADR-0030 明确拒绝,egress 走工具港)。
  • host 侧怎么判 target_is_self、怎么注册 renderer —— 那是 host 的事,本票只交付 core 契约。
  • 其余 31 条实现类落差 → #103。
> **ADR-0030 整份从未落地。** 它是 2026-07-29 那轮全 ADR 查漏的**起因**,却不在查漏产出的 77 条里——因为当初把它连同 `MediaKind.file` 一起当作"标定种子"写进扫描 prompt、明确让扫描者**不要重复报告**,于是它既没被报告、也没被单独开票,直到有人追问"还有待办吗"才发现。**这是第三次同型漏项**(另两条见 #102),机制一样:提到过、当成"已知"、然后没有归宿。 > > **趁零 host 做**:核实两个 host 仓(`dxkuma-bot-ob11` / `dxkuma-bot-qq`)的 `uv.lock` 里 arise **零命中**,即至今没有任何 host 集成 arise。本票包含移除四个能力位、改 `ConversationInput.interaction` 形状、删两个 `EgressPort` 方法——**都是破坏性变更,现在做是免费的;ob11 接入之后就变成两边协调的迁移**。 ## 现状核实(对 `origin/main` @ 0ea65b7) | ADR-0030 决策 | 代码现状 | |---|---| | 四个能力位 `poke`/`poke_send`/`reaction_receive`/`reaction_send` **整体移除** | `capabilities.py:22-25` **四个全在**,`tools.py`/`runtime_loop.py` 仍在按它们门控 | | 新增 `ArisePorts.interaction_renderers` | **全仓零命中** | | `ConversationInput.interaction` 改开放形状 | `conversation.py:11` 仍是 `InteractionKind = Literal["poke", "reaction"]` | | egress 侧改走工具港 | `EgressPort.poke_send`/`reaction_send` 仍在,`POKE_SEND`/`REACTION_SEND` 仍是 core 工具 | **方向是净简化**:删四个能力位 + 两个 core 工具 + 两个 `EgressPort` 方法,换一个注册表 + 一层固定信封。`InteractionEvent` 在 `src/` 里**只有 `conversation.py` 一个消费点**,改动面比听起来小。 ## ADR-0030 决策原文(逐条要落的) - **`ConversationInput.interaction` 改为开放形状**:`{kind: str, target_is_self: bool, actor_id: str, target_id: str | None, payload: dict}`,取代原封闭 `InteractionEvent{kind: Literal["poke","reaction"], target_message_id, emoji}`。`kind` 是任意字符串,**core 不理解其语义**。 - **`target_is_self` 由 host 判定并上报**:host 拿原始平台事件与自己的 bot user_id 判断,**core 只吃这个布尔值做路由**,不参与判断。 - **路由不变,机制变了**:`target_is_self=True` → ADR-0029 统一评估管线(事件触发、全量上下文);`False` → ADR-0014 的 `EnvironmentSignal`(复用其 `actor_id`/`subject_id`)。ADR-0023「命中本人走 reactive、旁观走环境信号」这条**边界判断本身不变**,只是判定者从"core 按 kind 认"改成"host 上报布尔值"。 - **渲染改为 host 注册**:`ArisePorts.interaction_renderers: dict[str, IngressInteractionRenderer]`,host 在 `configure()` 时按 `kind` 注册 `render(payload: dict) -> str`。**core 仍拥"信封"**——固定包一层 `<interaction kind="..." sender_id="..." sender_name="..." time="...">{host 渲染内容}</interaction>`,与 `<message>` 同一套入口序列化外壳。第三方事件(`target_is_self=False`)**不需要这套渲染**,直接复用 `EnvironmentSignal` 现有表示。 - **`poke`/`reaction` 不作为 core 内置预注册项保留**:彻底清空,变成 host 自己注册的普通 registry 成员,与未来任何新增交互原语地位相同,**不享有特殊待遇**。 - **四个具名能力位整体移除**:「**registry 里有没有注册**」本身就是支不支持的完整信号,避免布尔位与注册表状态不同步——与 ADR-0003 对 `@all`/`@user`/`@face` 的处理原则相同(那次也是"注册表状态即能力信号,不设独立能力位",issue #28 已验证过这个模式)。 - **egress 侧改走工具港(ADR-0019),不开对称的 egress registry**:host 把"发一次戳一戳/回应"注册成**普通自定义工具**,core 全程黑盒透传。 ## Acceptance criteria - [ ] `ConversationInput.interaction` 改为开放形状;`InteractionKind` 封闭 `Literal` 删除。 - [ ] `ArisePorts.interaction_renderers` 注册表 + core 侧固定信封渲染;`kind` 未注册时的行为要明确(建议沿用 `tags.py` 既有先例:未注册即整体剔除、不留占位,见 issue #28)。 - [ ] `target_is_self` 路由:`True` → 统一评估管线;`False` → `EnvironmentSignal`。**路由行为不得改变**,只换判定者。 - [ ] 四个能力位从 `CapabilitySet` 删除,`tools.py`/`runtime_loop.py` 相应门控删除。 - [ ] `EgressPort.poke_send`/`reaction_send` 删除,`POKE_SEND`/`REACTION_SEND` core 工具删除——host 改经工具港注册。 - [ ] **注意穷举守卫**:`CapabilitySet` 少四个字段,若有按字段枚举的测试/守卫(`get_args` 或字段清单式断言)会红,那正是它该做的——**顺着红的地方改,不要绕开守卫**。 - [ ] 测试侧:`InteractionEvent` 在 `tests/` 有 3 处引用(`runtime_loop_helpers.py`/`test_conversation.py`/`test_runtime_loop.py`),加上能力位相关用例,一并适配。 - [ ] **不要写成空转测试**:验证「未注册 kind 被剔除」时,断言不能是"输出里没有那段文本"——那在渲染压根没实现时同样成立。要有反向对照(已注册 kind 必须正常渲染出来)。 - [ ] **文档同步(`docs` 分支,按规则不推送)**:ADR-0004「决策」节至今仍列着 `poke` 位,而 ADR-0030 宣布移除了它——这是一处**已存在的 ADR 间矛盾**,本票落地后追加更新节消掉它。ADR-0030 自身追加"已实现"更新节。CONTEXT.md「交互事件」「能力探测」两条术语核对。**按本仓惯例只追加 `## 更新(…)` 节,不就地改写既有「决策」/「拒绝了」。** ## Not in scope - **ADR-0023 不作废**:ADR-0030 明写它取代的是 ADR-0023 的**封闭 kind 设计**,而「命中本人走 reactive、旁观走环境信号」这条**边界判断保留有效**。别顺手把 ADR-0023 整份标废。 - 不新开对称的 egress registry(ADR-0030 明确拒绝,egress 走工具港)。 - host 侧怎么判 `target_is_self`、怎么注册 renderer —— 那是 host 的事,本票只交付 core 契约。 - 其余 31 条实现类落差 → #103。
Yushu closed this issue 2026-08-03 05:10:51 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ProjectKuma/arise#104
No description provided.