Repository navigation
fix(heartbeat): preserve the Codex App quiet completion receipt - #8454
Conversation
Signed-off-by: ZaynQuinn <95160622+ZaynQuinn@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
动机 / Motivation
Codex App 的定时任务在没有新消息时,也可能需要返回包含标识、决定和原因的完整完成回执。 旧提示把安静写成完全不输出,或要求单独返回跳过原因,可能诱导模型漏掉宿主回执;新提示明确聊天保持安静,同时完整返回宿主要求的结果。 已验证实际 CLI 的 thin/full 提示及共享渲染路径消除了这组歧义,未改变执行准入、结算命令或通知许可。 本 PR 不增加通知、不安装运行时、不更改调度,也不替宿主定义新的完成协议。 提示文本修复已经验证;真实 Codex App 模型是否持续遵从完整回执规则,仍未通过本次源码检查证明。
改动思路 / Approach
入口仍是 heartbeat-prompt,经 builder 选择已有渲染器,再组合通知规则。通知字段只决定给用户看什么;是否工作、是否写回以及观察回执是否记账,仍由当前控制面契约分别决定。宿主若要求完整完成结果,生成器应让出具体格式的定义权,不能用“安静”把它覆盖。
安静输出语义属于已有提示投影 owner;共用一句条件规则足够,无需在 Core 硬编码宿主 XML、增加状态或建立新执行分支。本 PR 只修正生成提示的完成格式指引,保留通知、执行与结算各自的现有权威。与仅修改一份持久 automation 文本相比,修共享生成源能覆盖每次唤醒获取的实际提示,也避免不同模式各自维护含义。
具体改动 / Changes
关键代码讲解
评审头为 8454@017abe70456fd8b820ec02f9cad0ce7ef4d6ab17,实际 diff 基线为 cffb53fe46ea57bae5394cd633bf98f71af93d3a。五个文件增加 39 行、删除 12 行:两个生产提示文件、两个已有测试文件和操作文档,没有新模块、命令、状态或协议版本。
HEARTBEAT_QUIET_COMPLETION_RULE统一“无聊天文本,完整返回宿主要求的静默结果”。full 与 brief/thin 的公开输出规则复用它。条件词 required 保留:不要求回执的宿主不会被发明出一种新响应。render_heartbeat_task_body删除 should_run=false 最后分支的单独 skip-reason 响应,改为遵守上方完整用户通道规则。该分支禁止实施、编辑和探索的限制保留;monitor_quiet_skip 的原回执、同 ID 重试与 no-spend 路径未改。render_thin_heartbeat_task_body对普通与 captured-envelope 两个分支都补齐规则。action.must_attempt=work仍是工作义务;agent_context 虽从并列清单移除,下一句仍明确要求 planning 前读取 inline agent_context,并保留 detail_ref 读取路径。因此这部分是去掉重复提及,没有删除阅读义务。compact 的旧 quiet 映射也由相同规则替代。
规范依据为 docs/heartbeat-automation-prompt.md,spec_revision 为 cffb53fe46ea57bae5394cd633bf98f71af93d3a,按改动前契约判断:Two Prompt Layers 的条件、响应和预算保真已实现;Agent Checklist 2 的观察回执、无重复 poll、无 delivery/spend 已保留;Agent Checklist 9 的 quiet 不能取消 admitted work 已保留。准确宿主格式仍由宿主提供,本修复没有自创完成协议。
对主干的风险 / Risks
最强反例是“用户不需要消息,但本轮必须工作”,或者“本轮只观察,但宿主需要完整 quiet receipt”。我逐条比较了输出、执行、时序、失败恢复和结算义务;新规则不能把前者变成 no-op,也不能让后者获得编辑或扣额权限。generic_cli 同样消费共享提示,故补做了四模式、两 profile 的八组 base/head 比较:非 task_body/interface_budget 的顶层字段逐项相等,提示新增的是有意披露的共享格式澄清。这不是默认关闭功能,没有以安装或可用性代替启用的问题。
PASS:38 项针对性测试覆盖最长 host scope、预算、通知与工作分离、五种渲染变体及 envelope;测试临时目录已清理,未留下 Git fixture。真实源码 CLI thin/full 在 immutable base 均出现旧歧义,head 均输出完整条件规则。原先无明确 registry/runtime 的额外探针曾被本机双默认根冲突拒绝,随后改为显式隔离路由完成对照;该失败发生在环境路由阶段,不是产品回归,也没有放宽断言。PASS:quick premerge 的直接检查和三个选定 canary,包含维护预算与语义词汇扫描;该 quick tier 不是全树穷尽测试。远端 CI 依配置不查询。
语义与 CI 对齐
复用既有 notify 与 must_attempt vocabulary,仅把宿主 quiet completion 的提示义务说明完整;没有 substring 分类器、额外权限或领域专用任务要求。提示是给模型的义务,测试并不强制模型遵从。作者保留了两次未复现原始裸 decision 症状的结果,本评审也未执行模型或安装流程,不能把文本检查等同于真实宿主问题已消失。
我的整体评价 / Verdict
当前结论是 justified_increment,范围和机制成本相称。long_horizon 判定 preserved:持续工作和原结算仍由控制面决定;user_experience 判定 improved,限于消除实际生成提示歧义且没有新增用户步骤。改动是生成给 Agent 的提示和文档,没有应用页面布局或可视组件变更;已检查实际 CLI 输出。未观察安装后 Codex App 的模型长期遵从与真实宿主回执,未将提示检查当作该结果。共享常量已完成有价值的局部收敛,无需再抽象一套机制;没有持久兼容迁移负担。
English verdict: APPROVE the bounded prompt-source correction at the reviewed head; actual model compliance and installed-host incident resolution remain unverified.
Goal And Delivered Outcome
A scheduled LoopX heartbeat ended with
<decision> DONT_NOTIFY </decision>in the regular Codex App chat. The chat should have stayed quiet.The run used
heartbeat-prompt --thin --codex-app. The thin prompt saidDONT_NOTIFY=no output.It did not explain that the chat should stay quiet while the host can still require a complete receipt. The full prompt also asked for a skip reason without saying where it belonged.This PR targets
main. The screenshot is from the original incident. The script below reproduces the LoopX prompt defect against the base commitcffb53fe46ea.Reproduction
Download repro_loopx_heartbeat.py or copy the script below. Run it with Python 3.11+ against a baseline checkout and a checkout with this patch. It generates the thin and full prompts through the actual LoopX CLI using a temporary fixture. It does not require Codex App or a model call.
Before the fix, both checks print
FAILand the script exits with code 1. After the fix, both printPASSand it exits with code 0.repro_loopx_heartbeat.py
Fix
I used one rule across the heartbeat prompt variants, including the thin TurnEnvelope path:
I removed the separate instruction to include the skip reason in the full prompt's final response because it left the placement ambiguous. When Codex App requires a short status explanation, it still goes in the complete receipt's
<message>field. For diagnostics,loopx quota should-runstill provides the structuredreason.should_runstill governs execution. The background receipt, stall observation and retry formonitor_quiet_skipstill use the existing flow.NOTIFYfollows the existing notification branch.Author Declaration
Scope And Continuation
Complete within the LoopX prompt scope. The patch updates the shared guidance, its tests and the heartbeat documentation.
Validation
017abe70456fd8b820ec02f9cad0ce7ef4d6ab17finishedsyntheticreal_entrypointpassedregression_paritypassedintegrationpassedmanualpassedNOTIFYmarked the chat unread.manualfailedintegrationnot_runFrontend / Visual Evidence
none. The change is in LoopX prompt generation.Type of Change
LoopX Area
Shared-authority RFC fixture impact
N/A.
Boundary Checklist
none.Signed-off-bytrailer.