Repository navigation
fix(lark): retain owner messages through identity outages - #6888
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent · gpt-6.1-sol · OpenAI · runtime_reported · reasoning_effort=max
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
当前 head 3108bafa0506830cf4a30347c4459ef0f7c2b0cd,相对基线 a72dcb21a2906fccf099b11959725b595aa826ed 的完整四文件改动,未发现阻塞性问题。以下是源码和实际执行的判断;正式安装、真实服务持续可用及整个消息积压清空仍须分别核验。
动机
受影响的是向已绑定的个人助手发送私聊的用户,以及随后继续工作的管家。身份服务短暂不可用时,旧实现可能在保存消息前退出;另一个拒绝通知路径又可能生成没有原请求身份的执行记录,阻塞后续消息。现在先保留可恢复的候选消息,并把通知送达与真实执行记录分开。用户能够在原授权恢复后继续原请求,已经终结的拒绝通知也不会因重复投递启动工作。本片不承诺上游模型、网络和账号始终可用,也不代替每项实际业务的完成验收。真实 provider 网络故障、上游长上下文路由以及正式服务采用仍需运行维护分别确认。
改动思路
保留完整接收语义需要同时修复保存次序和回执归属。只在身份探测失败后打印日志仍会丢消息;只让管家忽略缺字段,则继续制造无主执行记录。当前方案把候选保存放到已有每请求文件锁内,后续继续走原身份、来源和 Core 授权;拒绝通知只完成传输自己的回执,Core 只接受已经存在的原请求身份。没有增加另一套队列、消费者、授权判定或人工确认流程。
下面是该 head 的控制流比较,属于源码说明,执行证据在后文:
| 触发情况 | 原路径 | 当前路径 |
|---|---|---|
| 已绑定本人的私聊,身份 IO 暂不可用 | 尚未保存就退出 | 私有候选留存,尚无执行、反馈或权限 |
| 相同候选在恢复后被扫描 | 没有原候选可找 | 重验 App、本人、来源和 grant,使用原请求进入原队列 |
| Core 受理前拒绝,拒绝通知已送达 | 写出只有 delivery 标志的 Core 文件 | 仅关闭传输通知,不创建执行;重投也不新受理 |
| 读取原生委托或路由选择 | 无关残缺条目可抛错 | 先检查当前资源/绑定范围,再读相关内容 |
机制归属合适:网络和文件观察仍属于 Lark/Python 接入层;身份、Session 和执行许可仍由既有 typed Core 决定。当前 PR 的边界是已配置私聊的接收与回执恢复,未把外部身份服务的可用性升格为默认执行权限。保留现有存储和 scanner 比新增拒绝状态协议或补录历史消费者更小,也更容易回滚。
具体改动
采用不可变基线的 App conversations RFC 作为现有约束,版本 a72dcb21a2906fccf099b11959725b595aa826ed。本次只判断已交付私聊 checkpoint 的相关条款,不把该 RFC 尚未完成的更广泛体验当作本补丁新增义务。
- App-scoped owner/source identity:implemented。候选只为本地启用绑定的本人私聊保存;新增和既有负例验证撤销、更换本人、来源发送者变化、群消息和其他发送者都不能据此获得执行。
- Durable feedback and final delivery:implemented。传输拒绝通知不构成 Core 受理;原生回执必须匹配现有 request_ref,重复拒绝通知不会创建 Session、Turn 或第二条回复。
- Canonical scanner recovery:implemented。重建接入对象后由同一 scanner 读取原候选,完成一次原生执行并读回最终通知;再次投递不增加上游 turn/start 或 provider 写入。
关键代码讲解
LarkPrivateConversations.admit先从本地绑定生成来源,再在原请求锁内保存满足本人私聊范围的候选。随后仍调用当前bindings.resolve和原来源读回;原有拒绝类型保留。群输入不走这条提前保存分支。已经送达且从未拥有执行关联的拒绝通知直接保留其终态。reconcile_request沿原捕获恢复、同 Session 队列和原消息投递。最终送达后,只在真实 Core 请求存在时登记 Core 回执;没有 Core 的路径只有明确的传输拒绝才能闭合,其余缺失关联仍保持失败可恢复。ChatExternalConversations.record_delivery在既有锁内核对 request_ref,避免读取不存在文件得到空对象后写出假的执行记录。已有 commission adoption 和 Session/Turn 关联检查继续生效。_admit_prepared的两处选择历史查询先匹配绑定,未声明任何绑定的旧回执不能参与路由或序号计算。commission_evidence先限定授权资源,再读取其绑定和来源。没有委托资源的旧通知不再阻塞整段管家上下文,也不会被当成委托完成。
两个测试文件覆盖完整因果序列。test_lark_private_conversations.py 验证身份中断、恢复、权限反例、拒绝通知及重复投递;test_native_steward_private.py 在保留旧 delivery-only 原字节的情况下,让后续管家模型协议路径及项目选择真正继续执行。测试替代 provider/model,但不替代实际磁盘 journal、原生 Session 队列或 typed bridge。
对主干的风险
主要反向风险是把“保存候选”误当成授权,以及传输拒绝在重投后重新执行。撤销绑定、本人变化和来源发送者变化三个恢复反例均保持零 Core/Session/外部写入;群和其他发送者也没有获得新的预捕获。重复投递反例恢复真实 Core admit 后再次提交原事件,仍没有执行记录、Session 或额外回复。这比只检查返回码能更直接证明拒绝终态。
旧回执兼容保留的是持久数据,未新增旧请求解析器。两个正常查询只会排除不属于当前绑定/授权资源的条目,不会制造它们的身份或改写旧文件。损坏但自称属于当前授权委托的数据仍会暴露错误,未用 blanket catch 把任意损坏宣称为无事发生。保留未验证内容是诊断边界,不是可执行来源。
在不可变 2d94432ee 上,同一新增用例实际复现了两次候选丢失、拒绝通知生成空 Core 文件,以及残缺记录阻塞后续受理。当前 head 的六个相关测试族共 307 passed;安装到可丢弃环境的 wheel 使用实际打包模块,九项故障恢复用例通过,源文件摘要与当前提交一致。另用同一个独立 fixture 比较基线和 wheel:绑定关闭且 provider 可用/不可用两种状态均拒绝,没有候选、Core、Session、模型或 provider 写入。启用绑定的恢复路径由上述九项用例覆盖;安装本身不激活绑定。
Ruff、项目配置的 mypy、当前 Chat 构建、wheel 安装、公开边界扫描以及 standard diff-selected premerge 均通过。后者包含四项直接检查、Python 编译和十项选中 canary/smoke。首次 wheel 构建因合入主干后前端 bundle 过旧而正确拒绝,重建后再安装验收,没有绕过来源检查。早期新增测试还暴露了旧序号查询的缺字段路径,已把修复覆盖到实际受理及选择;没有放宽该断言。CI 根据当前 Goal 的 wait_for_ci=false 未查询或等待,不把远端未观察状态写成成功。
语义与 CI 对齐
这是 reuse_existing:继续使用原 capture/request/delivery schema、原 Core 状态和每请求锁。局部结构判断代表已送达且从未有执行关联的传输通知,不使用正文、名称或关键词推断权限。实际行为变化是身份 IO 前可恢复保存本人候选,以及禁止通知凭空生成执行;PR 描述已经披露。没有修改机器调度、quota、提示词或任一 Goal 的验收开关,也没有把必要来源校验改成建议。
我的整体评价
APPROVE,justified_increment。 long_horizon 为 improved:连续消息、身份恢复和旧回执共存时,原队列能够继续,重复消息没有新增执行。user_experience 为 improved:合法的短暂故障恢复不增加用户重新发送或确认的轮次。该结论限于实际覆盖的接入和打包路径,不声称模型采用、业务效果或长时间在线 SLA 已验证。
生产增量集中在两个现有 owner,测试增量覆盖两种相互放大的故障;继续保留现有 journal 比另造可靠收件服务更合比例。同域简化已通过范围先行的查询和拒绝回执归位完成,无须增加兼容框架。真实 provider 网络故障、上游长上下文路由以及正式服务采用仍需运行维护分别确认;这些不应通过删除原失败、重放未知效果工作或扩大授权来掩盖。
English verdict: APPROVE — 3108bafa0506830cf4a30347c4459ef0f7c2b0cd. Identity outages retain locally bound owner candidates without granting execution; verified transport refusals cannot create Core requests or execute on redelivery. Existing source, owner and grant checks remain required. The historical loss and journal-poisoning cases fail on the old baseline; 307 current regressions, nine installed-wheel fault/recovery cases, paired disabled-binding cases, static checks and standard local premerge pass. Provider/model fixtures are synthetic; live service adoption and upstream availability remain separate. CI was not consulted.
|
当前 head 最新已读取主干 |
An identity outage could drop a locally bound owner's private message before a durable capture existed. A separate refusal path then recorded a verified transport notice as a new, empty Core execution journal, which could block subsequent admission and Steward context reads. This fixes both parts of the same receive/recovery journey.
Goal and delivered outcome
Author declaration and implementation basis
Written by: model_agent — OpenAI Codex, GPT-6 family.
No separate written specification; the reproduced defects and request in this PR are the implementation basis.
LarkPrivateConversations.admitand existing capture recoverytest_owner_event_survives_identity_outage_and_recovers_oncerecord_deliveryidentity guardtest_rejected_source_returns_notice_without_creating_core_executiontest_legacy_delivery_only_receipt_does_not_block_steward_conversationScope and continuation
Complete within this receive/recovery slice. Existing provider authentication, upstream model availability and final business acceptance remain separate operational conditions. This proposal does not claim those external services are always available or that every queued business request has completed. It changes the existing Lark transport and native journal IO; no configuration or frontend control is added.
Validation
Tested revision:
3108bafa0(production modules in the installed test wheel match this checkout byte for byte).Run state: finished. Inputs: synthetic, public_fixture.
2d94432ee; current code preserves capture and recovers exactly onceThe tests exercise identity outages, restart, duplicate delivery, source/owner changes, revocation, foreign/group inputs, refusal receipts and later Steward work. They use the actual native queue, typed bridge and disk store with a synthetic upstream host. They do not inject faults into live provider accounts or launch a paid validation conversation.
Frontend / visual evidence
UI impact: none. Existing message entrypoints and settings are retained; no visual composition, new capability or control is introduced. Before/after viewport evidence is not applicable to this transport/journal repair.
Boundary checklist