Repository navigation
fix(lark): wake existing reply workers when durable work arrives - #6703
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)
Exact head: dc3feb5ce85a0b4bd8879b8b7be465bbd6cdf5b6. 未发现阻塞性问题;以下结论只覆盖该源码调度增量。
动机
飞书用户发出已获授权的请求后,需要在原会话收到受理反馈和最终答复。
新消息在回复扫描器休眠时到达,或模型在上次扫描后结束:旧路径等待下一次定时扫描,新路径唤醒原工作池重新读持久记录。
挡住定时轮询后,原 listener 受理的请求和完成后的答复仍能由原工作池送达;旧代码及丢信号改法在相同断言中失败。
本 PR 不承诺端到端五秒停止、provider 网络提速或安装态与 Web 首次可见结果已经验收。
工作槽被占用时仍可能延至后续扫描;安装态推广、完整停止回执和 Web 首次可见延迟仍需分别验收。
不改动会保留每次空闲扫描的等待,单纯缩短定时器则增加无变化扫描。本次把真实生产者接回原消费者,既没有让用户重复发消息,也没有新增配置、确认、复制或恢复步骤。原绑定权限和不确定写入的恢复门槛仍然适用。这是有用、可独立回滚的增量,不能结清上层全部长期会话验收。
改动思路
信号只负责本地调度,投递判断仍由既有 journal/Core 与 transport 完成;复用原工作池比缩短轮询、直接在模型线程发消息或新增消费者更合适。
本次边界是可回滚的回复扫描唤醒;同一发布仍须单独核验安装态与端到端停止/Web 结果。
以提交前的已接受规范为独立依据:docs/architecture/rfcs/app-conversation-and-async-inbox-v0.md,spec_revision fdf03ec6112aedf497b7ab1d6e5bc4db19c2ae5f,普通工作区会话检查点。提取的验收项 durable-admission、original-return-recovery、app-isolation 已由现有 Core/transport 和本次入口回归验证;installed-acceptance 仍明确延期给现有 native-chat/Lark 运行时负责人。新文档段落说明默认调度变化及限制,没有把旧规范改写成“所有延迟已通过”。
| 触发 | 旧路径 | 当前路径 | 继续保留的依据 |
|---|---|---|---|
| 原 listener 受理新请求 | 等下一秒扫描 | 受理返回后唤醒原池 | canonical journal、原 App/source 核验 |
| 本地执行结束 | 等下一秒扫描 | worker 释放后唤醒 | fresh Core 结果、原投递意图 |
| 关闭或无 transport | 原生命周期控制 | 关闭唤醒并注销;无 transport 不注册 | 不启动第二消费者 |
这是源码流程说明,不是实测延迟图。Event 可以合并或丢失,不能表达完成、授权或投递成功;保留一秒恢复轮询。原四槽上限、attempted 公平性和三十秒通知节奏继续共用一个扫描器;没有对每个完成 Future 立即回调,避免尚未完成的请求形成忙循环。
具体改动
_admit_private(loopx/extensions/lark/goal_topic_runtime_service.py:133)由现有 _poll_profile 的 App 消费者锁范围调用,继续执行同一个带 defer_background_stop 的受理入口,成功返回后才设置提示;服务已经关闭时在受理前拒绝。原始消息、身份、workspace grant、Core Turn 和 provider intent 均不由该 wrapper 新建规则。重复输入即使触发一次提示,也不能凭空产生待投递记录。
_reconcile_private(loopx/extensions/lark/goal_topic_runtime_service.py:79)在扫描前清除 Event,避免“取空快照期间新请求到达,随后清信号”的真实丢唤醒窗口。原 pending Future 集合、工作槽预留、请求重试排序和 owner attention 分支均保留;自定义 controller 没有 hook 时继续原有轮询。close 设置关闭和提示,再沿原 listener/线程回收路径退出。
TurnReleaseWakeup(loopx/chat_turn_release.py:7)只拥有进程内 Event 的注册寿命,拒绝同一个 Event 的重复嵌套注册,finally 注销,notify 在复制集合后设置 Event。ChatRuntimeController 的启动失败、准备失败、正常/异常 worker finally 三条释放边界发出提示,既不在模型线程执行网络 IO,也不把信号当成 terminal state。大控制器最终只增加五行,2204→2209,未超过原 2210 上限;生命周期 owner 30 行。现有 replay cache 不负责这一职责,没有把内容缓存和调度混在一起。
227 行测试主要用于真实 listener lease、File/Core、原消息投递和确定性 native app-server 子进程之间的时序控制。Feishu 与模型响应是 fixture 边界,canonical 状态和 provider 写入断言不是由一个“成功”stub 直接提供。两个受理时机及 native 完成用独立屏障挡住计时恢复,初始/后续校验分别读取持久结果和实际写入。没有新 CLI、schema、数据库格式、消费者、模型 runner 或 UI。中英文 RFC 同时更新。
对主干的风险
最值得防的回归是扫描期间清掉刚到达的信号,或模型实际结束却未接到原回复扫描器。本次故意把 clear 移至扫描后、删除三条 release notify,在同一原入口测试中各产生一个送达断言失败;基线 fdf03ec6112aedf497b7ab1d6e5bc4db19c2ae5f 的三个原送达断言全部失败,固定 head 的六个新增旅程通过。fixture source fingerprint 为 d449c8b5d8842eb5c8a57462303bb19304f28b9466b6415cee8fd755a1c18081,两边复用同一测试、ordinary fixture 与 transport fixture;没有缩短输入、删掉投递断言或放宽恢复屏障来取绿。
精确实现的 180 项 runtime/notification/queue/dispatch 回归、201 项完整 private transport 回归、Ruff、配置的 mypy(17 个源码文件)、Python 编译和中英文 RFC diff 均通过。标准 premerge 执行 19 项检查,gate passed;语义漂移、模块上限和公开边界均通过。早期 controller 体量检查确实失败,已通过归属调整修复,原上限未提升;早期 /status 文案断言误用了另一提示分支,只修正文案 oracle,保留了独立时序及原回复断言。这些旧失败保留在验证记录中,没有算作当前通过。
恢复、重复、撤权、原消息变化、ambiguous write、exact stop 和 App/group 隔离仍走现有 transport 规则。重启用原 journal 发现积压,不重放模型;关闭先停新工作,注销提示后不会生成投递权限。工作槽正被慢 provider IO 占用时仍可能等恢复扫描,这个限制已公开。真实 provider 网络、安装态和完整停止/Web 首次可见旅程未被 fixture 证明;本 PR 不据此声称全链路已达到五秒。
相关 open #6699 的 chat runtime 改动属于会话展示/配置,已检查共享 owner 的 diff;没有另一个回复唤醒或新队列可以复用。沿用的 fresh-source 校验不改变。remote CI 按本 Goal 原生 wait_for_ci=false 策略未查询、未等待,独立的合并授权和 readiness 仍须执行。
我的整体评价
APPROVE:原工作池接回了真实受理及 worker 释放触发,自动推进原消息投递,用户不用补一个手动动作。源码路径、canonical 状态、原回复和故意破坏信号的反例组成了相互独立的证据,足以支持这一有界调度修复。当前六文件、54 行净生产代码的责任范围合适:Core/transport 继续判定,Python 只处理局部生命周期,未增加第二 authority。
信号只负责本地调度,投递判断仍由既有 journal/Core 与 transport 完成;复用原工作池比缩短轮询、直接在模型线程发消息或新增消费者更合适。 本次边界是可回滚的回复扫描唤醒;同一发布仍须单独核验安装态与端到端停止/Web 结果。 原 installed-acceptance、provider 和端到端停止/Web 缺口继续由既有验收承接;合并与安装分别取得当时的准确版本和活动检查,不能把本评审当成部署或更广权限。
English verdict: APPROVE - dc3feb5; the existing bounded reply scanner is now woken by durable admission and local worker release, with independent baseline/mutation failures and passing exact-head regression/premerge. No installed-service or end-to-end stop/Web latency certification is implied.
A newly admitted Lark request or completed local Turn could sit behind the reply scanner’s one-second idle poll. Wake the existing bounded reply pool after admission and worker release, while preserving canonical journal/source validation, uncertain-write recovery, fairness and owner-notification cadence. Shutdown wakes the scanner and unregisters the content-free runtime hint; older controllers retain polling.
The new File/Core and native-protocol journeys hold the fallback poll behind a barrier and prove delivery for arrivals during discovery, arrivals during wait, and terminal results after an earlier pending read. The same delivery assertions fail on the baseline and on lost-arrival / missing-release mutants. Preparation failure, duplicate registration, shutdown and disabled transport are covered. No new consumer, queue, model invocation or provider authority is introduced.
Validation: 180 focused runtime/notification/queue/dispatch tests and 201 private-transport regressions pass at the exact head. Ruff, configured mypy (17 source files), bilingual RFC check and the unchanged module-growth ratchet pass. Standard premerge executes 19 checks and passes. The initial controller-growth failure was repaired by placing the IO-free lifetime owner in a 30-line module; no ceiling or assertion was relaxed. Installed-service promotion and end-to-end stop/Web latency remain separate acceptance; occupied workers can still defer a request to the recovery poll.