test(control-plane): let the blocked-priority smoke read the shipped notice - #4622
Conversation
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
详细中文评审 — PR #4622 test(control-plane): let the blocked-priority smoke read the shipped notice
评审对象为精确 head a7fdfd2847de03f43d213255785eb06430ba91b5(1 个 commit,1 个文件,+16/−6)。基线 origin/main a921f01c7。这是测试专用改动(examples/),不改动任何产品行为。
动机
main 的 public smoke 套件是 checks / pytest / merge-gate 的依赖,因此它每多红一条,所有 open PR 都要多背一次失败。examples/control_plane/todo-first-open-summary-smoke.py 自「blocked priority 通知」上线后一直红在 assert fallback["notify_user"] is False:
AssertionError: {'notify_user': True, 'requires_user_action': False,
'reason': 'a higher-priority agent todo is blocked before the selected
executable fallback; the fallback continues and no owner
action is required'}
关键判断:这是产品有意为之的新默认,不是回归。「通知业主」与「要求业主行动」是两个不同决定,这次只改了前者——被阻塞(或 resume 条件未满足)的高优 Agent Todo 现在 notify_user: True 而 requires_user_action 保持 False,于是 heartbeat 提升为 NOTIFY、业主知道高优工作为何不动,却不被要求做事;而只是排在未来 monitor 窗口的 Todo 仍然是静默 deferral。旧 smoke 固化的是上一个默认,正是仓库规范覆盖的情形(应重命名那个固化旧默认的 smoke)。
改前/改后:改前该 smoke 退出非零并打印上述断言;改后输出 todo-first-open-summary-smoke ok 且退出 0。受益者是所有被这条 required check 卡住的贡献者,以及任何高优 Todo 阻塞但仍继续 fallback 的 goal 的业主。
改动思路
进入点:CI 以进程方式调用该 smoke;它守护的行为在运维侧对应 loopx quota should-run、heartbeat 建议、交互式 user channel 与 markdown 读回。
权威输入是 smoke 构造的合成 status payload(高优项 blocked + open fallback),决策归 build_quota_should_run,notify_user 由 should_run_prepare._blocked_priority_fallback 拥有,heartbeat 映射由 heartbeat_recommendation 拥有,渲染由 quota_markdown 拥有。本次改动只动测试,不触碰任何拥有者。
复用而非重造:未来 monitor 窗口的静默分支已由 tests/control_plane/test_blocked_priority_fallback_notice.py 的四个用例固化(含 test_scheduled_future_monitor_stays_a_silent_deferral),因此这里不重复实现;本 smoke 只补上投影链路那一层——单元测试只看 fallback 对象,看不到 heartbeat、interaction contract 与 markdown 三处一致性。
具体改动
测试专用:assert_blocked_priority_fallback_visible 重命名为 assert_blocked_priority_fallback_notice_visible(让旧默认无法再从测试名里被读出来),并同时断言这条区分的两侧:
notify_user is True、requires_user_action is False;heartbeat_recommendation.notify == "NOTIFY"、interaction_contract.user_channel.notify == "NOTIFY"且action_required is False;- user channel 的 reason 与 markdown 读回都含 "no owner action is required";
- markdown 行读作
blocked_priority_fallback: notify_user=True,且 blocked item 行与 selected fallback 行仍在; should_run is True且 fallback 仍被选中——通知永远不会变成 gate。
这样任何一个方向的回归(把通知退化成静默,或把通知升级成 gate)都会在这里失败;此前该文件只有单侧断言,两个方向都漏过。
对主干的风险
最强回归场景:产品把 blocked-priority 通知退回静默,或把它变成阻塞 fallback 的门;触发态是「高优 Agent Todo 为 blocked(或 resume 条件未满足)而 fallback 仍可执行」。允许/阻断它的代码路径是 _blocked_priority_fallback → heartbeat 映射 → markdown 渲染三处,本 smoke 现在同时断言三处与两个非门禁标志。
爆炸半径:仅测试文件。若本 PR 写错,后果是 smoke 误过或误红,不是产品行为。
反例覆盖:现有单元测试只断言 fallback 对象上的 notify_user,即使 heartbeat 映射或 markdown 渲染丢掉了通知它依然会通过;本 smoke 正是覆盖端到端投影的反例。真实边界:smoke 用合成 payload 调真实投影与渲染函数,不跑真实 agent turn——对投影契约来说这是正确的边界;未覆盖的是前端自己渲染通知的那一层。
可观测性:smoke 自身输出与断言消息(断言消息会打印整个 fallback payload)。回退:还原即恢复旧断言与红灯,无需迁移状态。
验证矩阵(本 head 实跑):改前在 pristine 文件上实测失败并打印 shipped payload,改后 todo-first-open-summary-smoke ok;loopx canary premerge --from-git-diff 5/5 catalog canary 通过、0 failures、边界扫描通过;pytest tests/control_plane/test_blocked_priority_fallback_notice.py tests/presentation/test_quota_markdown_boundary.py tests/control_plane/test_quota_settlement_cli.py -q = 70 passed / 2 failed,两个失败是 test_prior_host_closeout_survives_hidden_todo_lifecycle[0-sqlite] / [6-sqlite] 的既有 Node.js fixture 失败,在未改动的 origin/main worktree 上同样复现,与本文件无关。CI 在本 head 尚在 pending,按既有规则未等待。
我的整体评价
结论:批准(APPROVE),无阻断性问题。 这是一个小而正确的修复:它没有放宽任何 guard,而是让一条 stale guard 重新对齐已发布的产品语义,并且顺带把它从单侧断言升级为双向断言——通知既不能被悄悄丢弃,也不能变成门。文件已存在的多投影覆盖说明把这条断言放进该 smoke 比新开文件更薄,符合「compress rather than append」。
不保留阻断意见。残余风险:本 smoke 覆盖投影与两个消费者表面,不覆盖真实 agent turn 与前端渲染;test_quota_settlement_cli 的两个既有 Node fixture 失败与本改动无关。
English verdict: APPROVE - exact head a7fdfd2; the stale blocked-priority smoke now asserts the shipped notice contract (notify_user true, heartbeat and user-channel NOTIFY, no owner action required, requires_user_action/action_required still false, should_run true) instead of the pre-notice default, so it fails for either regression direction; test-only, single file, repair of an existing public guard rather than a new artifact; validated by the smoke failing on the pristine file and passing on this head, a canary premerge with 0 failures, and 70 passing tests with only the two pre-existing Node fixture failures.
…notice
`blocked_priority_fallback` now sets `notify_user: True` when a higher-priority
Agent Todo is blocked, or waits on an unsatisfied resume condition, while an
executable fallback continues: informing the owner and requiring owner action
are different decisions, and only the first one changed. The public smoke still
asserted the previous silent default, so `todo-first-open-summary-smoke` has
been red on main since that change:
assert fallback["notify_user"] is False
AssertionError: {... 'notify_user': True, 'requires_user_action': False ...}
The smoke now states the shipped contract on both sides of that distinction,
which is what makes it worth keeping: `notify_user` is True, the heartbeat
recommendation and the interactive user channel both say NOTIFY, the reason and
the markdown readback both say no owner action is required, and
`requires_user_action` / `action_required` stay False with the fallback still
selected and `should_run` still True. A future change that turns the notice into
a gate, or drops it back to silence, now fails here.
The helper is renamed to `assert_blocked_priority_fallback_notice_visible` so
the old default cannot be read back out of the test name. The scheduled
future-monitor deferral branch stays a silent deferral and is already pinned by
`tests/control_plane/test_blocked_priority_fallback_notice.py`.
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
a7fdfd2 to
6083a76
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
详细中文评审 — PR #4622 test(control-plane): let the blocked-priority smoke read the shipped notice
评审对象为精确 head 6083a7683e208979231b6512c2660c8a759fd055(1 个 commit,1 个文件,+16/−6),已 rebase 到 origin/main a921f01c7 之上。这是测试专用改动(examples/),不改动任何产品行为。
动机
main 的 public smoke 套件是 checks / pytest / merge-gate 的依赖,因此它每多红一条,所有 open PR 都要多背一次失败。examples/control_plane/todo-first-open-summary-smoke.py 自「blocked priority 通知」上线后一直红在 assert fallback["notify_user"] is False:
AssertionError: {'notify_user': True, 'requires_user_action': False,
'reason': 'a higher-priority agent todo is blocked before the selected
executable fallback; the fallback continues and no owner
action is required'}
关键判断:这是产品有意为之的新默认,不是回归。「通知业主」与「要求业主行动」是两个不同决定,这次只改了前者——被阻塞(或 resume 条件未满足)的高优 Agent Todo 现在 notify_user: True 而 requires_user_action 保持 False,于是 heartbeat 提升为 NOTIFY、业主知道高优工作为何不动,却不被要求做事;而只是排在未来 monitor 窗口的 Todo 仍然是静默 deferral。旧 smoke 固化的是上一个默认,正是仓库规范覆盖的情形(应重命名那个固化旧默认的 smoke)。
改前/改后:改前该 smoke 退出非零并打印上述断言;改后输出 todo-first-open-summary-smoke ok 且退出 0。受益者是所有被这条 required check 卡住的贡献者,以及任何高优 Todo 阻塞但仍继续 fallback 的 goal 的业主。
改动思路
进入点:CI 以进程方式调用该 smoke;它守护的行为在运维侧对应 loopx quota should-run、heartbeat 建议、交互式 user channel 与 markdown 读回。
权威输入是 smoke 构造的合成 status payload(高优项 blocked + open fallback),决策归 build_quota_should_run,notify_user 由 should_run_prepare._blocked_priority_fallback 拥有,heartbeat 映射由 heartbeat_recommendation 拥有,渲染由 quota_markdown 拥有。本次改动只动测试,不触碰任何拥有者。
复用而非重造:未来 monitor 窗口的静默分支已由 tests/control_plane/test_blocked_priority_fallback_notice.py 的四个用例固化(含 test_scheduled_future_monitor_stays_a_silent_deferral),因此这里不重复实现;本 smoke 只补上投影链路那一层——单元测试只看 fallback 对象,看不到 heartbeat、interaction contract 与 markdown 三处一致性。
具体改动
测试专用:assert_blocked_priority_fallback_visible 重命名为 assert_blocked_priority_fallback_notice_visible(让旧默认无法再从测试名里被读出来),并同时断言这条区分的两侧:
notify_user is True、requires_user_action is False;heartbeat_recommendation.notify == "NOTIFY"、interaction_contract.user_channel.notify == "NOTIFY"且action_required is False;- user channel 的 reason 与 markdown 读回都含 "no owner action is required";
- markdown 行读作
blocked_priority_fallback: notify_user=True,且 blocked item 行与 selected fallback 行仍在; should_run is True且 fallback 仍被选中——通知永远不会变成 gate。
这样任何一个方向的回归(把通知退化成静默,或把通知升级成 gate)都会在这里失败;此前该文件只有单侧断言,两个方向都漏过。
对主干的风险
最强回归场景:产品把 blocked-priority 通知退回静默,或把它变成阻塞 fallback 的门;触发态是「高优 Agent Todo 为 blocked(或 resume 条件未满足)而 fallback 仍可执行」。允许/阻断它的代码路径是 _blocked_priority_fallback → heartbeat 映射 → markdown 渲染三处,本 smoke 现在同时断言三处与两个非门禁标志。
爆炸半径:仅测试文件。若本 PR 写错,后果是 smoke 误过或误红,不是产品行为。
反例覆盖:现有单元测试只断言 fallback 对象上的 notify_user,即使 heartbeat 映射或 markdown 渲染丢掉了通知它依然会通过;本 smoke 正是覆盖端到端投影的反例。真实边界:smoke 用合成 payload 调真实投影与渲染函数,不跑真实 agent turn——对投影契约来说这是正确的边界;未覆盖的是前端自己渲染通知的那一层。
可观测性:smoke 自身输出与断言消息(断言消息会打印整个 fallback payload)。回退:还原即恢复旧断言与红灯,无需迁移状态。
验证矩阵(本 head 实跑):改前在 pristine 文件上实测失败并打印 shipped payload,改后 todo-first-open-summary-smoke ok;loopx canary premerge --from-git-diff 5/5 catalog canary 通过、0 failures、边界扫描通过;pytest tests/control_plane/test_blocked_priority_fallback_notice.py tests/presentation/test_quota_markdown_boundary.py tests/control_plane/test_quota_settlement_cli.py -q = 70 passed / 2 failed,两个失败是 test_prior_host_closeout_survives_hidden_todo_lifecycle[0-sqlite] / [6-sqlite] 的既有 Node.js fixture 失败,在未改动的 origin/main worktree 上同样复现,与本文件无关。CI 在本 head 尚在 pending,按既有规则未等待。
我的整体评价
结论:批准(APPROVE),无阻断性问题。 这是一个小而正确的修复:它没有放宽任何 guard,而是让一条 stale guard 重新对齐已发布的产品语义,并且顺带把它从单侧断言升级为双向断言——通知既不能被悄悄丢弃,也不能变成门。文件已存在的多投影覆盖说明把这条断言放进该 smoke 比新开文件更薄,符合「compress rather than append」。
不保留阻断意见。残余风险:本 smoke 覆盖投影与两个消费者表面,不覆盖真实 agent turn 与前端渲染;test_quota_settlement_cli 的两个既有 Node fixture 失败与本改动无关。
English verdict: APPROVE - exact head 6083a76; the stale blocked-priority smoke now asserts the shipped notice contract (notify_user true, heartbeat and user-channel NOTIFY, no owner action required, requires_user_action/action_required still false, should_run true) instead of the pre-notice default, so it fails for either regression direction; test-only, single file, repair of an existing public guard rather than a new artifact; validated by the smoke failing on the pristine file and passing on this head, a canary premerge with 0 failures, and 70 passing tests with only the two pre-existing Node fixture failures.
|
Merge decision (author-owned self-merge, test-only surface).
|
What this repairs
examples/control_plane/todo-first-open-summary-smoke.pyhas been red onmainsince the fallback notice shipped:The shipped behavior is deliberate, not a regression: informing the owner and requiring owner action are different decisions, and only the first one changed. A higher-priority Agent Todo that is
blocked, or that waits on an unsatisfied resume condition, now setsnotify_user: Truewhilerequires_user_actionstaysFalse, so the heartbeat refinement raises NOTIFY and the owner learns why the priority work is not moving without being asked to act. A Todo that is merely scheduled for a future monitor window stays a silent deferral.So the smoke was encoding the previous default, which is the case the repository's own guidance covers: rename the smoke that encoded the old default and update the docs/disclosure.
What changed
One file, one commit, test-only — no runtime behavior changes.
assert_blocked_priority_fallback_visiblebecomesassert_blocked_priority_fallback_notice_visible(so the old default cannot be read back out of the test name) and now asserts the shipped contract on both sides of the distinction:notify_userisTrue,requires_user_actionisFalse;heartbeat_recommendation.notifyisNOTIFYandinteraction_contract.user_channel.notifyisNOTIFYwithaction_requiredstillFalse;blocked_priority_fallback: notify_user=True, and the blocked item and selected fallback rows are still rendered;should_runis stillTruewith the fallback selected, so the notice never gates delivery.A future change that turns the notice into a gate, or drops it back to silence, now fails here. The scheduled-future-monitor deferral branch is already pinned by
tests/control_plane/test_blocked_priority_fallback_notice.py(added with the notice) and is not duplicated in the public smoke.Validation
python3 examples/control_plane/todo-first-open-summary-smoke.py— fails on pristinemainwith the assertion above, passes on this head.pytest tests/control_plane/test_blocked_priority_fallback_notice.py tests/presentation/test_quota_markdown_boundary.py tests/control_plane/test_quota_settlement_cli.py -q— 70 passed, 2 failed. Both failures are the pre-existingtest_prior_host_closeout_survives_hidden_todo_lifecycle[0-sqlite]/[6-sqlite]Node.js fixture failures that reproduce on an unchangedorigin/mainworktree, and are unrelated to this file.loopx canary premerge --from-git-diff— 5/5 catalog canaries passed, 0 failures, public-boundary scan passed.Boundary: test-only change under
examples/; it changes no runtime behavior, permission, persisted contract, or public evidence policy.Context
main's public smoke suite is red for several independent reasons and gates every open PR. #4620 restores five of them (chat stream throughput, managed turn operator flow, operator provider credential, extension entrypoint surface, cli help manpage). This PR is the sixth. #4593 owns the seventh group.One correction worth recording
While diagnosing the last red smoke I proved that
examples/auto-research-rollout-readpath-smoke.pyis not an independent cause and needs no design decision: the live frontier refuses to project whenquota.okis false, and that smoke'scollect_status(scan_roots=[Path.cwd()])scans the repository root, where the seven tracked-fixturepublic_boundary_violationhits make the contract unhealthy. On #4593's head that smoke exits 0, as dorepository-hygiene-smokeandcanary-promotion-readiness-smoke(which fails in CI because its innerloopx check --scan-path <repo>step exits 1 on the same seven hits). So after #4620, this PR and #4593, the suite's red list is empty.