Skip to content

修复 Windows 兼容:控制总线、shell 引号与进程组处理 - #66

Open
xing-kj wants to merge 6 commits into
AMAP-ML:mainfrom
xing-kj:windows-compat-fixes
Open

xing-kj wants to merge 6 commits into
AMAP-ML:mainfrom
xing-kj:windows-compat-fixes

Conversation

@xing-kj

@xing-kj xing-kj commented Aug 25, 2026 •

Copy link
Copy Markdown

修复 Windows 兼容:控制总线、shell 引号与进程组处理

背景

在 Windows 11(10.0.22631)上从源码安装 v0.1.7 后,lh-harness run 无法启动:引导阶段即报
unsafe run directory layout: secure control-bus directory creation is unavailable。
逐层排查后发现,核心循环、事件账本、适配器命令模板、监督锁等多处深度依赖 POSIX 专有能力,
Windows 上均直接失败。本 PR 为这些路径增加平台感知回退,不改变 POSIX 平台的任何既有行为。

问题与修复对照

# 故障点 根因 修复
1 启动即拒 control_bus 要求 O_NOFOLLOW+dir_fd 锚定目录遍历,Windows 无此能力 新增 _SECURE_DIRFD 能力检测;回退为「路径创建 + 逐级符号链接扫描」
2 控制命令互斥失效风险 fcntl.flock Windows 不存在 _process_lock:msvcrt 字节锁实现等价跨进程互斥
3 Manager 首轮崩溃 事件追加要求 O_NOFOLLOW _append_event 增加「校验后普通追加」分支
4 提示词写入崩溃 mkdir -p/chmod 是 POSIX 命令,create_subprocess_shell 在 Windows 走 cmd.exe LocalEnvironment 原生 ensure_dir/chmod;remote_files 优先调用并兼容 \ 路径分隔
5 所有角色 episode 0.1s 失败 命令模板用单引号 + VAR=x cmd 前缀,cmd.exe 不解析 新增 utils/platform_shell.py(shell_quote/cd_command/env_prefix),4 个适配器 + cli_agent 全部接入
6 进程清理不可用 os.killpg/SIGHUP 缺失 process_group 按 PID 回退 + 信号按存在性收集;service.py 4 处 killpg 加护栏

改动范围

13 个文件,+379/−79(git diff --stat 见 commit):

supervisor/control_bus.py    | 174 +++++++++-   核心兼容层
utils/platform_shell.py      |  47 ++          新增:平台 shell 引号
manager.py                   |  23 +           事件追加回退
supervisor/service.py        |  28 ++-         监督锁 + killpg 护栏
dashboard/state.py           |  33 +-          审批日志回退
adapters/{cli_agent,opencode,codex,claude_code,deepseek_harness}.py
environment/{local,remote_files}.py
utils/process_group.py

验证

  • python -m compileall src/lh_harness 通过
  • 兼容探针 5 项全过:控制总线原子写+读回、JSONL 追加互斥、ControlBus 命令协议
    (append/commands/conflict 检测)、事件追加、全部改动模块可导入
  • 端到端:opencode 后端 + 免费网关模型,完整跑通
    Manager→Executor→Auditor 多轮闭环(Round 1 规划/执行/审计三角色全绿
    complete/clean/aligned,Round 2 判定交付并生成中文最终报告),
    Executor 实际驱动本机 Edge 浏览器完成了 arXiv/GitHub 调研任务并落盘截图证据

已知遗留(后续补丁)

Executor episode 超时清理路径仍引用 signal.SIGKILL(Windows 无此常量),
超时场景会以 worker_exception 终止而非优雅回收——不影响正常运行,将在后续 PR 修复。

🤖 Generated with opencode


更新(第二阶段 commit c26437b)

首个 commit 解决"能启动";本阶段把 Windows 推进到全量测试套件通过:

  • 修复 SIGKILL 缺失(6 处超时/停止路径崩溃)、os.kill(pid, 0) 探测在
    Windows 语义为 TerminateProcess 导致的 pid 复用误杀(新增 Job Object
    内核级进程树管理 win_job.py)、O_BINARY 缺失导致 PNG 字节被改写、
    回退分支的符号链接/硬链接防护缺口、os.scandir(fd)/目录打开不可用等
    共 7 类问题
  • 18 个测试文件平台化(killpg 桩改打 _signal_worker 接缝、#!/bin/sh
    桩翻译为 .cmd、引号断言感知平台)
  • 新增 .github/workflows/ci.yml:ubuntu + windows + macos 三平台 pytest
    矩阵(此前仓库无任何测试 CI)
  • 结果:Windows 11 + Python 3.12 全量 402 passed / 4 skipped / 0 failed

