Repository navigation
feat(lark): keep App input feedback independent of answer delivery - #7858
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; model=gpt-6.1-sol; provider=OpenAI; runtime_reported; reasoning_effort=max
Request changes conclusion (author-owned PR; GitHub blocks formal self-review)
[P2] 可选表情 IO 在正文 outbox 锁内阻塞已完成答案。 这是独立本地反例确认的新增问题,不是 CI 未知导致拒绝批准。
动机 / Motivation
在 App 输入后、仍从原 Lark 私聊接收回传的用户,以及同一 Session 的现有投递 worker。例如 App 的用户输入镜像已送达,Core 也已完成答案,但新 Get 表情的 provider 请求尚未返回。当前 head 在正文 outbox 的锁内等表情 IO,后续答案因此不能继续;用户只看到输入镜像,却要等非必要反馈恢复后才能收到已经完成的结果。目标应是只给已核验的用户镜像显示 Get/OnIt,并让 queued、running、terminal 反馈准确;反馈失败或 pending 时,原正文仍能独立继续,且不重复写入、启动执行或结算原请求。本切片不完成安装版或全跨端同步验收,不新增 executor、capability 或权限;实际 Lark 呈现、完整 provider 延迟和长期净益仍需独立证据。
改动思路 / Approach
复用现有 Lark reaction journal、每消息锁和 canonical source/Turn 核验是合适的;但可选反馈 provider IO 必须退出正文投递持锁的关键路径,不能让 observer 的等待挡住已完成的必要结果。当前范围为六个文件 +549/-21:两个现有 Lark provider owner、343行测试扩展和28行成对 RFC 说明;不需要新增通用队列或 executor,所需修复应局限于现有正文/反馈执行边界。
现有 implementation 对来源、Turn、冻结正文和 provider post 的核验较完整;Get 保留、OnIt 仅在 starting/running、terminal 清理和 unknown-write journal 也有实际测试。问题不在是否复用 journal,而在新反馈被同步嵌入必要正文的持锁路径,普通立即失败 fixture 捕捉不到 provider 尚未返回的情况。将可选表情 IO 从正文 outbox 持锁路径分离,保留每次 effect 前的来源/Turn 检查、原 journals 和未知写入不重试;用 held-reaction 反例证明原答案可在表情恢复前送达。
具体改动 / Changes
精确 head 5b089800b421d1ac9a0aac5d5ba807ef026c7f8c,不可变 base 111519af76e56141c56eb7a27443da59def775b9;审查覆盖完整 diff,而非只核对上次发现或作者测试统计。
关键代码讲解
_web_projection_paths增加 Turn-directory 变化和失败反馈发现;mtime 是 discovery hint,不能代替发送授权。关闭反馈仍沿原正文发现路径。_web_projection_feedback只处理已 verified 的 user mirror,逐次重查 canonical input、原 Session/Turn、绑定/授权和冻结 provider 消息;长输入选择最后一条实际 post,不对 parts locator 操作。_reconcile_outbound_input_reactions复用创建/清理 journal,outbound 明确captured_pending=False,没有伪造 incoming event 或结算原请求。已收到成功而处理中失败的已知写入计数保留,旧 API 参数默认值也保持。_reconcile_web_projection从进入到返回都持有 outbox fence;1515行 新增的直接反馈调用在选取下一个 canonical body 之前同步等待,形成阻塞点。
已在 diff 前读基线原会话规格,并核对新增双语条款:original Session and source isolation → implemented;once-only feedback recovery → implemented for inspected source cases;nonblocking optional feedback → not_met;installed presentation → deferred,实际 Lark 呈现与安装验收仍未证明。既有 Core status、receipt、原 source/audience 词汇沿用;private feedback 意图由新镜像 write 前自动产生,状态投影不能成为新 grant。
对主干的风险 / Risks
[P2] 将可选反馈移出正文持锁路径。 触发条件是 Core 已完成原答案、正文 API 可用,但表情 create 请求 pending。独立反例只用 Event 挂起这一个 provider 操作,保留原10秒 drain约束:
| 相同原会话/已完成 Core | 表情释放前的答案 | 结果 |
|---|---|---|
| 不可变基线,反馈开 | 已继续 | PASS |
| 当前 head,反馈关 | 已继续 | PASS |
| 当前 head,反馈开 | 仅一条用户镜像,答案未继续 | FAIL;释放表情后才到达 |
这是实际 Core/HTTP/store/provider adapter 路径,不是复制新 helper 的期望。_reply_runner 的单次 provider bound 为30秒;即使最终会 timeout,也已经把非必要反馈等待接到了后续必要结果上。将可选表情 IO 从正文 outbox 持锁路径分离,保留每次 effect 前的来源/Turn 检查、原 journals 和未知写入不重试;用 held-reaction 反例证明原答案可在表情恢复前送达。
四个完整 Python 模块:base434 PASS/0 FAIL;head452 PASS/1 FAIL,19个新增用例全部通过。唯一共同用例变化是长回复在原10秒 drain约束超时;单例后来 base/head 都通过,故首次超时的完整原因仍未知,不能因重跑绿就抹去,也不声称上述 controlled probe 已解释它的全部成因。该 probe 独立、稳定地暴露新的同步阻塞路径。
撤权、provider身份/正文/Session/Turn改变、context recovery、unknown-write 不重试、长输入最后 post、历史已结束镜像不回填,以及反馈关闭/立即失败的正文路径均有 PASS 证据。focused Ruff、配置17模块 mypy、双语/public scan和6 direct+11 selected premerge命令 PASS;整体仍 FAILexit1 quality_stale_receipt。初次测试路径拼错、no tests collected 的失败保留,修正实际路径后才执行上述完整模块。CI 未查询或等待。
我的整体评价 / Verdict
REQUEST_CHANGES:有用的反馈路径、权限核验和 journal 复用已实现,但必要结果仍依赖可选 provider 返回,当前有反例,不能宣布这个用户任务完成。long_horizon=regression、user_experience=regression 的依据是原答案被拖住,而非 CI 未知;关闭路径和原来源保护不能抵消开启后的阻塞。修复应保留有用实现和原证据,在现有 owner 内消除耦合,再运行同一 held-reaction、关闭/撤权/未知写入与正文重放反例,不需要新 executor、通用队列或更宽权限。
真实 Core/HTTP 与 synthetic provider 已核验状态和目标消息;本轮没有实际 Lark 原生呈现截图或安装读回,不能把 API 回执与字段正确等同可见反馈已验收。安装版、实际 Lark 呈现与 provider 延迟未验证;完整 Python 仓库矩阵、真实 PostgreSQL 和长期采用/成本净益未获本轮资格。最初长回复超时的完整成因仍未知,后续单例通过没有抹去失败。
English verdict: REQUEST_CHANGES 5b08980; optional reaction IO waits under the required body outbox fence and blocks already-completed answers. Base/on and head/off advance; head/on advances only after reaction release. This is an independently reproduced regression, not a CI-success requirement.
…urrent-20261101-2307 Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
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 | OpenAI | self_reported (host execution observation unavailable)
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
动机 / Motivation
在 App 输入后,从原 Lark 私聊接收答案的用户和同一 Session 的投递 worker。 App 输入已镜像到原 Lark 私聊且 Core 已完成答案时,修复前挂起的 Get 表情请求阻塞正文;修复后当前和后续答案在表情释放前继续送达。 仅已核验用户镜像得到 Get/OnIt;必要正文与可选表情分别推进,保留原来源和未知写入保护。 本 PR 不创建新消费者、业务执行器或通用队列,不补写历史已结束镜像的表情。 真实安装后的 Lark 呈现、实际 App 表情往返和长期延迟仍需沿原服务验收;合成 provider 证据不代表这些已完成。
改动思路 / Approach
正文回执、canonical Turn 和 reaction journal 各有既有 owner;可选反馈可以观察和投影状态,不能持有必要正文的投递锁。 原功能的 Get/OnIt 状态与目标核验保留;本轮删除正文扫描对反馈重试和 Turn 目录 mtime 的耦合,改由原四槽回复池最多一个独立反馈任务观察 canonical Turn。反馈只短暂读取 outbox,在另一把反馈锁内等待 provider,最后重新取得正文锁并只合并仍匹配冻结正文的反馈字段。正文 worker 因而可以发送当前和后来加入的答案。在原 Lark provider 和四槽回复池内分离反馈 IO,沿原授权和 journals 执行,不新增执行或授权 authority。 表情状态是观察投影,原生 Turn 和来源授权仍是 authority。
具体改动 / Changes
本轮以原评审 5731202458 的不可变验收框架(spec_revision 5731202458)为修复规范,结合已读基线 111519af76e56141c56eb7a27443da59def775b9 的原会话 RFC;没有用本 PR 新写的条款反向定义要求。C-SOURCE(原 Session/来源隔离)、C-ONCE(一次性反馈恢复)、C-IO(可选反馈不阻塞)均 implemented;C-INSTALLED(真实安装呈现)deferred,由同一受托部署验收继续,未声称已取得真实截图。完整 PR 为七个现有文件 +641/-22,其中397行测试、33行成对 RFC、211行生产新增;对比旧评审的改动与整个 PR 分别核对。
关键代码讲解
_reconcile_outbound_input_reactions 复用原每消息锁、创建和清理 journal,outbound 明确 captured_pending=False,原 incoming 默认值保持 True;既不制造入站消息,也不结算原请求。未知写入仍停留在原 prepared 记录,不重发。
_web_projection_feedback 只处理已核验 user mirror;每次 effect 前核验原来源、授权、canonical 输入/Turn 和冻结 provider 内容。queued 只收到,starting/running 才处理中,三类终态清理;长输入使用最后一条实际分段 post。
_reconcile_web_projection 的正文锁内不再执行表情 IO。reconcile_app_feedback 在锁外执行可选反馈,仅在来源与冻结 phase 仍一致时合并 feedback,保留并发新增正文和 outbox 状态,无变化反馈不重复写回。_reconcile_private 复用同一有界池,最多一个反馈 Future,没有新 executor、队列或消费者。
对主干的风险 / Risks
原 P2 的最强反例保留:只挂起一个 Get create,Core、HTTP、原协议进程、文件存储及真实服务回复池继续运行。基线在等待期限内只有输入镜像,FAIL;最终提交当前和后续六条 canonical 正文在表情释放前送达、合并后六条 verified 回执均保留,PASS。该验证也排除了“只挪锁但仍卡住同一路径 Future”的较小伪修复。来源/App/Session/Turn/正文变化、撤权、context recovery、assistant、历史不回填、长输入和未知写入仍有负例覆盖。
完整相关 owner suite439项 PASS;最终无变化回写优化后的36项 App 旅程与5项并发/失败检查 PASS。Ruff、配置 mypy、双语 RFC、完整 premerge 和公开边界检查按最终提交核验。初始测试文件路径错误保留,修正路径后才取得完整结果。首次整组还出现一次8秒启动前置等待超时,此时挂起表情的断言尚未开始;没有为首次扫描设8秒产品承诺,故单独把该前置等待改为20秒,挂起后的正文仍按既有10秒 drain 约束验收。相同新版 fixture 在原实现仍失败、最终提交通过;首次调度延迟的具体性能成因未归因。旧评审的长回复十秒超时成因仍未知,后续单例和当前 suite 通过不抹去历史,也不冒称 held-reaction 解释了所有原因;旧 quality_stale_receipt 失败继续保留,当前 Goal 的资格策略已关闭,当前 premerge 是独立结果。按 wait_for_ci=false 未查询、轮询或等待 CI。
语义与 CI 对齐
复用已有 canonical status、reaction journal、source grant 和 body outbox 词汇;Lark 细节保留在 provider 层。反馈意图由当前原镜像写入路径自动产生,其结果只是投影,不是新的授权或业务义务。回滚保留原 journals,可用既有关闭表情选项恢复原正文路径。
我的整体评价 / Verdict
APPROVE,justified_increment;long_horizon 和 user_experience 均 improved:独立 held-provider 反例证明必要结果持续推进,而不是仅记录一个阻塞状态。旧 P2 已在现有 owner 内修复,新增一个服务边界比新建异步 executor 或通用队列更合适;原来重复的发现状态也已删除。未改变前端或 Lark 渲染器与布局,新来源复用既有表情;真实 provider 呈现仍是部署验收缺口。真实安装后的 Lark 呈现、实际 App 表情往返和长期延迟仍需沿原服务验收;合成 provider 证据不代表这些已完成。 源码批准不代表所有积压业务或安装验收完成。
English verdict: APPROVE — optional App feedback is isolated from current and later canonical answer delivery, with original source and unknown-write fences retained.
App 发出的原会话消息已经镜像到 Lark 时,现在复用现有表情回执:Get 表示已收到,只有 Core 已开始处理才显示 OnIt,终态清理处理中状态。每次表情操作重查原授权、canonical 输入和已核验的实际镜像;长输入使用最后一条实际分段消息,关闭表情反馈时正文路径保持。
修复原评审指出的阻塞:表情 IO 独立于正文 outbox 锁,最多使用原回复池的一个槽位,回写只合并反馈字段,允许同一会话当前及后续答案继续送达。复用原 reaction journal,未知 provider 写入不重发,不创建新入站消费者或业务执行。
验证:439 项投递、表情和监听完整测试通过;最终 36 项 App 旅程和 5 项并发/关闭/失败检查通过。相同真实 HTTP/Core/原服务回复池 fixture 在原实现中复现“表情卡住阻塞答案”,修复后当前与后续答案均先于表情释放送达,6 条正文回执在并发合并后保留。完整 premerge、语法、双语 RFC、边界检查通过。合成 provider 资格不代表已完成真实 Lark App 表情与安装后可见性验收,部署后沿原服务另行核验。