Repository navigation
fix(chat): recover untouched native watches after host restart - #7425
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 | OpenAI | self_reported (host session lookup unavailable)
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
动机
已启用原生监控的本机托管会话和管家,会在宿主重启后遇到检查长期不再更新。例如,服务在接收一次检查后、写入原生观察前退出:旧入口一直读回失败请求;新入口核实尚无效果后,由原空闲服务接续一次检查。可观察结果是原失败记录保留,原生检查只提交一次,下一检查时间向前推进,期间不调用模型、不重发业务消息、不消耗执行配额。本次只修复监控重启接续;日报内容生成、业务委派重做和效果未知请求的恢复均不在此范围。定时报告的自然语言配置、完整交互验收,以及独立的可选 terminal-source 误判问题仍未由本 PR 解决。
改动思路
保留该实现的理由是:已有原生监控、租约和回执仍各自拥有事实与效果,新增准入条件由两类会话共用的 TypeScript 规则裁决,Python 只收集宿主证据和维持相关身份。当前 PR 的交付边界是原服务从“尚未观察就重启”恢复到一次原生检查和下一窗口;未知效果、业务执行和报告生成仍留给各自原责任层。
原生观察身份由已有 Todo 和截止时间决定。宿主重启后,新增的相关记录只承载同一个观察,原失败请求不改为成功,也不重新开启。普通监控请求不读取新增恢复证据;只有候选恢复才读取完整原事件、原生历史、当前及旧格式事务回执,并独立排除旧 worker。重置原请求虽更短,却会破坏失败保留和未知效果边界;另设重试服务则重复已有所有权,因此选择扩展原入口。
下表是当前提交的源码控制流对照,不是执行证据;采用表格表述,无未核验渲染的图形。
| 触发状态 | 原入口 | 当前入口 |
|---|---|---|
| 启用监控,观察前宿主退出 | 反复读回失败,截止时间停住 | 核实无效果后,只创建一个关联承载请求,提交原观察 |
| 原租约已释放 | 旧 acquire 回执不能代表新租约 | 新承载请求使用新租约键,原观察身份保持不变 |
| 有 pending/旧事务、破损日志或旧 worker | 不应重复任何效果 | 保留失败和原截止时间,等待原责任层核对 |
| 接续本身失败 | 不能形成无限重试 | 读回已有接续失败,不再创建下一层 |
具体改动
规范依据是 Native status monitoring,路径 docs/product/periodic-reports.md#native-status-monitoring,固定规范版本 9992182。按原规范逐项核对:WATCH-NATIVE 由原 Core worker 和原生 poll 完成,模型与配额断言保持;WATCH-USER 保留活动工作及用户队列优先;WATCH-FENCE 保留当前授权和租约 generation 的最终复核;WATCH-RESTART 保留失败与未知请求,并仅对已证明未观察的请求接续;WATCH-ISOLATION 继续隔离不可读会话,不阻断独立监控。
关键代码讲解
admitMonitorRestartRecovery是唯一新增资格裁决:比较原 actor、Session、Todo、截止时间及请求字段;只接受无响应、无上游 Turn、无增量、无原生 poll 的停止请求,同时保留队列和租约检查。两种会话都调用它。monitor_restart_basis只收集宿主事实。它严格读取完整日志并检查当前与旧格式的 poll 事务;缺失必需原记录、读取错误或旧 worker 占用都不能解释为无效果。StewardTurnHost.submit保留原失败,并以固定相关键查找唯一接续。已有接续无论成功失败都读回;不产生新的原生观察身份。普通ManagedSessionTurn.submit复用相同规则,仍在原 Session 提交围栏内执行。StewardMonitor.observe为接续取得新的租约回执,再由原_commit_poll复核当前范围并调用原生 owner。这样既不借用已释放的旧 acquire 回执,也不更改 poll 的幂等身份。
改动为四个既有生产文件、一个既有回归文件和一处文档,共 +273/-4;测试 +148,文档 +12。没有新增模块、调度器、任务库、CLI 或消息消费者。复用检索覆盖原空闲服务、两个 submit、旧准入失败恢复、报告恢复以及原生 poll/lease owner;相关 PR 7261、7149、7302、7418 的边界未被扩大。
对主干的风险
最强反例是“原请求其实已经有事务或 worker 仍在执行”,以及“原租约已释放却读回旧 acquire 回执”。新增完整日志/事务检查、旧执行锁排除、唯一接续和新租约键分别防止这些情况。异常会保留原失败,不能自动推断业务完成。当前回归通过真实 Chat worker、File/SQLite、原生 Todo、租约与 poll;模型/provider 和崩溃注入为合成边界。控制器重开不等于已证明本机服务采用,因此安装与原服务自行恢复仍须单独验收。
固定 CPython 3.12.15 环境的完整监控、宿主、报告和定时任务回归为 292 passed。相同当前测试文件在固定基线上的 8 个重启场景全部在“没有恢复承载请求”处失败,12 个普通监控及拒绝路径控制通过;当前提交在同一批场景及后续窗口、44 个新增反例和 4 个控制器重开场景中均通过。原始日志与同一测试文件指纹保留,未将预期基线失败当作当前失败。
额外的 legacy adapter mypy 探测在固定基线与当前生产文件均报相同 43 项诊断,逐条规范化签名一致;它不属于仓库当前配置的 17 文件检查范围。配置内 mypy、TypeScript typecheck、17 项共享 typed-owner 测试、改动文件 Ruff,以及合并前 6 项直接检查和 15 项选择检查均通过。未查询或等待远端 CI。早期测试夹具缺目录、额外窗口时间条件及被切换的并行 Python 环境记录均保留,不能作为当前准出结果。
语义检查将新增相关字段判为 local_only:复用现有 work_kind、失败状态、观察身份及原生回执所有权;没有新公共权限模型、词汇 authority 或用户确认义务。文档披露了停止不再永久卡住的默认差异,准入仍是强制裁决而非建议。
我的整体评价
APPROVE;没有阻断性发现。固定环境与基线对照已验证这个可独立交付的监控恢复增量,生产采用仍按原服务单独核验。该边界的 long_horizon=improved、user_experience=improved:一次原观察到持久回执、下一窗口和失败接续停止均有明确路径,已有授权下增加的必需交互轮次为零。本次检查了监控状态读回入口及全部六个改动文件,没有页面呈现、排版、布局或响应式改动,故视觉检查不适用。本次未新增页面截图,也未将后端测试等同于完整定时报告界面验收。
定时报告的自然语言配置、完整交互验收,以及独立的可选 terminal-source 误判问题仍未由本 PR 解决。这些留在原 Chat/report 工作中继续验收,不因本次监控修复而报告整体完成。保守拒绝仍会留下未知事务及失败接续,需要原事务或服务责任层恢复;不能以额外重试绕过。
English verdict: APPROVE for head 3e4bfbd. The bounded original-watch continuation preserves no-replay, authority and quota boundaries. Local validation: 292 Python tests, 17 typed-owner tests and all scoped premerge checks pass; the same baseline harness reproduces 8 restart failures with 12 control passes. Installed adoption and broader report UX remain separate acceptance. No remote CI was consulted.
A host restart could leave an enabled native watch permanently due: the idle pump kept reading its failed Chat request and never advanced the original observation.
Allow one separately correlated continuation only when the original watch has no model activity or native poll evidence, its old worker is excluded, and current ownership, authority, deadline, lease generation and user queue still permit observation. Both local managed Sessions and the steward use the shared typed admission owner. The failed request stays intact; the continuation retains its native observation identity and acquires a fresh lease receipt. Unknown effects and another failed continuation remain held.
Validation: