Repository navigation
fix(chat): recover anchored historical answers without replay - #7981
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)
动机 / Motivation
无阻塞发现。既有私聊管家用户及其原消息受众。上游已完成完整答案,旧解析器却在引用标记处截断;修复解析器后,旧请求仍因已完成状态被补办扫描跳过。既有原队列可找回确切原输入的完整文本并沿原受众补发,后续输入的答案不会串入。不重跑业务、不重放未知效果输入、不补交结构化操作、不新增消费者或扩大权限。安装态自动采用、真实原消息独立读回及其他未闭业务仍需分别验收;本 PR 不表示积压已清空。
这是结果回传缺口:同一次上游执行已有完整答案,原 Core 记录的正文却是不完整前缀。只修新答案的解析不能修复这些历史记录;让用户再发一次则可能重复业务副作用。该增量应交付已存在答案的自动取回,不把整个历史队列当成可重跑工作。
改动思路 / Approach
扩展既有 typed 结果采用规则与原生只读证据生产者,继续复用现有投递 journal、原来源核验和不确定回执处理。
当前 PR 只恢复由原 Core 输入锚定、纯文本严格扩展且元数据不变的历史答案;执行准入、后续业务和结构化动作保留原责任层。
词法状态由现有解析器从原正文派生,只决定是否安排只读核验;它不是新授权。原生观察器按 Core 在发送前冻结的输入索引与哈希定位原段,读完整轨迹并核对首尾 idle/数量一致,止于下一条 USER_INPUT。原输入、时间窗、执行身份及正常 final 均须一致。已有 failed-result 分支仍要求最新输入,历史放宽只用于已完成、纯文本、严格延长原前缀且元数据不变的结果。
TypeScript 继续拥有语义规则;Python 只采集事实并执行已有 journal/message 副作用。结果采用、消息崩溃恢复、原来源再次核验和 provider 回执沿既有责任层,无第二队列、迁移开关或用户操作步骤。相比手工重放或加消费者,这是直接修责任层的最小完整切片。
具体改动 / Changes
规范依据为 docs/architecture/rfcs/app-conversation-and-async-inbox-v0.zh-CN.md,不可变修订 2afe4b0a0d2a45b5f7a172f01bdc0542adcdec52,本 PR 未修改该规范。original-result-owner 对应稳定原 Session 与原投递意图;uncertain-effect-retention 对应缺 receipt 不盲重发;source-revalidation 对应每次副作用前核验原 App/来源/Core;这三项均 implemented。installed-journey 按规范的独立验收边界保持 out_of_scope,不能拿合成通过替代真实原受众送达。
六个既有文件增加 288 行、删除 11 行,其中生产增加 75 行、删除 10 行,测试增加 213 行及替换一行夹具配置。没有新增产品模块、存储或 CLI。
关键代码讲解
completed_result_observation从完整原生输入索引中只选确切 Core anchor,截取到下一输入之前。未锚定的旧记录仍只读最新输入;不同 execution、未完成步骤、错误终止或不完整覆盖都不生成证明。历史 proof 才加 end_step_index,最新 proof 维持旧形状,避免已冻结 journal 的深比较被字段变化破坏。context_recovery_candidate让已有回复池发现已完成却仍有未闭字面量的正文。_context_recovery_plan重新从原 Turn 的完整事件读取唯一 native_input,派生提示,再交给同一 typed owner;仅提示命中不能冻结或发送内容。原来每请求六十秒只读探测退避仍在原 transport,未增加自己的重试计时器。planContextRecovery限定原请求、原 Session、原输入哈希和段边界,并使用pureResultText/extendsOriginalText核对 schema、严格更长前缀及全部非正文元数据。它复用 adopt_native_result,不返回 client_turn_id;已送达后直接退出,不能转入格式审阅或业务续跑。已冻结但未确认送达的完整结果继续显示 pending。- 原队列集成回归 用实际临时 Core 文件、原队列、binding 与 Lark transport,以及真实 HTTP native fixture,先保留旧解析结果,再完成不同的后续输入,最后只调用普通 reconcile 发现历史答复。它逐字核对完整原答案与 provider 读回、原 Turn/events 不变、两个原生输入仍恰好两个,并把模型 start/queue admission 设为直接失败,防止伪装补办。
对主干的风险 / Risks
最强反例是旧请求误拿后来答案,或一段看似完整的文本偷偷带入未提交动作。六个原生历史用例覆盖缺 anchor、哈希不符、跨 execution、未完成步骤和 stream error;47 项最终 typed 测试覆盖严格前缀、元数据动作、来源、排队、busy、stop 与 steering 拒绝。七个原 worker 历史旅程覆盖成功、改源、关闭、丢回执、journal/message 崩溃、非前缀和未完动作。丢回执仍保留 provider_delivery_receipt_unknown 和 pending,不盲重发;消息落盘崩溃使用既有冻结正文恢复,不重新生成意图。
PASS:最终两套 Python 417 项;最终 typed 47 项及 TypeScript 全量类型检查;精确提交标准 premerge 的 6 个直接、10 个选择检查,无阻塞失败。公共代码六文件与公开文本边界扫描干净。维护性检查的 machine_section_projection 旧告警在实际不可变基线/候选 stdout 完全相同,仍保留为继承 advisory,没有放宽预算。聚合 boundary 命令另有两个外部旧 Goal authority 错误,未将它们误说成本 PR 扫描失败或修复。
同一最终 public 夹具在真实 detached 基线、正确 cwd/import 下失败:旧队列没有恢复结果,完整答案 oracle 失败;候选相同路径通过。宽 TypeScript 初跑为 5902 通过、8 个依赖加载失败、41 个环境跳过;缺失 pg/typescript 的八文件在正常 npm 依赖安装后独立重跑,126 通过、36 个 PostgreSQL 环境跳过、零失败。此处报告累计覆盖,不声称某一全套命令全部通过。早期新测试未允许只读模型配置发现 RPC、以及 nullable 类型检查失败均已保留;当前只读 RPC 边界与最终类型检查覆盖相同条件。
语义与 CI 对齐
本 PR 复用既有结果采用词汇,明确改变历史已完成文本的发现/采用资格;不是 feature-off 隔离声明。来源、原受众、原执行和不确定投递的权威不变。词法布尔值仅选择探测,真正的状态判断继续由 typed Core 执行。没有改通用义务、提示词、配额、执行器权限或公开错误语义。当前管理 Goal 的 wait_for_ci=false,未获取、查询、轮询或等待 CI;CQR 当前禁用。
我的整体评价 / Verdict
long_horizon 改善在于已有答案可以沿原队列收尾,不再需要重做已经执行过的请求;user_experience 改善在于原受众能取回确切完整正文。可观测收益有真实责任层的基线反例与修复后结果支撑,额外生产机制适度,复用了原投递和 frozen intent。没有渲染布局变动,既有恢复正文 renderer 直接消费新采用的原文本;因此视觉布局验收不适用。
扩展既有 typed 结果采用规则与原生只读证据生产者,继续复用现有投递 journal、原来源核验和不确定回执处理。当前 PR 只恢复由原 Core 输入锚定、纯文本严格扩展且元数据不变的历史答案;执行准入、后续业务和结构化动作保留原责任层。安装态自动采用、真实原消息独立读回及其他未闭业务仍需分别验收;本 PR 不表示积压已清空。未锚定、动作元数据改变及其他未知效果的旧记录继续保持未证,不据此推断业务完成。无阻塞发现;最强剩余验证是正常安装后原服务自行补办、原 Lark 原受众完整读回及历史字节保留。
English verdict: APPROVE at a0953b5. The existing queue can return an exact anchored historical text result without replay; source authority, immutable original history and uncertain-delivery handling remain intact. Production adoption and independent original-audience acceptance remain separate.
A previous response parser could truncate a completed answer at a quoted review-envelope marker. Fixing the parser protected new answers, but the original external queue excluded ordinary completed Turns and the native reader selected only the latest input, leaving historical truncated replies unrecoverable.
This change lets the existing original queue probe a completed text-only reply with an unfinished literal, then adopt only a strictly longer answer from its exact Core-frozen native input. The reader bounds historical steps at the next input and requires a stable idle trajectory, original input hash, source, time, execution and normal terminal proof. Non-message metadata must remain identical and action-free. Failed-result recovery retains its latest-input restriction.
The existing result journal and transport remain responsible for frozen message identity, current source validation, delivery/readback and uncertain-effect handling. The original completed Turn and events are preserved. No input is replayed and no model Turn, consumer, permission or business operation is added.
Validation: focused original-worker journeys, historical native HTTP counterexamples, typed-rule tests, Python and TypeScript checks, and repository premerge. Production adoption and independent original-audience readback remain separate acceptance steps. The managed Goal disables CI waiting; no CI is queried.