Repository navigation
fix(context): avoid blocking native hooks on FIFO replay sources - #7466
Conversation
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
启用原生上下文 hook 的 Codex 会话,在 resume 或 PostToolUse 读取 replay history 时会遇到来源不可用的情况。
当来源路径变成没有 writer 的 FIFO 时,旧实现会在检查文件类型前阻塞并持有 journal 锁;新实现立即打开 descriptor,再拒绝非普通文件并走原有恢复路径。
相同四组真实 CLI 用例在基线为两项开启态超时、两项关闭态通过,在当前 head 全部通过,恢复仍指向原始 capture。
本 PR 不改变连续性资格、跨平台支持范围、设置或结算规则;正式 App 采用和模型收益未由这些测试证明。
本次审阅完整 base beede96e0ade200353ee0e4646a0b52d81576c37 到 head 3080cfbc1ed328842b223fe8f5c94bdad1d2ed55,没有继承作者的测试或旧 head 的 approval。判断对象是该可复现的小范围缺口,母目标的自然采用与效果验收继续保留。
改动思路 / Approach
O_NONBLOCK 加在现有共享 Python IO 边界;descriptor 的 regular-file、owner、inode 与权限校验继续保留,TypeScript 仍独占 lifecycle 决策。
本次边界是 FIFO 来源不能阻塞 hook,且失败沿现有完整恢复路径返回;不新增 timeout worker、reader、协议或第二个状态 owner。
不改动会把无效来源卡在 fstat 之前;先按路径 stat 再打开存在竞态。现有 descriptor open 增加非阻塞标志,比新增线程、timeout 或 reader 更小并保留实际文件检查。
Python 保留真正的 host IO,TS 的资格、生命周期和结算规则没有复制到 Python。先打开实际 descriptor 再检查,保留了 inode/owner/权限边界;先按路径 stat 再 open 的较小表面方案反而会引入竞态。现有 seam 已足够清楚,额外抽一个单行 helper 没有独立不变量,bounded refactoring 判断为无需增加结构。
具体改动 / Changes
_snapshot:非阻塞打开并验证实际 descriptor,阻止 FIFO 在 fstat 前挂住。observe_native_read:把完整页观察与合法 replay 来源检查分开。native_resume_verified:来源不能证明连续性时拒绝复用并触发原 capture 恢复。checkpoint_native_read:只在当前 replay 来源验证后生成 checkpoint 证据。
先读取变更前接受合同 docs/reference/required-work-context.md,完整规范 revision 为 beede96e0ade200353ee0e4646a0b52d81576c37。原段落没有独立编号,以下仅作为本次对应索引:
NR-FAILURE:无效或未知 replay 来源不得证明连续性,须可进入恢复;由上述实际 caller 及 base/head、off、恢复对照验证。NR-COMPLETE:完整页交付与跨进程 replay 连续性分别验证;由上述实际 caller 及 base/head、off、恢复对照验证。NR-ORIGINAL:恢复同一原始 capture,不重新准入或扣额;由上述实际 caller 及 base/head、off、恢复对照验证。NR-OFF:显式关闭保留既有输出和无副作用边界;由上述实际 caller 及 base/head、off、恢复对照验证。
正向路径:273 项现有 CLI/lifecycle 测试覆盖普通文件、真实 File/SQLite 临时后端及来源变更;另有 6 项原生进程 resume/history-change 测试通过。 负向路径:同一无 writer FIFO 经 resume/PostToolUse 两个真实入口,在旧基线各触发五秒子进程保护;当前 head 立即拒绝来源且不写 authority store,关闭态 journal 原字节不变。 两条路径都落到实际 codec operation、CLI 或原生 host 返回,没有让 mock 提供待证明的 postcondition。
对主干的风险 / Risks
主要风险是绕过 descriptor 校验或把完整页交付误当跨进程保留。当前 CLI 反例保留两者区分;POSIX 上已验证,Linux/Windows 实机及 live App 采用没有新增证据。
- PASS:四组真实 FIFO CLI 对照;基线为 2 项启用态
TimeoutExpired/ 2 项关闭态通过,head 四项全过。没有外部 writer,也未靠放宽 timeout 通过。 - PASS:resume、lifecycle、saved-project、context-delivery CLI 四个完整模块 273 项通过、5 项显式可选 native 用例跳过;另行启用真实 native binary 的 process resume/history-change 选择 6 项通过。
- PASS:Ruff、配置 mypy(17 source files)、生产与测试 py_compile、diff/public 扫描、维护性 ratchet 和 diff semantic advisory。普通文件仍经历相同 descriptor 校验,关闭态 journal 原字节不变,隔离 registry/runtime 未创建。
- PASS:当前精确范围质量回执
cqr_83424d23bcf7715ad594回读 valid;标准 premerge 的 6 项 direct 与 10 项选中检查实际全过,无 manual hold。全仓 pytest、远端 CI 和跨平台实机没有新增验证。
没有查询、轮询或等待远端 CI。原生验证使用 Codex 0.163.0-alpha.11.1 与隔离的确定性本地 provider;公开说明只包含合成场景和聚合结果,未发布原始运行记录。既有设置开关、无效来源恢复和明确关闭路径继续可用,不新增人工确认轮次。
我的整体评价 / Verdict
原本会在类型检查之前挂住的两条真实 hook 路径,现在能进入既有拒绝和完整恢复流程。正例、反例、关闭态、原生恢复及本地门禁足以支持此小范围修复的 APPROVE;不由此推断安装采用或模型效果,也不授予 merge 权限。
O_NONBLOCK 加在现有共享 Python IO 边界;descriptor 的 regular-file、owner、inode 与权限校验继续保留,TypeScript 仍独占 lifecycle 决策。 本次边界是 FIFO 来源不能阻塞 hook,且失败沿现有完整恢复路径返回;不新增 timeout worker、reader、协议或第二个状态 owner。
视觉审阅:已检查 loopx/control_plane/turn_driver/codex_context_history.py 及既有 machine-configuration-settings.tsx;本次没有渲染页面或交互文案变化。 未验证范围:未新增 live App 安装、真实模型采用或跨平台实机资格。 历史经验建议用于追踪真实恢复结果,没有将 context delivery 声称为已验证的 memory utility。
English verdict: APPROVE 3080cfb — FIFO regression reproduced on base and fixed through both real hook entries; local qualification passed, platform/adoption limits retained.
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
3080cfb to
6953693
Compare
Goal And Delivered Outcome
原生上下文 hook 持有 journal 锁打开 replay history,然后才检查文件类型。若
transcript_path被替换为没有 writer 的 FIFO,SessionStart/resume 与 PostToolUse 会在fstat前阻塞,无法进入既有恢复路径。共享
_snapshot现在以O_NONBLOCK打开来源,仍按实际 descriptor 检查 regular-file、owner、inode 和写权限。来源不能证明跨进程连续性时,沿用 typed lifecycle 恢复同一原始 capture;完整页交付与跨进程 replay 资格继续分开。6953693950369174dacf5d684c46420cf4d54bb4.loopx-project/loopx:main,整合至17b2a0f9b818dc6e6fe77ccc567b20ff91b3bc40。Author Declaration
docs/reference/required-work-context.md, “Opt-in native reuse”, at17b2a0f9;完整交付、未知来源与同 capture 恢复边界。codex_context_history._snapshot;resume/PostToolUse FIFO 回归及同 capture 原始字节断言native_resume,下次 resume 全量恢复quota context-hook;关闭态 journal 逐字相同,隔离 registry/runtime 不创建Scope And Integration
PR diff 仍只有共享 Python IO 入口与真实 CLI 回归两文件,64 insertions / 1 deletion。原有三种调用方共用
_snapshot;未增加 reader、配置、词表、timeout 或 authority。保留 descriptor 检查,避免 path-stat 后打开的竞态。本次 rebase 前后两份改动文件逐字相同。本次消费已合入 main 的 #7459 协议兼容 fixture 修复:历史 capture 的哈希按捕获字节验证,fresh writer 与 fresh Envelope 分开比较,完整
execution_obligation保留。该修复不是本 PR 新增实现。main 也已有 Goal drawer smoke 的布局预期更新;此处未修改 UI 或其断言。Validation
以下为 新 head
69536939的实际结果。Python 3.14.8、Node 24.21.0、SQLite 3.53.4;native 用例使用 Codex 0.163.0-alpha.11.1、一次性 SQLite 与确定性 loopback provider。bd2bc15e9592747ba9c89443d56fc2adb5a45ad5c2a6d0526dee215d73412af3。TimeoutExpired与两项关闭态通过;新 wheel 四项通过。原 capture 字节、关闭态 journal 和零 registry/runtime 副作用断言保留。此负例是预期复现,不是被豁免的验证失败。context_delivery.test.ts+protocol_action_packet_compat.test.ts:32 passed / 0 failed / 0 skipped。包含整合后的上下文定位与历史/当前协议比较。npm run build:chat;CI 规定的五个 Ruff roots;配置 mypy 17 source files;diff、DCO 与两文件 public boundary scan。未绕过 frontend 打包一致性检查。cqr_849c21c08926228dbcd6回读 valid;standard premerge 6 项 direct + 10 项选中检查全部通过。仅是本地验证资格,不授予 merge 权限。旧
3080cfbc1上已执行的六项 process-resume/changed-history 原生用例,以及此前隔离 wheel 四项 FIFO 资格,保留为该旧版本历史结果;没有冒充新 head 结果。新 head 的资格以上表新运行和新 wheel 为准。准备阶段的失败也保留:首次隔离解释器定位错误导致 pytest 未启动;补正后发现 fixture 缺少已安装 TypeScript 产品路径,四项在启动 native 前失败。随后仅将 fixture 别名指向实际安装包,原测试和断言未修改;新 head 按正确布局重新构建、安装并执行完整九项。
CI / Review Holds
当前 head
69536939的 release build 已失败于blocked-reason-readback:窄屏 Todo inspector 打开时,测试仍操作背景的“刷新状态”按钮。以下本机对照已将这一项从“相同源码、尚未归因”推进到可复核的测试路径归因,完整远端 CI 仍未通过。17b2a0f9与 head69536939的对应 wheel 分别安装到隔离 Python 3.11.17(base 本轮构建,head 复用该提交已构建的 wheel);Node 24.21.0、Playwright 1.62.1、同一 macOS arm64 浏览器环境。两份包的 1749 个 Python/TypeScript 模块逐一核对 source/wheel/install,全部 36 个前端 bundle 文件相同;产品包只差本 PR 的codex_context_history.py。Promise.all的waitForResponse超时;远端先报locator.press超时,保留这一诊断差异。ca17664d0ccf6176373cbc8cb57515c1c6591880后,两份未改产品包均完成全场景:每份 中文/英文 × 桌面/390px 四组合通过。修正显式核对窄屏aria-modal/背景inert,关闭 inspector 后刷新并重开同一 Todo;桌面仍保持 inspector 打开,原动态原因、缺原因、延迟响应、恢复/归档及零 UI 写入断言保留,无增加 timeout。以下失败发生在旧 head
3080cfbc1的生成 mergecb777a9bc(target59c8bbf79),不会因 rebase 被追溯改为通过。新 head 需要新 CI 和准确提交评审;旧 head 上的COMMENTED APPROVE不延用于本次提交。execution_obligation与不可变旧 capture 直接比较。对不可变旧 base 与旧 head 独立运行同一命令,均为 4 passed / 1 failed、同一字段差异;上游 test: align release fixtures with canonical authority and replan contracts #7459 已修复。整合后的本地相关两套测试现为 32 passed;未据此声称完整远端 shard 已通过。mutation_lock_timeout,实际runtime_startup_timeout;相关源码未变不等于 Windows baseline 已复现。失败仍未归因或豁免,未扩大 deadline。因此完整 CI / merge readiness 尚未资格;本 PR 保持正常 review 状态,未合并、正式安装或发布。自然 App/solver 采用、真实任务效果及完整成本仍属于原有验收,未以本小范围修复关闭这些要求。
Frontend / Visual Evidence
UI impact:
none;无页面、展示文案或交互改动,截图与预览不适用。Type of Change / Boundary