Fixes #70

上游实现深度依赖 POSIX 能力(O_NOFOLLOW/dir_fd 目录锚定、fcntl.flock、
POSIX shell 引号、killpg/SIGKILL 进程组),在 Windows 上从启动引导到
运行循环多处直接失败,导致 `lh-harness run` 无法使用。本补丁为这些
路径增加平台感知回退,不改变 POSIX 平台的既有行为:

- supervisor/control_bus.py:新增能力检测 _SECURE_DIRFD;无锚定目录
  fd 的平台回退为「路径创建 + 符号链接扫描」;fcntl 缺失时以 msvcrt
  字节锁实现等价跨进程互斥(_process_lock);_open_nofollow、
  _open_private_regular_at、_atomic_bytes_write、_append_jsonl 与
  ControlBus._locked 均增加路径分支
- utils/platform_shell.py(新增):shell_quote/cd_command/env_prefix
  按 platform 生成 cmd.exe 兼容的双引号转义、`cd /d` 与 `set "K=V"`
  前缀,替代硬编码的 POSIX 单引号语法
- adapters/cli_agent.py、opencode.py、codex.py、claude_code.py、
  deepseek_harness.py:命令模板统一改用平台感知引号与环境变量前缀
- environment/local.py:新增原生 ensure_dir/chmod 方法;
  environment/remote_files.py 优先使用原生实现,父目录推导同时兼容
  POSIX 与 Windows 路径分隔符
- utils/process_group.py:无 killpg 的平台回退为按 PID 发信号;信号
  枚举按存在性收集(Windows 无 SIGHUP)
- manager.py:事件追加在无 O_NOFOLLOW 平台走「校验后普通追加」分支
- dashboard/state.py:审批日志追加改用 _process_lock 并兼容路径模式
- supervisor/service.py:监督锁增加 Windows 回退;4 处 killpg 调用
  加平台护栏

验证环境:Windows 11 (10.0.22631) + Python 3.12
- `python -m compileall src/lh_harness` 通过
- 兼容探针 5 项全过:控制总线原子写+读回、JSONL 追加互斥、
  ControlBus 命令协议(append/commands/conflict)、事件追加、全部
  改动模块可导入
- 端到端:opencode 后端完整跑通 Manager→Executor→Auditor 多轮闭环,
  任务正常完成并产出审计报告

