fix(goal): prevent ambiguous quota spend retries - #3258
Conversation
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
huangruiteng
left a comment
There was a problem hiding this comment.
Request changes conclusion (author-owned PR; GitHub review-state fallback)
精确评审头(Exact Head): 3258@ee75fff5151b05cb6f9d20a8ece35fe76e4e7fb4
Base: main | 类型: runtime prompt + docs(8 files, +41/-37) | 作者: huangruiteng(owner)
详细中文评审
动机
PR 解决一个真实的运行歧义:quota spend-slot 是带账本副作用的变更命令,但渲染给 agent 的命令默认没有 --format json;agent 若用管道/过滤消费输出,可能因 Markdown 输出被吞掉而把“已成功但无输出”误判为失败,随后重复执行 spend 造成重复记账。PR 给 Goal 与 heartbeat prompt 中渲染的 spend 命令统一加 --format json,并写入“原样执行一次、不加管道/过滤、输出不明确时用只读 quota status 核对、不得重跑”的显式契约,同时保持 compact/brief 变体在既有 interface budget 内。
改动思路
单点修改渲染源头,再同步所有下游投影与断言:project_prompt.render_quota_spend_command 直接生成 --format json 前缀;task_body 的 full/brief/compact 三套 body 都追加 no-pipe/no-retry 指引;对应文档(heartbeat-automation-prompt、new-project-codex-prompt)与三个 smoke(heartbeat-prompt-smoke、project-prompt-smoke、install-local-smoke)和 test_host_loop_activation 同步断言。顺带对 goal prompt 做措辞压缩(保持语义、减少 token)。
具体改动
loopx/project_prompt.py(+7/-4):render_quota_spend_command加--format json;bootstrap 消息与中文 prompt 增加“原样执行、不加管道/过滤、不明确时用只读 quota status、不能重跑”。loopx/control_plane/heartbeat/task_body.py(+12/-13):full/brief/compact 三套 body 的 spend 步骤改为“spend once as rendered; no pipe/filter/retry”,并在 brief/compact 中把关键短语压缩(如 “Brief LoopX heartbeat; detail”),保持预算。docs/heartbeat-automation-prompt.md(+2/-2)、docs/operations/new-project-codex-prompt.md(+3/-2):同步--format json与 no-pipe/no-retry 指引。examples/control_plane/heartbeat-prompt-smoke.py(+10/-10)、examples/project/project-prompt-smoke.py(+4/-3)、examples/install-local-smoke.py(+1/-1):断言改为含--format json的渲染命令及新指引。tests/test_host_loop_activation.py(+2):断言quota_spend_command以loopx --format json开头、body 含 “no pipe/retry”。
关键行为讲解
render_quota_spend_command:所有下游(Goal、heartbeat、project prompt)从同一函数拿到loopx --format json --registry ... quota spend-slot ...,机器可读输出消除“空输出=失败”的歧义。- no-pipe/no-retry 契约:full 与 compact body 均写明“exactly once / no pipe/retry”,并指定歧义时用只读
quota status核对,而不是重跑变更命令——这是防重复记账的核心。 - 措辞压缩:
_render_goal_task_body把 “At each continuation inspect LoopX state/status/repo” 压成 “Each continuation inspect state/status/repo”,把 “take the highest-priority in-scope unblocked agent todo” 压成 “take highest-priority unblocked in-scope todo”;语义不变但改变了下游断言文本。
正向路径
Agent 按渲染命令执行 loopx --format json ... quota spend-slot ... → JSON 输出即使被截断也稳定可解析;若输出不明确 → 只读 quota status 核对 → 不重复 spend → 记账唯一。
负向路径
仍使用管道/过滤吞掉输出 → 契约明确要求不得据此重跑;默认无 --format json 的旧命令被所有渲染源替换;compact/brief 预算由 smoke 断言守护。
对主干的风险
阻断项(P1,CI):exact head 上全量 pytest 1 failed——tests/test_ark_managed_agent_host.py::test_goal_prompt_is_one_transport_independent_activation 仍断言旧短语 take the highest-priority in-scope unblocked agent todo,而 task_body.py 已压缩为 take highest-priority unblocked in-scope todo(本地已复现)。PR 更新了 test_host_loop_activation 但漏了 Ark 托管 host 的同一断言。修复:同步该测试断言(或保留原短语)后重推。
非阻断(P2):goal prompt 措辞压缩同时改动了其他可见短语(如 “Each continuation inspect state/status/repo”),建议顺带 grep 仓库内其他断言旧短语的 smoke/test,避免下一次 CI 再红;compact/brief 预算变化已被 smoke 覆盖。
验证矩阵(exact head 实测):
| 场景 | 结果 |
|---|---|
examples/control_plane/heartbeat-prompt-smoke.py |
PASS |
examples/project/project-prompt-smoke.py |
PASS |
tests/test_host_loop_activation.py |
77 passed |
tests/test_ark_managed_agent_host.py::test_goal_prompt_is_one_transport_independent_activation |
FAIL(复现 CI:旧短语断言) |
| GitHub pytest | 1 failed / 3406 passed / 2 skipped |
| GitHub checks | Sign-off/build/dependency-review pass;pytest FAIL |
| 改动范围 | 8 files, +41/-37,runtime prompt 渲染 + docs/smokes/tests |
我的整体评价
改动方向正确、范围收敛:机器可读 spend 输出 + 显式 no-pipe/no-retry + 只读 status 回退,能有效消除重复 spend 记账风险,且所有渲染源、文档与 smoke 同步一致。唯一阻断是 CI 红——漏同步 Ark 托管 host 测试里同一句 prompt 断言;补上即可放行。结论:REQUEST_CHANGES(窄化到测试断言同步)。
English Verdict
Verdict: REQUEST_CHANGES — exact head 3258@ee75fff5151b05cb6f9d20a8ece35fe76e4e7fb4.
Summary: The change is well-scoped and sound: rendered quota spend-slot commands now carry --format json, with an explicit once-only / no-pipe / no-retry contract and a read-only quota status fallback, synced across prompt renderers, docs, and smokes. Heartbeat/project smokes and the host-loop suite pass at the exact head (77 tests).
Blocking (P1): CI pytest is red — tests/test_ark_managed_agent_host.py::test_goal_prompt_is_one_transport_independent_activation still asserts the old phrase "take the highest-priority in-scope unblocked agent todo", which the PR compressed to "take highest-priority unblocked in-scope todo" (reproduced locally). Sync that assertion (or restore the phrase) and re-push.
P2: grep for other stale assertions of the compacted goal-prompt phrases before merge.
huangruiteng
left a comment
There was a problem hiding this comment.
Request changes conclusion (author-owned PR; GitHub review-state fallback)
精确评审头(Exact Head): 3258@94c6db0740c624b5bc4b3579214f7a387f882749
Base: main | 类型: runtime prompt + docs + tests(10 files, +47/-43) | 作者: huangruiteng(owner)
详细中文评审
动机
这是对上一轮 REQUEST_CHANGES(3258@ee75fff5)的跟进:作者在 spend --format json + no-pipe/no-retry 基础上,又增加“terminal guard readback”——spend 后以只读方式重跑同一 guard,只有 should_run=false + terminal no-follow-up 才完成 Goal,否则服从 guard 的 next action。动机合理:防止“spend 已消费但 Goal 仍非 terminal”的状态漂移,把完成判定交给只读 guard 而非 prompt 自述。
改动思路
在 _render_goal_task_body 的 closeout 区新增一行“Rerun the same guard read-only. Complete ... only on should_run=false + terminal no-follow-up”,并把“Reuse this activation across phases/revisions until terminal”压缩为“Reuse this Goal until terminal”;test_visible_goal_terminal_settlement 增加顺序断言(refresh < spend < readback)与 terminal_readback 短语断言。核心仍是单一渲染源 + smoke/测试同步。
具体改动(相对上一 head)
loopx/control_plane/heartbeat/task_body.py(+6/-6):新增 terminal guard readback 指令;压缩 “Reuse this Goal until terminal”;移除旧的 “Complete ... only on LoopX terminal success” 措辞。tests/test_visible_goal_terminal_settlement.py(+7):断言 spend 后必须出现 readback,且 terminal 完成条件为should_run=false+ no-follow-up。- 上一 head 的 8 个文件(docs/smokes/
project_prompt.py/test_host_loop_activation)保持不变。
关键行为讲解
- Terminal guard readback:spend 之后 “Rerun the same guard read-only”,完成条件从“LoopX terminal success”改为“
should_run=false+ terminal no-follow-up”——把结束判定绑定到 guard 的只读状态,避免 agent 自报完成。 - 顺序契约:
refresh < spend < readback由测试锁定,防止未来把 readback 挪到 spend 前。 - 措辞压缩延续:上一轮的 “take highest-priority unblocked in-scope todo” 保留,但 Ark 托管 host 测试的旧短语仍未同步。
正向路径
Goal 完成 → refresh → spend 一次 → 只读重跑 guard → should_run=false + no-follow-up → 完成;若 guard 仍返回 next action,则服从它而不是标记完成。
负向路径
spend 后 guard 仍 should_run=true → 不完成、继续下一 action;输出不明确 → 只读 status 核对、不重跑 spend;pipe/过滤吞掉输出 → 契约禁止据此重跑。
对主干的风险
阻断项(P1,两个):
- DCO sign-off 缺失:commit
94c6db0740的 message 无Signed-off-bytrailer,GitHub Sign-off check FAIL。 - CI pytest 仍红:
tests/test_ark_managed_agent_host.py::test_goal_prompt_is_one_transport_independent_activation仍未同步——上一轮已指出take the highest-priority in-scope unblocked agent todo旧短语,本轮新压缩又引入Reuse this Goal until terminal,该测试同时断言旧版Reuse this activation across phases/revisions until terminal(本地已复现 FAIL,先挂在 76 行,修复后 85 行还会挂第二个旧短语)。
非阻断(P2):pytest CI 评审时刻 pending;heartbeat-prompt-smoke 与 test_visible_goal_terminal_settlement 在 exact head 均通过(后者验证新 readback 顺序),说明 prompt 本体与新测试正确,问题集中在 Ark 测试同步。
验证矩阵(exact head 实测):
| 场景 | 结果 |
|---|---|
examples/control_plane/heartbeat-prompt-smoke.py |
PASS |
tests/test_visible_goal_terminal_settlement.py |
PASS(readback 顺序断言) |
tests/test_ark_managed_agent_host.py::test_goal_prompt_is_one_transport_independent_activation |
FAIL(旧短语未同步,本地复现) |
| commit sign-off | 缺失 → Sign-off check FAIL |
| GitHub checks | build/dependency-review pass;Sign-off FAIL;pytest pending |
| 改动范围 | 本轮 +2 files(+13/-6),相对 ee75fff |
我的整体评价
Terminal guard readback 是正确且重要的收敛:把完成判定交给只读 guard,顺序由测试锁定,与上一轮的 no-pipe/no-retry 契约同构。但本 head 仍有两个 P1:Sign-off 缺失,以及 Ark 托管 host 测试的旧短语未同步(且因新压缩新增第二个失配短语)。结论:REQUEST_CHANGES(窄化:补 sign-off + 同步 Ark 测试断言)。
English Verdict
Verdict: REQUEST_CHANGES — exact head 3258@94c6db0740c624b5bc4b3579214f7a387f882749.
Summary: The new terminal guard readback (re-run the guard read-only after spend; complete only on should_run=false + no-follow-up) is a sound improvement, and the heartbeat smoke plus the visible-goal settlement test pass at the exact head. Two pre-merge blockers remain: (1) commit 94c6db0740 lacks a Signed-off-by trailer (Sign-off check FAILS), and (2) tests/test_ark_managed_agent_host.py::test_goal_prompt_is_one_transport_independent_activation still asserts the old prompt phrases, now mismatching two compressed strings (reproduced locally).
huangruiteng
left a comment
There was a problem hiding this comment.
Re-review at exact head: blockers unchanged; one P2 added
精确评审头(Exact Head): 3258@94c6db0740c624b5bc4b3579214f7a387f882749
Base: main | 类型: runtime prompt + docs + tests(10 files, +47/-43) | 作者: huangruiteng(owner)
这是一次独立复核,确认此前 exact-head REQUEST_CHANGES 结论仍然成立,并补充一条此前未指出的 P2。
详细中文评审
动机
PR 解决两类真实运行风险:一是 quota spend-slot 是带账本副作用的变更命令,但渲染命令默认输出 Markdown,agent 若用管道/过滤消费输出,可能把“成功但输出被吞”误判为失败并重复执行,造成重复记账;二是 spend 成功不等于 Goal 已 terminal,刷新后的状态可能仍存在 runnable 后继。修复方向是给所有渲染的 spend 命令加 --format json,写入“原样执行一次、不加管道/过滤、不得重跑”的契约,并在 visible Goal host 上要求 spend 后只读重跑 guard,只有 should_run=false + terminal no-follow-up 才完成 Goal。
改动思路
单点改渲染源头再同步下游:render_quota_spend_command 统一加 --format json;full/brief/compact heartbeat body 增加 no-pipe/no-retry;_render_goal_task_body 增加 spend 后 guard readback 与新的完成条件;render_codex_cli_bootstrap_message_text 和中文 project prompt 增加“输出不明确时用只读 quota status 核对、不能重跑”的恢复路径;对应文档、smoke、测试同步更新。整体是 prompt 契约收敛,不是状态机或数据模型改动。
具体改动
loopx/project_prompt.py(+7/-4):render_quota_spend_command加--format json;bootstrap 消息与中文 prompt 增加原样执行、不重跑、只读 status 核对。loopx/control_plane/heartbeat/task_body.py(+12/-13 后 +6/-6):full/brief/compact body 的 spend 步骤改为 “spend once / no pipe/retry”;goal body 增加 “Rerun the same guard read-only. Complete ... only onshould_run=false+ terminal no-follow-up”。docs/heartbeat-automation-prompt.md、docs/operations/new-project-codex-prompt.md:同步--format json命令。examples/control_plane/heartbeat-prompt-smoke.py、examples/project/project-prompt-smoke.py、examples/install-local-smoke.py、examples/codex-cli-bootstrap-message-smoke.py:同步新命令与短语断言。tests/test_visible_goal_terminal_settlement.py(+7):锁定refresh < spend < readback顺序与 terminal readback 短语。tests/test_host_loop_activation.py(+2):断言quota_spend_command以--format json开头、body 含no pipe/retry。
关键代码讲解
render_quota_spend_command(loopx/project_prompt.py:115):所有 Goal/heartbeat/project prompt 从同一函数拿到loopx --format json --registry ... quota spend-slot ...,机器可读输出消除“空输出=失败”的主要歧义。_render_goal_task_body(loopx/control_plane/heartbeat/task_body.py:544):把完成判定从 “LoopX terminal success” 改为 “spend 后只读重跑 guard,且should_run=false+ terminal no-follow-up”,防止 agent 自报完成而 LoopX 仍有后继。render_codex_cli_bootstrap_message_text(loopx/project_prompt.py:758):在项目接入场景给出“输出不明确 -> 只读 quota status -> 不重跑”的恢复路径。
正向路径
Heartbeat:guard → 选定 todo → 验证/writeback → refresh 因果记录 → 原样执行一次 JSON spend → 不重跑 → 可选 state-only refresh → 按 notify 返回。Visible Goal:同上,spend 后只读重跑 guard;should_run=false + 无后继才完成,否则服从 next action。
负向路径
Agent 给 spend 加管道/过滤导致输出为空:契约禁止据此重跑;项目接入面还明确用只读 quota status 核对,避免重复记账。Visible Goal 在 spend 后 guard 仍 should_run=true:不完成,继续执行 guard 的 next action,而不是把 spend 当作 terminal 证明。
对主干的风险
阻断项(P1)
- DCO:commit
94c6db0740缺少Signed-off-bytrailer,GitHub Sign-off check FAIL。前两个 commit 已有签名,只有最后一个没有;修复方式是最后一个 commitgit commit --amend --signoff后 force-push。 - pytest 仍红:
tests/test_ark_managed_agent_host.py::test_goal_prompt_is_one_transport_independent_activation仍断言两句旧文案:Reuse this activation across phases/revisions until terminal(77 行)和take the highest-priority in-scope unblocked agent todo(85 行)。PR 已压缩为Reuse this Goal until terminal与take highest-priority unblocked in-scope todo。本地已复现:fix 第一句后第二句仍会失败。
非阻断(P2)
- heartbeat 文档与生成 body 不一致:
docs/heartbeat-automation-prompt.md第 8 步只把 spend 命令改成--format json,但没有同步生成 body 中的 “run spend once as rendered; no pipe/filter/retry”,也没有项目 prompt 里已有的 “输出不明确 -> 只读 quota status 核对 -> 不重跑” 恢复路径。既然 PR 动机是 heartbeat 场景的重复 spend,建议把同一契约同步进 heartbeat 文档(以及render_heartbeat_task_body的 full body),否则文档与生成 prompt 会再次漂移。
验证矩阵(exact head 实测)
| 场景 | 结果 |
|---|---|
examples/control_plane/heartbeat-prompt-smoke.py |
PASS |
examples/project/project-prompt-smoke.py |
PASS |
examples/install-local-smoke.py |
PASS |
examples/codex-cli-bootstrap-message-smoke.py |
PASS |
examples/codex-cli-tui-bootstrap-smoke-bundle-smoke.py |
PASS |
tests/test_visible_goal_terminal_settlement.py + tests/test_host_loop_activation.py |
78 passed |
tests/test_ark_managed_agent_host.py::test_goal_prompt_is_one_transport_independent_activation |
FAIL(两句旧文案,本地复现) |
| GitHub checks | Sign-off FAIL;pytest FAIL;其余 pass |
git diff --check |
PASS |
我的整体评价
改动方向正确、范围收敛,--format json + no-pipe/no-retry + terminal guard readback 是同一类“防重复/防假完成”契约的合理延伸,现有 smoke 与新增顺序断言都通过了。但 exact head 仍有两个 P1:最后一个 commit 缺 sign-off,以及 Ark 托管 host 测试的两句旧文案未同步;另外 heartbeat 文档还缺少 no-pipe/no-retry 与只读 status 恢复路径(P2)。结论:REQUEST_CHANGES,修复范围窄且明确。
English Verdict
Verdict: REQUEST_CHANGES — exact head 3258@94c6db0740c624b5bc4b3579214f7a387f882749.
Summary: The change is sound and well-scoped: generated spend commands now use --format json, prompt variants enforce once-only / no-pipe / no-retry, and visible Goal hosts must re-run the read-only guard after spend before completing. All focused smokes pass at the exact head.
Blocking (P1): (1) commit 94c6db074 is missing a Signed-off-by trailer (DCO check FAILS); (2) tests/test_ark_managed_agent_host.py::test_goal_prompt_is_one_transport_independent_activation still asserts two old prompt phrases that the PR compressed, so CI pytest FAILS (reproduced locally).
Non-blocking (P2): docs/heartbeat-automation-prompt.md only syncs the JSON command, not the no-pipe/no-retry and read-only quota status fallback that the generated prompt and project-prompt surfaces now carry.
Signed-off-by: huangrt01 <huangrt01@163.com>
Signed-off-by: huangrt01 <huangrt01@163.com>
94c6db0 to
90c9ed0
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
精确评审头(Exact Head): 3258@90c9ed0422d5537bec2c7f6bd0a833b20a845892
Base: main | 类型: spend retry 契约修复的 P1/P2 收口(4 files, +13/-5)
作者: huangruiteng(owner)
详细中文评审
动机
此前的 exact-head 评审(94c6db0740)留下两个 P1(最后一个 commit 缺 DCO sign-off;Ark 托管 host 测试仍断言两句旧 prompt 文案)与一个 P2(heartbeat 文档未同步 no-pipe/no-retry 与只读 status 恢复路径)。作者新增两个 commit 收口这些问题。
改动思路
窄修不改设计:给缺失 trailer 的 commit 补 sign-off(force-push 后本头两个新 commit 都带 Signed-off-by);把 tests/test_ark_managed_agent_host.py 的两句旧断言同步为压缩后的新文案(Reuse this Goal until terminal、take highest-priority unblocked in-scope todo);把“原样执行一次、不加管道/过滤、不重跑;输出不明确时用只读 quota status 核对”写入 heartbeat 文档与生成 body,并在 heartbeat-prompt-smoke 增加对应断言。
具体改动
tests/test_ark_managed_agent_host.py(+0/-2 断言):两句旧短语 → 新压缩短语。docs/heartbeat-automation-prompt.md(+10/-3):spend 步骤与 Agent Checklist 增加 exactly-once / no pipe/filter/retry / 只读 status 恢复路径。loopx/control_plane/heartbeat/task_body.py(+3):full body 的 spend 步骤同步同一恢复路径。examples/control_plane/heartbeat-prompt-smoke.py(+1):断言新增恢复路径文案。- commit
2a14feb0b/90c9ed042:均带Signed-off-bytrailer(Sign-off check 已 SUCCESS)。
关键内容讲解
- P1-1 已消:新头两个 commit 的 trailer 存在,GitHub Sign-off check SUCCESS。
- P1-2 已消:本地在 exact head 复跑
test_ark_managed_agent_host.py通过(旧文案断言已同步)。 - P2 已消:heartbeat 文档与生成 body 现在承载同一“exactly-once + 只读核对”契约,smoke 锁住文案,文档/生成面不再漂移。
对主干的风险
无阻断项。改动是文档/测试/提示文案同步,不含运行语义变化;此前已批准的核心修复(--format json + terminal guard readback)不变。唯一红色检查是 main 上既有的 Frontstage Pages docs 构建失败(ModuleNotFoundError: loopx,在 main 的 #3265 前后同样失败),非本 PR 引入,合并前需在 main 侧修复/清理。
验证矩阵(exact head 实测)
| 场景 | 结果 |
|---|---|
pytest test_visible_goal_terminal_settlement / test_host_loop_activation / test_ark_managed_agent_host |
91 passed |
heartbeat-prompt-smoke / project-prompt-smoke / install-local-smoke / codex-cli-bootstrap-message-smoke |
PASS |
| 新 commit DCO trailer | 存在(Sign-off check SUCCESS) |
| Sign-off / dependency-review / Release Artifacts build | SUCCESS(GitHub) |
| pytest(CI) | IN_PROGRESS(评审时刻) |
| Frontstage Pages(docs build) | FAILURE(main 既有问题,非本 diff 引入) |
我的整体评价
两个 P1 与一个 P2 全部按评审要求收口,修复范围窄且可验证;核心契约不变,本地 91 测试与 4 个 smoke 全绿。结论:APPROVE(author-owned PR,以 COMMENTED 记录批准结论;合并前置条件 = CI pytest 绿 + 修复/清理 main 既有 Frontstage Pages 失败)。
English Verdict
Verdict: APPROVE (author-owned PR; recorded as COMMENTED because GitHub blocks formal self-approval). Both prior P1 blockers and the P2 are resolved at this head: the new commits carry Signed-off-by trailers (Sign-off SUCCESS), the Ark managed-host test asserts the compressed prompt phrases and passes, and the heartbeat docs/body/smoke now carry the exactly-once + read-only-verification contract. 91 focused tests and 4 smokes pass locally. The only red check is the pre-existing main Frontstage Pages docs-build failure, not introduced by this diff.
Review addressedExact head: Addressed all review findings:
Validation:
Note: the Frontstage Pages |
What changed
quota spend-slotcommands with JSON output across Goal and heartbeat promptsWhy
A quota spend can succeed even when an added shell filter hides its Markdown output. An agent may interpret the empty output as a failed command and run the mutation again, producing duplicate accounting. Machine-readable output plus an explicit no-pipe/no-retry contract closes that ambiguity.
Spend success is also not proof that the Goal is terminal: the refreshed state may still project runnable follow-up or repair work. A mandatory read-only guard after spend prevents the host Goal from reporting completion while LoopX still requires another action.
Validation
scripts/loopx canary premerge --from-git-diff --format json— 18/18 selected checks passed, including install/update, quota/work-lane, benchmark-ledger, CLI-budget, and public-boundary coverage; 0 failures, warnings, or manual holdspython examples/control_plane/heartbeat-prompt-smoke.pypython examples/project/project-prompt-smoke.pygit diff --check