Repository navigation
fix(chat): recover unused result-review preparation without replay - #8349
Conversation
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
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
动机 / Motivation
#8341 实际上线后暴露了可复现的漏答:原消息、答案和授权未变,宿主事件压缩产生的纳秒文件标记却在 JavaScript 中被舍入,精确证据比较因此误拒。那次复核尚未向模型发输入,就消耗了原有预约;只修未来比较仍不能补回这条消息。仅修复已有旧消息结果复核的宿主缓存舍入误拒,以及一次可证明模型尚未派发的准备失败。真实服务采用、实际模型答复及飞书原消息送达仍是合并后的独立验收,其他积压没有在此被宣称完成。
适用用户:Original private-project Bot messages with a qualified same-question peer review.
前后行为:Two canonical cache triples change integer precision across JSON; base host-gates, head compares all semantic evidence and independently returns the existing artifact.
可见结果:Original worker returns its own bounded read-only answer, retaining the failed preparation and all original delivery records.
范围边界:No writable replay, new invocation budget, counter reset, manual business answer, second consumer or universal backlog closure.
改动思路 / Approach
仅修复已有旧消息结果复核的宿主缓存舍入误拒,以及一次可证明模型尚未派发的准备失败。
原 worker/Session 队列负责发现与投递,native-chat 采集事实,typed Core 决策,既有 Codex provider 证明原生关闭清单;没有第二个业务或发送 owner。先排除两处已知宿主缓存标记,继续逐项固定完整答案、来源、权限、时间和状态;再由 Core 证明此前失败仅发生在模型准备阶段。复用已有只读结果复核和原消息投递,失败 Turn 与预约保留在历史,新增固定的准备回执和投递阶段,不清次数或重授模型调用。直接复制同文消息答复会丢失本条原身份;泛化重试则可能重复业务,两者均不满足这个问题。
| 情况 | 结果 |
|---|---|
| 仅两处宿主缓存标记发生表示变化 | 继续比较完整语义证据 |
| 恰为入队后本地 host_gate,无原生输入、delta、响应,原生清单关闭且来源当前有效 | 一次只读准备恢复 |
| 实际派发、额外事件、未知原生工作、停止、撤权、语义变化、第二次失败 | 保持等待 |
| 回滚到旧运行时 | 旧读取器识别 preparation-1 并拒绝只读策略组合,派发前暂停 |
具体改动 / Changes
读取的规范依据是提交 ecec9f4 下的 docs/architecture/rfcs/app-conversation-and-async-inbox-v0.md:保留原来源身份、单次只读调用、禁止业务重放的不变量。本 PR 的准备恢复是明确披露的新语义,不能用本 PR 修改后的 RFC 倒推它早已存在。验收映射:semantic-evidence-only 由相同大整数反例验证;unused-review-preparation 由真实 worker 的旧失败及两处崩溃验证;rollback-read-only 由不可变旧读取器接到真实队列路径验证。
关键代码讲解
- related_review_basis 只删除两处 derived event_compaction_revision。它不删响应、身份、预算或授权,也为旧持久化预约提供同样的读取表示。
- collect_related_result_review 是从原 admission owner 移出的规范事实采集,不增加第二个资格策略。完整原日志、已投递 peer 和固定来源继续由既有 Core 判定。
- planRelatedResultReview 要求规范轨迹恰为 queued、host_gate,原生输入为空、delta 为零、无响应且等待至少 60 秒。实际已派发、未知效果和第二次准备均不能取得新额度。
- recovery_context 用已有 preparation-1 标识及显式只读准备字段相关联。旧版本识别该标识后在适配器选择前拒绝策略组合,避免新 suffix 被旧版本当作普通可写输入。预约或入队回执丢失仍认回同一准备身份。
7 个文件共 225 行增加、53 行删除;两份 RFC、两个测试文件、三个生产文件。抽出 54 行证据模块后,原恢复模块回到继承的 2000 行限制之内,没有调高预算;producer 是原 worker,consumer 是既有队列及投递器,没有新 provider、CLI、消费者或人工维护的状态镜像。
对主干的风险 / Risks
最强反例有两个:用小整数测试会漏掉线上纳秒舍入;仅测试新运行时会漏掉旧版本对未知恢复标识的处理。两者均做了独立反验。同一 fixture SHA-256 为 8518bca524a4ed7cf2379afb82ded41231140f17d94e05a1da47eb84bf4ae6b8,同一命令 uv run --extra test pytest -q tests/test_lark_legacy_result_reviews.py -k compacted-source 在不可变 base 上因复核 failed 而退出 1,在当前 head 上退出 0;不规范化语义结果、权限或原身份关系。旧读取器真实队列 oracle 因 runtime_unavailable 提前终止,无 upstream、delta、响应,仅保持原先三次 turn/start。新版本完成自己的只读复核后总数为四次,产物保持 catalog-item:verified,旧失败、旧投递和原请求字节均保留。
PASS:当前 head 的 10 项原 worker 回归、62 项 Core 用例及 control-plane 类型检查;新证据模块单文件严格类型检查通过。标准 premerge 的 6 项直接门、14 项选择检查通过,无 hold;包含双语、架构和公开边界。既有工作续办回归另有记录;不将全仓 mypy 称作通过。按 wait_for_ci=false 未查询、等待或投票远端 CI。
测试使用真实文件状态、原 worker、队列和 stdio 协议进程,模型与飞书 peer 是合成实现。因此合并并不证明线上已经补答。旧版本回滚有意保留暂停,需要采用合格版本才能接续;不会放开已派发、效果未知、停止或失败次数保护。
我的整体评价 / Verdict
APPROVE 精确 head ac34333,未发现尚未修复的阻断问题。原 worker/Session 队列负责发现与投递,native-chat 采集事实,typed Core 决策,既有 Codex provider 证明原生关闭清单;没有第二个业务或发送 owner。用户无需重发问题,原 Bot 可沿原消息补答;消息渲染和 UI 未改。没有页面或渲染变化,视觉复核不适用。真实服务采用、实际模型答复及飞书原消息送达仍是合并后的独立验收,其他积压没有在此被宣称完成。相较不修、删全量证据比较或泛化重试,这个范围与维护成本有证据支持;采用与原消息读回仍须各自验收。
English verdict: APPROVE ac34333; semantic cache normalization and one proven pre-dispatch preparation recovery preserve original scope, failures, read-only permissions and invocation budget. Installed real-message acceptance remains separate.
修复 #8341 上线后发现的真实准备失败:宿主事件压缩缓存包含超过 JavaScript 安全整数范围的纳秒文件标记。跨 Python/TypeScript 传输后数值舍入,答案、原消息和授权完全未变,却被误判为关联证据变化,模型 Turn 尚未派发就终止。
将原有同文证据采集移入 native-chat 内部的
result_review_evidenceowner,仅在两处已知 Turn 位置排除宿主缓存标记;答案、身份、权限、时序、状态和预算仍逐项固定。Core 保留全部资格判断,不引入第二个 policy、消费者或 provider。抽取使原 admission owner 回到继承的 2000 行上限以内,未提高预算。对已经发生的本地准备失败,原 worker 可进行一次有界准备恢复:必须是规范的入队后
host_gate,无原生输入、delta、答复,并有新鲜的关闭原生清单排除隐藏派发。重新核验原来源、授权、停止、队列和证据;不替换可写 owner。失败 Turn 和预约留在历史,使用独立、固定的准备回执与投递阶段。已派发、未知原生工作、语义变化及第二次准备失败均保持等待;不会清次数、重授模型调用或重放业务。验证通过原 Lark worker、文件状态和真实 stdio 协议进程:相同大整数 fixture 在 #8341 base 复现误拒,head 补回独立答案且产物不变;恢复已发生的准备失败时,总
turn/start数仍是原有四次,失败记录保留。包含两处崩溃、停止、撤权、来源变更、实际已派发/未知输入拒绝。双语 RFC 同步,67 项回归、62 项 Core 用例及类型检查已通过;最终提交标准 premerge 通过(6 项直接门、14 项选择检查),无手动 hold。审阅中发现并修复旧版本回滚兼容:只读准备沿用已识别的 preparation-1 标识及显式类型,旧运行时在模型派发前拒绝策略组合。不可变旧读取器经真实未改队列路径的反验通过,无新增 native input;当前最终 head 的 10 项 worker、62 项 Core、35 项既有恢复及类型检查通过,最终标准 premerge 6/14 通过。