🤖 Generated with [opencode](https://github.com/anomalyco/opencode)
@xing-kj xing-kj changed the title [Windows] ????????????? 淇 Windows 鍏煎锛氭帶鍒舵€荤嚎銆乻hell 寮曞彿涓庤繘绋嬬粍澶勭悊 Aug 25, 2026
@xing-kj xing-kj changed the title 淇 Windows 鍏煎锛氭帶鍒舵€荤嚎銆乻hell 寮曞彿涓庤繘绋嬬粍澶勭悊 修复 Windows 兼容:控制总线、shell 引号与进程组处理 Aug 25, 2026
这是对 AMAP-ML#66 第一个 commit 的延续,把"Windows 能启动"推进到"Windows 全量
测试套件绿",并补上此前完全缺失的测试 CI。

## 源码修复

- utils/win_job.py(新增):Windows Job Object 封装。按 pid 裸杀在该平台
  有致命缺陷——子进程退出后 pid 可被复用,TerminateProcess 会误杀无关进
  程(本机实测曾打断整个测试进程树)。Job 句柄由内核保证只杀树内进程。
- utils/process_group.py:SIGKILL 平台化(Windows 无此常量,此前超时清
  理路径直接 AttributeError 崩溃);sig=0 存在性探测在 Windows 上会变成
  击杀,改用只读句柄查询(win_job.pid_alive)。
- supervisor/service.py:_signal_worker 单一信号分发接缝;_open_worker_log、
  _saved_task_from_rounds、_supervisor_locked 增加 Windows 回退(路径校验 +
  符号链接扫描,语义与 control_bus 一致);stop/abort 路径全部走接缝。
- supervisor/control_bus.py:_open_nofollow 回退补最终符号链接检查;
  _atomic_bytes_write/_append_jsonl 回退先校验祖先链再建目录(此前会穿
  透符号链接目录边界创建文件);ControlBus 锁与追加增加 nlink 别名拒绝。
- dashboard/state.py:自有 _open_nofollow 补 O_BINARY(此前文本模式把
  \r\n 读成 \n,PNG 截图字节被静默改写)与符号链接/nlink 防护;
  list_round_artifacts 增加 Windows 路径枚举分支(Windows 无法 os.open
  目录,os.scandir(fd) 亦为 POSIX 专属)。
- environment/local.py:chmod 平台化(POSIX 真执行,Windows no-op),
  修正上一 commit 中无差别 no-op 的疏漏;子进程接入 Job Object。

## 测试

- conftest.py 新增 write_executable_stub:适配器测试的 `#!/bin/sh` 桩在
  Windows 翻译为等价 .cmd(echo/printf/exit/heredoc/if-then 小语法)。
- 18 个测试文件的平台化:killpg monkeypatch 改打 _signal_worker 接缝、
  引号断言感知平台、NTFS 不支持的文件名用例显式 skip。
- 结果:Windows 11 + Python 3.12 全量 402 passed / 4 skipped / 0 failed
  (此前:启动即崩 → 28 failed + 21 errors + 进程树被误杀)。

## CI

- 新增 .github/workflows/ci.yml:ubuntu + windows + macos 三平台矩阵运行
  pytest。此前仓库仅有 release workflow,测试从未在任何 CI 上运行过——
  本系列修的全部问题都源于此盲区。

## 已知遗留

- test_model_catalog::test_deepseek_catalog_exposes_default_model_and_cli_
  availability 在未修改的上游克隆上同样失败(期望标签与 src 的乱码不一致,
  疑为上游提交时的编码问题),与本 PR 无关,建议单独处理。

🤖 Generated with [opencode](https://github.com/anomalyco/opencode)
@xing-kj

xing-kj commented Aug 25, 2026

Copy link
Copy Markdown
Author

第二阶段已推送(c26437b),本 PR 现在包含两个 commit:

  1. 85da21d — 让 Windows 能启动(控制总线、shell 引号、进程组)
  2. c26437b — 让 Windows 全量测试套件通过(402 passed / 0 failed)+ 三平台 CI

第二阶段要点:

  • 新增 win_job.py:Windows Job Object 内核级进程树管理。修复 os.kill(pid, 0)
    存在性探测在 Windows 实为 TerminateProcess 导致的 pid 复用误杀(实测会打断
    整个测试进程树)与 SIGKILL 缺失崩溃
  • dashboard/state.py 补 O_BINARY(此前文本模式把 PNG 的 \r\n 读成 \n)
  • 18 个测试文件平台化;#!/bin/sh 桩经 conftest.write_executable_stub 在
    Windows 翻译为等价 .cmd
  • 新增三平台 pytest CI(此前仓库无任何测试 CI,本系列问题皆源于此盲区)

完整说明见 PR 正文与 commit message。问题全景见 #70。

🤖 Generated with opencode

- cli.py:Windows 上强制 stdout/stderr 为 UTF-8(reconfigure)并将控制台
  输出码页切至 65001。此前 harness 的 UTF-8 产物(中文报告/事件)经 cp936
  管道或控制台输出时乱码,含 ✓ 等字符时甚至直接 UnicodeEncodeError 崩溃。
- utils/platform_caps.py(新增):SECURE_DIRFD 能量检测与
  validate_no_symlink_chain 的唯一权威定义,从 control_bus 上移;control_bus
  保留私有别名,全部既有引用(manager/cli/tests)零改动。
- utils/platform_shell.py:IS_WINDOWS 改为引用 platform_caps,消除重复定义。

测试:全量 402 passed / 4 skipped 保持不变。

🤖 Generated with [opencode](https://github.com/anomalyco/opencode)
此前 computer-use 插件只为 claude_code 与 codex 生成配置,opencode 被硬编码
排除(cli.resolve_mcp_config 直接返回 None),opencode 用户因此没有任何
GUI 能力。本 commit 补齐整条链路:

- plugins/state.py:write_mcp_config 新增 opencode 原生格式(`{"mcp":
  {name: {type: local, command, args, enabled}}}`,落盘 *.opencode.json)。
- adapters/opencode.py:构造器接受 mcp_config;与 base_url 的 provider 覆盖
  合并写入同一个 OPENCODE_CONFIG 运行时文件(_write_endpoint_config 升级为
  _write_runtime_config),用户自有 providers/models/MCP 不受影响。
- cli.py:resolve_mcp_config 解除 opencode 排除;_build_agent 透传
  mcp_config;_MCP_AGENT_CHOICES 加入 opencode(doctor / plugin list /
  plugin install --agent 全部自动获得 opencode 视图)。
- plugins/community_computer_use.py:open-computer-use 与 clawdcursor 声明
  支持 opencode。

测试:新增 tests/test_opencode_mcp.py(配置格式、插件状态解析、适配器
合并/拒绝路径共 8 项);全量 410 passed / 4 skipped / 0 failed。

🤖 Generated with [opencode](https://github.com/anomalyco/opencode)
此前 auditor 的三态判定(status/integrity_status/contract_audit_status)完
全依赖对自然语言控制头的正则推断,多语言措辞漂移或模型改写句子即会误读。

- prompt_texts.py:四份 auditor 指令(en/zh × GUI/CLI)在保留三行控制头
  的前提下,要求报告末尾追加一个 ```json 机器可读判定块(status/
  integrity_status/contract_audit_status + integrity_findings/
  deleted_artifacts),并要求与控制头一致、2000 字符以内。
- auditor_agent.py:_extract_structured_audit 提取并逐字段校验枚举(取最
  后一个合法块);parse_audit_report 与 audit_report_from_episode_result
  改为 JSON 字段优先、控制头正则逐字段兜底——旧格式报告(无 JSON)解析
  路径与此前完全一致;violation 时的删除账本优先采用结构化
  deleted_artifacts 并归一化为既有 ledger 形状。
- 验收约束守卫、violation→incomplete 降级等既有安全逻辑全部保持权威。

测试:新增 tests/test_audit_structured.py 7 项(JSON 权威、契约不一致降
级、部分字段回退、坏 JSON 兜底、无 JSON 兼容、结构化删除账本、多块取
末);全量 417 passed / 4 skipped / 0 failed。

🤖 Generated with [opencode](https://github.com/anomalyco/opencode)
- agent_logs.token_usage():跨后端解析轨迹中的 token 用量(Codex/Claude 的
  顶层 usage 对象与 OpenCode 的 part.tokens/cost 两种形态,未知形态安全跳
  过);CommandAgentAdapter 每个 episode 将汇总写入 metadata["token_usage"]。
- manager:ManagedRound 携带本轮 token_usage(executor+auditor 合并),
  report.json 新增顶层 token_usage 汇总块(无任何记录时输出空对象,保持
  schema 向后兼容)。
- 新增 docs/windows-troubleshooting.md:PATH 生效、控制台编码、Store 版
  codex.exe 空别名、GUI 桌面会话要求、代理与免费模型限流等实测坑位;
  中英文 README 各加一节链接。

测试:新增 tests/test_token_usage.py 5 项(OpenCode/Codex 形态求和、噪声
忽略、角色合并、跨轮聚合与空块语义);全量 422 passed / 4 skipped / 0 failed。

🤖 Generated with [opencode](https://github.com/anomalyco/opencode)
prax211 added a commit to cogniziocompany/LongHorizon-Harness that referenced this pull request Sep 5, 2026
…); deploy-job defects AMAP-ML#66-AMAP-ML#68

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
prax211 added a commit to cogniziocompany/LongHorizon-Harness that referenced this pull request Sep 6, 2026
…states — owner cec370bb, run 1eeb4daf; AMAP-ML#66 must not merge

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
prax211 added a commit to cogniziocompany/LongHorizon-Harness that referenced this pull request Sep 6, 2026
 closed; promotion run fixes #44-#49

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows 11 全流程不可用:启动引导即失败(O_NOFOLLOW/dir_fd/flock/信号 等 POSIX 依赖)

1 participant