fix(turn-driver): say what the managed runtime verdict is a claim about - #4623
huangruiteng wants to merge 4 commits into
Conversation
`managed_executor_binding` answers `dsh_runtime_unavailable` from
`importlib.util.find_spec("deepseek_harness")` in whichever interpreter probed,
and the readback never said so. One machine can therefore hold a service
environment where the SDK resolves and a checkout environment where it does not,
and the same machine reports `available: true` from one and `available: false`
from the other with the same typed reason and the same remediation list. An
operator reading the refusal cannot tell "this machine needs the dsh runtime"
from "this process needs it", which is exactly the case that made a healthy
steward look broken:
manager.channel_binding (service, ~/code-reading/loopx/.venv, 3.11.15)
-> available: true, executor dsh, deepseek-v4-flash@high
managed_executor_binding("dsh") (canonical checkout .venv, 3.13)
-> available: false, dsh_runtime_unavailable,
remediation [configure_dsh_runtime, select_individual_host]
The binding and the channel binding that quotes it now carry
`runtime_probe`: the module probed (`deepseek_harness`), the scope of the claim
(`probing_interpreter`), and the same availability the verdict used, so a surface
can state that the answer describes the probing environment rather than the
machine. `unavailable_reason` values, the remediation codes, and the fail-closed
behaviour are unchanged.
The probing interpreter is deliberately not reported. This readback is carried
into the Turn execution payload, and the dsh adapter's boundary forbids
publishing a local absolute path into LoopX state, so the docs point an operator
at `loopx doctor`'s `python.executable` instead. `runtime_probe` is `None` for
non-managed executors so no reader branches on the field's absence.
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)
详细中文评审 — PR #4623 fix(turn-driver): say what the managed runtime verdict is a claim about
评审对象为精确 head 2d17714b0a6dfce421a34b91d2b30ffb7c4a99a7(1 个 commit,5 个文件,+103/−0)。基线 origin/main 3ca868193。
动机
本 lane 的目标是管家/managed 模式可用,而「配置正确却被读成坏了」直接破坏这个目标。managed_executor_binding 的 dsh_runtime_unavailable 是在探测它的那个解释器里用 importlib.util.find_spec("deepseek_harness") 得出的,但读回里从不说明这一点。于是一台机器可以同时存在「服务环境能解析、checkout 环境不能解析」两种事实,同一个 executor 得到同一个 typed reason、同一份 remediation,却对应两种完全不同的处境。本机 2026-09-17 实测:
manager.channel_binding(服务:~/code-reading/loopx/.venv,3.11.15)
-> available: true,executor dsh,deepseek-v4-flash@high,credential source service_environment
managed_executor_binding("dsh")(canonical checkout .venv,3.13)
-> available: false,dsh_runtime_unavailable,
remediation [configure_dsh_runtime, select_individual_host]
服务是健康的、管家在跑;第二条对「发问的那个进程」是真的,但它会让 operator(或摘要这份读回的 agent)去重新准备一台机器本来就有的 runtime。这正是本 lane 反复出现的同一类问题:某个 scope 内正确的结论,不能被读成另一个 scope 的事实。
改前/改后:改前两条读回都不含 claim 的 scope;改后同样的 reason 与 remediation 不变,另加 runtime_probe = {schema_version: managed_runtime_probe_v0, module: deepseek_harness, scope: probing_interpreter, available: false},且 channel binding 引用同一份。受益者是读 loopx turn --format json 或前端 capabilities 的 operator,以及需要判断该改机器还是该改进程的 agent。
改动思路
进入点有两个,都读同一份 binding:loopx turn 与前端消费的 steward capabilities。权威输入是解析环境的环境变量 + dsh_runtime_importable(module_probe);这个答案只对跑它的解释器权威。决策归属不变:managed_executor_binding 拥有 verdict 与 reason,managed_runtime_probe 只声明这份 verdict 的 scope,manager_channel_binding 只引用而不另造第二个 verdict。
复用而非重造:module_probe seam 已存在,探测逻辑一行未改;reason/remediation 的闭集一字未动;「哪个解释器」这件事由既有的 doctor 的 python.executable 回答,因此文档指向 doctor,而不是在 payload 里再造一条身份。
关键取舍(本轮真实收敛):第一稿把探测解释器的绝对路径写进 payload。但 docs/integrations/deepseek-harness-connector.md 的 adapter 边界明确禁止把 local absolute paths 发布进 LoopX state,而这份读回会随 Turn execution payload 进入 LoopX state。于是收敛为 module + scope + available 的 path-free 形态,并加测试阻止路径回归——这是「先满足边界,再加字段」的顺序。
具体改动
关键代码讲解
loopx/control_plane/turn_driver/host_binding.py::managed_runtime_probe(第 143 行)——新增。纯构造器,输出{schema_version, module, scope, available};docstring 明确写出「不报解释器路径」这条不变量(因为该读回会进入 Turn execution payload),以及 operator 该用doctor比较环境。loopx/control_plane/turn_driver/host_binding.py::managed_executor_binding(第 246 行)——managed 分支加runtime_probe;非 managed 分支加runtime_probe: None,让字段永远存在,读方无需按存在性分支。run/refuse 判定完全未变:仍是DSH_RUNTIME_UNAVAILABLE→OPERATOR_CREDENTIAL_UNCONFIGURED→ profile reason 的顺序。loopx/chat_manager.py::manager_channel_binding(第 533 行)——把同一份runtime_probe引用进 channel binding(仅 managed endpoint),docstring 说明「显示dsh_runtime_unavailable却没有 scope 时,operator 无法判断这台机器上哪个解释器缺 runtime」。- 文档
docs/integrations/deepseek-harness-connector.md——新增一段说明 verdict 的 scope、被探测的 module、为什么同一台机器会给出两种答案,以及先用loopx doctor看python.executable再决定是否准备 runtime。 - 测试——
tests/test_turn_managed_executor_binding.py的精确形状断言同步新增字段(不是放宽),并新增test_the_runtime_verdict_states_what_it_is_a_claim_about:probe 跟随同一个 seam、scope/module 钉住、individual host 为None、且序列化结果既不含/也不含凭据值;tests/test_manager_channel_binding.py断言 channel 传播该 scope。
对主干的风险
最强回归场景:consumer 按 key 集分支、或 Turn execution payload 的形状契约被打破。触发态是任何 managed executor 解析;允许/阻断它的路径是「字段可加且 managed 恒存在、其他为 None」+「base/head 的 operator 可见输出逐字节比较」。
爆炸半径限于两份读回与序列化它们的地方;没有任何判定逻辑改变,因此 launch、refusal、settlement 行为都不受影响。可观测性就是字段本身。回退即删掉一个嵌套对象,且它读时派生、无持久化依赖。
反例覆盖:既有测试只断言 available/unavailable_reason/remediation——即使 scope 整段缺失、或悄悄加了路径,它们照样通过;本 PR 新增的测试正是针对这两种情形的反例,而同文件里的精确形状断言会在字段被删时失败。真实边界与 mock 限制:测试通过产品真正使用的 module_probe seam 注入 runtime 是否存在,而「两种 scope 不一致」是在运行中的服务上实测得到,不是模拟。
相关契约变化:unavailable_reason 取值、remediation 码、fail-closed 路径均未改;channel binding 只多一个被引用的 key。
验证矩阵(本 head 实跑):pytest tests/test_turn_managed_executor_binding.py tests/test_manager_channel_binding.py -q = 51 passed(连同 chat manager 套件 86 passed);loopx canary premerge --from-git-diff = 14 checks / 0 failures(5 catalog canary + 8 risk-profile smoke + 边界扫描);cli-output-budget-regression-smoke(base origin/main)ok,即 operator 可见输出未增长;两个 turn smoke exit 0;ruff(CI 同款 scope)All checks passed。CI 在本 head 尚 pending,按既有规则未等待。
我的整体评价
结论:批准(APPROVE),无阻断性问题。 这是一个「把结论的适用范围说出来」的小而必要的修复:它没有改任何判定、没有放宽任何门禁,却消除了一类会让人误判机器状态的读回。比例合适(+103 行里近一半是测试),并且在本轮内完成了一次真实的形态收敛——因为 LoopX state 的边界禁止 local path,第一稿的解释器路径被替换为 path-free 的 scope 声明并加了回归测试。文档在同一 PR 内交付,前端渲染留作后续 UI 切片而不是塞进契约变更。
保留一条非阻断意见(P3):字段说明了 scope,但没说「是哪个解释器」,operator 仍需 loopx doctor 或读服务启动文件才能确定;若将来前端要直接展示身份,应加public-safe identity(解释器路径的稳定摘要,而不是路径),现有 path-free 断言会挡住路径方案。
残余风险:没有真的用新读回启动一次 managed Turn,前端也尚未消费该字段;本 PR 与 #4620 都会编辑 loopx/chat_manager.py 的同一函数(用途不同、hunk 不相邻),后落地的一方最多需要一次轻微 rebase。
English verdict: APPROVE - exact head 2d17714; the managed-executor and steward-channel readbacks now carry a typed runtime_probe (module, scope: probing_interpreter, availability) so a process-level dsh_runtime_unavailable cannot be read as a machine-level fact, with the verdict, reason vocabulary, remediation codes and fail-closed behavior unchanged and the probing interpreter deliberately not reported because this payload enters LoopX state; validated by 51 focused tests (86 with the chat manager suites), a 14-check premerge with 0 failures, an unchanged CLI output budget and two turn smokes; one P3 note recorded.
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Reviewed exact head: bf864161c33ae01d431bd5305cc0a0095b2574f8 (re-review after merging origin/main; clean merge).
loopx pr-review --check-result on the matching packet returned ok: true with no approval_blockers; all 19 evidence rows for this control-plane plan are verified.
动机
托管执行器的读回只报 available: False + unavailable_reason: dsh_runtime_unavailable,但不说这是谁探测出来的。而这台机器上两个解释器会给出相反答案:checkout 的 venv 没装 SDK、服务用的 venv 装了。运维看到这条结论只能去重装运行时——在已经装好的机器上做重复修复。
这个 PR 的价值:让结论带上范围,运维能区分「这台机器没装」与「这个解释器没装」。
改动思路
关键判断是只增范围、不改判定,而且不报路径。最直觉的做法是把 sys.executable 写进读回,但 dsh 适配器的边界禁止把本地绝对路径发布进 LoopX 状态——那既是隐私面也是可移植性问题。所以改用一个 typed 的 scope 字段声明「该结论属于探测它的解释器」,解释器标识的比较交给本地面的 doctor(python.executable)。
具体改动
loopx/control_plane/turn_driver/host_binding.py(+37):MANAGED_RUNTIME_PROBE_SCHEMA_VERSION、RUNTIME_PROBE_SCOPE_INTERPRETER、managed_runtime_probe()(四个字段),并接进managed_executor_binding。loopx/chat_manager.py(+8):通道读回引用该子结构(只引用、不重算;非托管执行器保持None)。docs/integrations/deepseek-harness-connector.md(+10):说明如何用 doctor 比较环境。tests/test_turn_managed_executor_binding.py(+41)、tests/test_manager_channel_binding.py(+7)。
关键代码讲解
return {
"schema_version": MANAGED_RUNTIME_PROBE_SCHEMA_VERSION,
"module": DSH_RUNTIME_MODULE,
"scope": RUNTIME_PROBE_SCOPE_INTERPRETER,
"available": bool(runtime_available),
}四个字段就是全部:没有路径。scope=probing_interpreter 是这条结论的边界声明——它说的是「探测它的那个解释器」,不是这台机器。docstring 同时写明了为什么不报路径(会被携带进 Turn 执行载荷,而 dsh 适配器边界禁止发布本地绝对路径),避免后来者「顺手」把它加回来。
对主干的风险
纯追加字段,available / unavailable_reason / unavailable_remediation 的取值与语义一字未改(有既有缺失运行时用例锁定)。风险面是两个方向:写入本地路径(被字段集合断言拦住——读回恰为四个字段)与把 scope 误读成机器级结论(只能靠命名与文档缓解,已在描述与 docstring 明示)。执行器选择、调度、quota 均未触碰。
在更新后的 head 上实测:
env -u PYTHONPATH uv run --extra test python -m pytest tests/test_manager_channel_binding.py tests/test_turn_managed_executor_binding.py tests/test_chat_manager_context.py -q→ 70 passedenv -u PYTHONPATH uv run --extra test loopx canary premerge --from-git-diff→ 0 failures / 0 advisories(catalog canaries 5/5)- 宿主机状态对照:本 head 上
examples/operator-provider-credential-smoke.py与examples/loopx-managed-turn-operator-flow-smoke.py仍失败,我特意在干净 main worktree(/tmp/main-green.CEZZNu@9060ddc91)上跑了这两条——以完全相同的方式失败(都是「这台机器没装 dsh 运行时」),本 head 只是多出runtime_probe字段。所以这两条红与本 PR 无关,修复由 #4620 承载(它让 smoke 声明自己所断言的宿主事实)。
未验证 / 如实标注:本机没有安装 dsh 运行时,因此「已安装」分支只由测试桩覆盖,未在真实装有 SDK 的解释器或真实双 venv 布局上做端到端对照;读回仍不提供解释器标识,比较环境需要跨到 doctor。
边界声明:本 PR 改动 loopx/**,按仓库规则属控制面改动,只提 PR、由维护者合并,作者不做自合并。
我的整体评价
用声明范围而不是泄漏路径来修「结论没有适用范围」的问题,方向与边界都对,且完全不改判定。无阻断性发现。
一个值得维护者留意的组合关系:#4620 让那两条 smoke 不再读宿主机状态,#4623 让运行时结论带上范围——两者合起来才让「托管运行时不可用」这条读回既可信又可归因。
English verdict: APPROVE - re-verified on exact head bf86416: the managed runtime verdict now says what it is a claim about. Instead of reporting dsh_runtime_unavailable with no scope, the executor and channel bindings carry runtime_probe (module, scope=probing_interpreter, available), so an operator can tell a machine-level gap from a per-interpreter one without LoopX publishing a local absolute path - which the dsh adapter boundary forbids. available, unavailable_reason and the remediation values are unchanged, and the probe readback is asserted to hold exactly those four fields. 70 tests pass and premerge canary reports 0 failures; the two public operator smokes that still fail on this head fail identically on a clean main worktree, so they are the host-state condition #4620 repairs rather than a regression here. No blocking finding.
The managed-executor readback now says which environment answered its launchability verdict (managed_runtime_probe_v0), but nothing read that field through the public CLI: the smoke asserted the verdict and never the scope it is a claim about, so a payload that dropped or reshaped the field would still pass. Assert the probe where an operator actually sees it -- the plan readback, the fail-closed refusal for an unlaunchable runtime, and the explicit ``None`` an individual host still carries -- and keep the public-safety property pinned at the same boundary (no absolute path, no credential value in the serialized field). A/B: with the scope dropped, the field absent, or an interpreter path leaking into it, the smoke fails; unchanged, it passes. 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: 6502ab5024ce844764c348c426e490b645b0b4cc (re-review after adding the CLI-boundary smoke pin; previous review was bf864161c).
loopx pr-review --check-result on the matching packet returned ok: true with no approval_blockers; all 19 evidence rows for this control-plane plan are verified.
动机
托管执行器的读回只报 available: False + unavailable_reason: dsh_runtime_unavailable,但不说这是谁探测出来的。而这台机器上两个解释器会给出相反答案:checkout 的 venv 没装 SDK、服务用的 venv 装了。运维看到这条结论只能去重装运行时——在已经装好的机器上做重复修复。
这个 PR 的价值:让结论带上范围,运维能区分「这台机器没装」与「这个解释器没装」。
改动思路
关键判断是只增范围、不改判定,而且不报路径。最直觉的做法是把 sys.executable 写进读回,但 dsh 适配器的边界禁止把本地绝对路径发布进 LoopX 状态——那既是隐私面也是可移植性问题。所以改用一个 typed 的 scope 字段声明「该结论属于探测它的解释器」,解释器标识的比较交给本地面的 doctor(python.executable)。
本次 head 增量补的是消费者:这个字段现在必须真的出现在操作员看到的公开 CLI 载荷上(turn plan 的读回、turn run-once 失败关闭的拒绝载荷、以及非托管执行器仍保留的显式 None)。既有的 smoke 只断言了裁决与 executor_kind 的一致性,读回即使被丢弃或改写也照样通过——字段加了但没人读,等于没加。
具体改动
loopx/control_plane/turn_driver/host_binding.py(+37):MANAGED_RUNTIME_PROBE_SCHEMA_VERSION、RUNTIME_PROBE_SCOPE_INTERPRETER、managed_runtime_probe()(四个字段),并接进managed_executor_binding。loopx/chat_manager.py(+8):通道读回引用该子结构(只引用、不重算;非托管执行器保持None)。docs/integrations/deepseek-harness-connector.md(+10):说明如何用 doctor 比较环境。tests/test_turn_managed_executor_binding.py(+41)、tests/test_manager_channel_binding.py(+7)。examples/loopx-turn-managed-executor-binding-smoke.py(+49/-9):在公开 CLI 边界钉住该读回;9 行删除是把重复的 fixture 凭据字面量收敛为CREDENTIAL_VALUE。
关键代码讲解
return {
"schema_version": MANAGED_RUNTIME_PROBE_SCHEMA_VERSION,
"module": DSH_RUNTIME_MODULE,
"scope": RUNTIME_PROBE_SCOPE_INTERPRETER,
"available": bool(runtime_available),
}四个字段就是全部:没有路径。scope=probing_interpreter 是这条结论的边界声明——它说的是「探测它的那个解释器」,不是这台机器。docstring 同时写明了为什么不报路径(会被携带进 Turn 执行载荷,而 dsh 适配器边界禁止发布本地绝对路径),避免后来者「顺手」把它加回来。
def _expect_probe(binding: dict[str, Any], *, available: bool) -> None:
probe = binding["runtime_probe"]
assert probe["schema_version"] == MANAGED_RUNTIME_PROBE_SCHEMA_VERSION, probe
assert probe["module"] == DSH_RUNTIME_MODULE, probe
assert probe["scope"] == RUNTIME_PROBE_SCOPE_INTERPRETER, probe
assert probe["available"] is available, probe
serialized = json.dumps(probe)
assert "/" not in serialized, serialized
assert CREDENTIAL_VALUE not in serialized, serialized同一个断言被用在三处:计划读回(available=True)、失败关闭的拒绝载荷(available 取当时的真实探测结果)、以及非托管执行器的 runtime_probe is None。拒绝载荷与被拒绝的计划引用的是同一个绑定(managed_executor_payload_entry),所以「拒绝时告诉运维去装运行时」的那条消息,现在会同时说明是哪个环境没能 import 它。
语义与 CI 对齐
本 PR 不是词表扩张,而是给既有判定加范围声明:candidate_decision=extend_vocabulary,复用的仍是既有 dsh_runtime_unavailable 与 remediation 词表。CI 方面:控制面改动只提 PR、由维护者合并;本次增量是示例/smoke,若维护者希望缩小增量,单独的 smoke 提交(6502ab502)可以独立摘除,控制面部分不受影响。
对主干的风险
纯追加字段,available / unavailable_reason / unavailable_remediation 的取值与语义一字未改(有既有缺失运行时用例锁定)。风险面是两个方向:写入本地路径(被字段集合断言与 smoke 的 "/" not in serialized 拦住)与把 scope 误读成机器级结论(只能靠命名与文档缓解,已在描述与 docstring 明示)。执行器选择、调度、quota 均未触碰。
在当前 head 上实测:
env -u PYTHONPATH uv run --extra test python -m pytest tests/test_turn_managed_executor_binding.py tests/test_manager_channel_binding.py tests/test_loopx_turn_executor.py -q→ 108 passedenv -u PYTHONPATH uv run --extra test python examples/loopx-turn-managed-executor-binding-smoke.py→ passed- 变异对照(证明新断言承重):把
managed_runtime_probe分别改成「缺scope」「带sys.executable绝对路径」「返回None」,smoke 分别以KeyError: 'scope'、路径断言失败、TypeError失败;恢复原实现后通过。 env -u PYTHONPATH uv run --extra test loopx canary premerge --from-git-diff→ 0 failures / 0 advisories(catalog canaries 5/5、risk smokes 8/8、public boundary 1/1)- 宿主机状态对照:本 head 上
examples/operator-provider-credential-smoke.py与examples/loopx-turn-managed-operator-flow-smoke.py仍失败,我特意在干净 main worktree(/tmp/main-green.CEZZNu@9060ddc91)上跑了这两条——以完全相同的方式失败(都是「这台机器没装 dsh 运行时」),本 head 只是多出runtime_probe字段。所以这两条红与本 PR 无关,修复由 #4620 承载。
未验证 / 如实标注:本机没有安装 dsh 运行时,「已安装」分支只由测试桩覆盖,未在真实装有 SDK 的解释器或真实双 venv 布局上做端到端对照;examples/loopx-turn-managed-default-flow-smoke.py 在本机因此直接跳过(本 PR 未改该文件,也未在其中加断言,避免交付无法本地验证的覆盖);读回仍不提供解释器标识,比较环境需要跨到 doctor。
边界声明:本 PR 改动 loopx/**,按仓库规则属控制面改动,只提 PR、由维护者合并,作者不做自合并。
我的整体评价
用声明范围而不是泄漏路径来修「结论没有适用范围」的问题,方向与边界都对,且完全不改判定;本次增量把该读回钉在操作员真正看到的 CLI 载荷上,并用变异对照证明断言不是空转。无阻断性发现。
一个值得维护者留意的组合关系:#4620 让那两条 smoke 不再读宿主机状态,#4623 让运行时结论带上范围——两者合起来才让「托管运行时不可用」这条读回既可信又可归因。字段之后的消费者(前端 / 管家回答)应在 #4623 合入后按已合并字段补,不叠链式 PR。
English verdict: APPROVE - re-verified on exact head 6502ab5: the managed runtime verdict now says what it is a claim about, and that claim is now pinned at the boundary an operator actually reads. The executor and channel bindings carry runtime_probe (module, scope=probing_interpreter, available) instead of a bare dsh_runtime_unavailable, so an operator can tell a machine-level gap from a per-interpreter one without LoopX publishing a local absolute path - which the dsh adapter boundary forbids. available, unavailable_reason and the remediation values are unchanged; the new smoke asserts the probe on the plan readback, on the fail-closed run-once refusal, and as an explicit None for individual hosts, and fails when the scope is dropped, an interpreter path leaks in, or the field disappears (mutation-checked). 108 tests pass and premerge canary reports 0 failures; the two public operator smokes that still fail on this head fail identically on a clean main worktree, so they are the host-state condition #4620 repairs rather than a regression here. No blocking finding.
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Reviewed exact head: d8ba3c0c113cd8d481150bd0d7106fe273bc89db (re-review after merging origin/main through e160b1bbc / #4633; this head only adds that merge, and all six changed files are byte-identical to the bf864161c and 6502ab502 heads already reviewed).
loopx pr-review --check-result on the matching packet returned ok: true with no approval_blockers; all 19 evidence rows for this control-plane plan are verified.
动机
托管执行器的读回只报 available: False + unavailable_reason: dsh_runtime_unavailable,但不说这是谁探测出来的。而这台机器上两个解释器会给出相反答案:checkout 的 venv 没装 SDK、服务用的 venv 装了。运维看到这条结论只能去重装运行时——在已经装好的机器上做重复修复。
这个 PR 的价值:让结论带上范围,运维能区分「这台机器没装」与「这个解释器没装」。
改动思路
关键判断是只增范围、不改判定,而且不报路径。最直觉的做法是把 sys.executable 写进读回,但 dsh 适配器的边界禁止把本地绝对路径发布进 LoopX 状态——那既是隐私面也是可移植性问题。所以改用一个 typed 的 scope 字段声明「该结论属于探测它的解释器」,解释器标识的比较交给本地面的 doctor(python.executable)。
本次 head 增量补的是消费者:这个字段现在必须真的出现在操作员看到的公开 CLI 载荷上(turn plan 的读回、turn run-once 失败关闭的拒绝载荷、以及非托管执行器仍保留的显式 None)。既有的 smoke 只断言了裁决与 executor_kind 的一致性,读回即使被丢弃或改写也照样通过——字段加了但没人读,等于没加。
具体改动
loopx/control_plane/turn_driver/host_binding.py(+37):MANAGED_RUNTIME_PROBE_SCHEMA_VERSION、RUNTIME_PROBE_SCOPE_INTERPRETER、managed_runtime_probe()(四个字段),并接进managed_executor_binding。loopx/chat_manager.py(+8):通道读回引用该子结构(只引用、不重算;非托管执行器保持None)。docs/integrations/deepseek-harness-connector.md(+10):说明如何用 doctor 比较环境。tests/test_turn_managed_executor_binding.py(+41)、tests/test_manager_channel_binding.py(+7)。examples/loopx-turn-managed-executor-binding-smoke.py(+49/-9):在公开 CLI 边界钉住该读回;9 行删除是把重复的 fixture 凭据字面量收敛为CREDENTIAL_VALUE。
关键代码讲解
return {
"schema_version": MANAGED_RUNTIME_PROBE_SCHEMA_VERSION,
"module": DSH_RUNTIME_MODULE,
"scope": RUNTIME_PROBE_SCOPE_INTERPRETER,
"available": bool(runtime_available),
}四个字段就是全部:没有路径。scope=probing_interpreter 是这条结论的边界声明——它说的是「探测它的那个解释器」,不是这台机器。docstring 同时写明了为什么不报路径(会被携带进 Turn 执行载荷,而 dsh 适配器边界禁止发布本地绝对路径),避免后来者「顺手」把它加回来。
def _expect_probe(binding: dict[str, Any], *, available: bool) -> None:
probe = binding["runtime_probe"]
assert probe["schema_version"] == MANAGED_RUNTIME_PROBE_SCHEMA_VERSION, probe
assert probe["module"] == DSH_RUNTIME_MODULE, probe
assert probe["scope"] == RUNTIME_PROBE_SCOPE_INTERPRETER, probe
assert probe["available"] is available, probe
serialized = json.dumps(probe)
assert "/" not in serialized, serialized
assert CREDENTIAL_VALUE not in serialized, serialized同一个断言被用在三处:计划读回(available=True)、失败关闭的拒绝载荷(available 取当时的真实探测结果)、以及非托管执行器的 runtime_probe is None。拒绝载荷与被拒绝的计划引用的是同一个绑定(managed_executor_payload_entry),所以「拒绝时告诉运维去装运行时」的那条消息,现在会同时说明是哪个环境没能 import 它。
语义与 CI 对齐
本 PR 不是词表扩张,而是给既有判定加范围声明:candidate_decision=extend_vocabulary,复用的仍是既有 dsh_runtime_unavailable 与 remediation 词表。CI 方面:控制面改动只提 PR、由维护者合并;本次增量是示例/smoke,若维护者希望缩小增量,单独的 smoke 提交(6502ab502)可以独立摘除,控制面部分不受影响。
对主干的风险
纯追加字段,available / unavailable_reason / unavailable_remediation 的取值与语义一字未改(有既有缺失运行时用例锁定)。风险面是两个方向:写入本地路径(被字段集合断言与 smoke 的 "/" not in serialized 拦住)与把 scope 误读成机器级结论(只能靠命名与文档缓解,已在描述与 docstring 明示)。执行器选择、调度、quota 均未触碰。
在当前 head 上实测:
env -u PYTHONPATH uv run --extra test python -m pytest tests/test_turn_managed_executor_binding.py tests/test_manager_channel_binding.py tests/test_loopx_turn_executor.py -q→ 108 passedenv -u PYTHONPATH uv run --extra test python examples/loopx-turn-managed-executor-binding-smoke.py→ passed- 变异对照(证明新断言承重):把
managed_runtime_probe分别改成「缺scope」「带sys.executable绝对路径」「返回None」,smoke 分别以KeyError: 'scope'、路径断言失败、TypeError失败;恢复原实现后通过。 env -u PYTHONPATH uv run --extra test loopx canary premerge --from-git-diff→ 0 failures / 0 advisories(catalog canaries 5/5、risk smokes 8/8、public boundary 1/1)- 宿主机状态对照:本 head 上
examples/operator-provider-credential-smoke.py与examples/loopx-turn-managed-operator-flow-smoke.py仍失败,我特意在干净 main worktree(/tmp/main-green.CEZZNu@9060ddc91)上跑了这两条——以完全相同的方式失败(都是「这台机器没装 dsh 运行时」),本 head 只是多出runtime_probe字段。所以这两条红与本 PR 无关,修复由 #4620 承载。
未验证 / 如实标注:本机没有安装 dsh 运行时,「已安装」分支只由测试桩覆盖,未在真实装有 SDK 的解释器或真实双 venv 布局上做端到端对照;examples/loopx-turn-managed-default-flow-smoke.py 在本机因此直接跳过(本 PR 未改该文件,也未在其中加断言,避免交付无法本地验证的覆盖);读回仍不提供解释器标识,比较环境需要跨到 doctor。
边界声明:本 PR 改动 loopx/**,按仓库规则属控制面改动,只提 PR、由维护者合并,作者不做自合并。
我的整体评价
用声明范围而不是泄漏路径来修「结论没有适用范围」的问题,方向与边界都对,且完全不改判定;本次增量把该读回钉在操作员真正看到的 CLI 载荷上,并用变异对照证明断言不是空转。无阻断性发现。
一个值得维护者留意的组合关系:#4620 让那两条 smoke 不再读宿主机状态,#4623 让运行时结论带上范围——两者合起来才让「托管运行时不可用」这条读回既可信又可归因。字段之后的消费者(前端 / 管家回答)应在 #4623 合入后按已合并字段补,不叠链式 PR。
English verdict: APPROVE - re-verified on exact head d8ba3c0: the managed runtime verdict now says what it is a claim about, and that claim is now pinned at the boundary an operator actually reads. The executor and channel bindings carry runtime_probe (module, scope=probing_interpreter, available) instead of a bare dsh_runtime_unavailable, so an operator can tell a machine-level gap from a per-interpreter one without LoopX publishing a local absolute path - which the dsh adapter boundary forbids. available, unavailable_reason and the remediation values are unchanged; the new smoke asserts the probe on the plan readback, on the fail-closed run-once refusal, and as an explicit None for individual hosts, and fails when the scope is dropped, an interpreter path leaks in, or the field disappears (mutation-checked). 108 tests pass on this head and premerge canary reports 0 failures; the two public operator smokes that still fail on this head fail identically on a clean main worktree, so they are the host-state condition #4620 repairs rather than a regression here. No blocking finding.
|
Superseded by #4647. The replacement distinguishes interpreter import from injected-runner availability, preventing the latter from falsely claiming an SDK probe. It also repairs the actual Chat refusal message and incorporates the manager probe seam from #4620. Existing host/credential verdicts were compared across 24 baseline/candidate combinations, with real isolated SDK-present and SDK-absent environments. |
Superseded by #4647. The replacement distinguishes interpreter import from injected-runner availability, preventing the latter from falsely claiming an SDK probe. It also repairs the actual Chat refusal message and incorporates the manager probe seam from #4620. Existing host/credential verdicts were compared across 24 baseline/candidate combinations, with real isolated SDK-present and SDK-absent environments.