docs(host-mode): define the declared host and assert the transition policy - #4462
Conversation
…olicy Three follow-ups from the review of #4456. `turn_mapping.host` (`generic-cli`) sat next to `host_resolution: resolved` and the new `host_selection: resolved_default` while `plan_command` pinned no host, so a consumer that read only `host`/`host_resolution` could still conclude that the concrete host had resolved to the compatibility adapter. The protocol doc now defines the field as the mode's *declared* host and scheduler context and states that the concrete host is resolved when `plan_command` runs; it also documents `host_selection` and `plan_command_rollback`. The RFC recorded the unattended mapping as an open decision between three options. The shipped behavior is one of them, so the paragraph now records the decision (write out the resolved default, label the pinned compatibility variant as the rollback path) instead of contradicting the code; the Chinese mirror is updated with it. The transition policy at `host_mode_planner.py` target commands had no assertion: `examples/host-mode-plan-smoke.py` only checked `preserves_agent_id`, `spends_quota` and the guard command. It now asserts that targets which resolve their host pin no `--host`, and that targets needing a visible identity still pin `--host codex-cli` with `--execution-mode interactive-visible`. This also supersedes the merged PR description, which described the affected transitions as hybrid-handoff although no transition targets that mode. Validation: `examples/host-mode-plan-smoke.py` ok, `examples/project/host-mode-plan-cli-smoke.py` ok, `pytest tests/test_host_mode_planner.py -q` 2 passed, `ruff check` clean, and `loopx canary premerge --from-git-diff --timeout-seconds 300` gate=passed with 11 executed checks and 0 failures. Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Reviewed exact head 2206618e76c73980dabec2363d8ad8d0a6d45e59. This is the follow-up batch for the three non-blocking P3 findings from the #4456 review; no runtime behavior is changed.
动机
#4456 修好了「无人值守预览把兼容路径当默认」,但那轮 review 留了三个 P3:协议文档没有定义 turn_mapping.host,RFC 仍把已决事项写成待决,transition 策略没有任何断言。前两条会让读者得出与实现相反的结论——host 取 generic-cli、旁边又是 host_resolution: resolved,而 plan_command 已经不 pin 任何 host,只看这两个字段的消费者仍会以为具体宿主已经解析成兼容适配器;第三条更直接:把策略改回 pin 死 generic-cli,现有检查不会红。成本是一条被反复误读的公共协议字段加一条随时可以静默回退的策略,而修复面只有一处文档定义、两段 RFC(英文+中文镜像)和既有 smoke 里的一段断言,不拆成三个 PR 反而更省。
改动思路
入口仍是 build_host_mode_plan,权威状态是 loopx/host_mode_planner.py 的模式目录与 RESOLVED_DEFAULT_TURN_HOST_MODES:谁决定 pin 不 pin 已经有唯一 owner,所以这次不去动实现,而是把该 owner 的语义补到消费者读的那份协议里,并把同一条规则变成可执行断言。具体是:在 docs/reference/protocols/host-mode-plan-v0.md 的 shape 示例后给出 host / host_selection / plan_command / plan_command_rollback 四个字段的定义——host 是该模式的声明宿主与调度上下文,具体宿主在 plan_command 运行时才解析;RFC 段落改成记录「已决并已落地」并指向协议文档;smoke 在既有 transitions[] 循环里按 transition id 断言策略。没有新增文档面、没有新增文件、没有改 payload 字段,也就没有 v1 迁移成本。
具体改动
docs/reference/protocols/host-mode-plan-v0.md:新增字段定义列表;功能点 2(Turn mapping)补上「把具体宿主留给运行时解析默认值、pinned 变体作为回滚命令」这一句。docs/architecture/rfcs/harness-selection-dsh-pi-v0.md:原先「仍需在三选项之间决定」的段落改成记录已决:写法采用「写出解析后的默认值」,pin 死的兼容变体报为plan_command_rollback,host_selection用类型化字段区分;真正需要可见身份的路径(转入visible_tui)仍然 pin。.zh-CN.md镜像同步。examples/host-mode-plan-smoke.py:在既有 transition 循环后,断言visible_bootstrap_to_isolated_headless_turn与im_gateway_to_isolated_headless_turn的target_turn_plan_command不含--host,断言两个转入visible_tui的 transition 仍含--host codex-cli与--execution-mode interactive-visible。这同时取代了 #4456 描述里「hybrid-handoff target commands」的错误措辞——现有四条 transition 里没有目标是 hybrid-handoff 的。
对主干的风险
风险很低但要说清:这是文档 + 断言,不改运行时。唯一真正的新增耦合是「smoke 现在编码了 pinning 策略」——将来如果有意改策略,必须同时改断言与 RFC 段落;这正是我们想要的耦合,但如果有人只改实现,失败信息会直接点名 transition id 和出问题的命令,不需要猜。反事实很清楚:在这次改动之前,把 resolved-default transition 改回 pin --host generic-cli 可以通过所有现有检查,因为只有 mode option 的命令被断言。文档侧没有字段改名、没有 schema 版本变化,host 仍是声明宿主,所以 display sink 的既有读取路径不受影响。验证:examples/host-mode-plan-smoke.py ok、examples/project/host-mode-plan-cli-smoke.py ok、pytest tests/test_host_mode_planner.py -q 2 passed、ruff clean、loopx canary premerge --from-git-diff gate passed(11 项、0 失败)。本轮按 owner 指示没有等远端 CI,因此 CI 状态未作为证据。
我的整体评价
三个 P3 都落在最小修复面上:定义写在字段被使用的地方、RFC 就地改成与实现一致的现状、断言加在既有的 transition 循环里而不是新开一个 smoke,所以 60 行插入里有 17 行是可执行断言、其余是文档。冒烟价值是真实的——它守的是 shipped planner 的策略边界,且现有覆盖扫描显示这份 smoke 就是该行为的既有守卫文件;同作者这几条 PR 是同一轮 review 的连续后续(#4454/#4456/#4460/本 PR),不是同形态刷量。剩余未验证项只有远端 CI:如果 owner 希望合并前看到它绿,就等它跑完;按仓库自有的 pre-merge gate 结果,这个 head 已满足自合并条件。
English verdict: APPROVE at exact head 2206618e76c73980dabec2363d8ad8d0a6d45e59. This closes the three P3 findings from the #4456 review: the protocol doc now defines turn_mapping.host as the mode's declared host (with host_selection and plan_command_rollback), the RFC (and its Chinese mirror) records the unattended mapping as decided in favor of writing out the resolved default, and the existing host-mode smoke now asserts that resolved-default transitions pin no --host while visible transitions still pin --host codex-cli — a case that previously passed even if the policy were reverted. Documentation plus 17 lines of assertions; no runtime change. Validation: both host-mode smokes ok, pytest tests/test_host_mode_planner.py 2 passed, ruff clean, loopx canary premerge --from-git-diff gate passed with 11 checks and 0 failures. Remote CI was not consulted in this round per the owner's instruction, which is the only unverified dimension.
Summary
Three follow-ups raised as non-blocking P3 findings in the review of #4456.
P3 —
turn_mapping.hostwas undefined next to the resolved-default fieldsturn_mapping.host(generic-cli) sat next tohost_resolution: resolvedand thenew
host_selection: resolved_defaultwhileplan_commandpinned no host, so aconsumer reading only
host/host_resolutioncould still conclude the concrete hosthad resolved to the compatibility adapter.
docs/reference/protocols/host-mode-plan-v0.mdnow defines the field as the mode's declared host and scheduler context, states that
the concrete host (including a credential- or configuration-resolved default) is resolved
when
plan_commandruns, and documentshost_selectionandplan_command_rollback.Functional point 2 (Turn mapping) states the same rule.
P3 — the RFC still described a decided item as open
docs/architecture/rfcs/harness-selection-dsh-pi-v0.md(and its Chinese mirror) recordedthe unattended mapping as still needing a decision between three options. The shipped
behavior is one of them, so the paragraph now records the decision — write out the
resolved default, report the pinned compatibility variant as
plan_command_rollback—instead of contradicting the code.
P3 — the transition policy had no assertion, and the merged description was wrong
examples/host-mode-plan-smoke.pyonly checkedpreserves_agent_id,spends_quotaandthe guard command over
transitions[], so nothing would have failed iftarget_turn_plan_commandpinned a host again. It now asserts that targets which resolvetheir host pin no
--host, and that targets needing a visible identity still pin--host codex-cliwith--execution-mode interactive-visible. This also supersedes themerged PR description, which described the affected transitions as hybrid-handoff
although no transition targets that mode.
Validation
python3 examples/host-mode-plan-smoke.py→ okpython3 examples/project/host-mode-plan-cli-smoke.py→ okpytest tests/test_host_mode_planner.py -q→ 2 passedruff checkon the changed smoke → cleanloopx canary premerge --from-git-diff --timeout-seconds 300→gate.status=passed, 11 executed checks, 0 failuresDocs and one smoke assertion only; no runtime behavior change.