Repository navigation
Conversation
上游实现深度依赖 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)
这是对 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)
|
第二阶段已推送(c26437b),本 PR 现在包含两个 commit:
第二阶段要点:
完整说明见 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)
…— owner cec370bb, run 1eeb4daf; #66 must not merge Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Upper9527
left a comment
There was a problem hiding this comment.
Thanks for the Windows implementation and the reported Windows test results. I reviewed the current diff and found a blocker in the fallback filesystem boundary.
In control_bus._open_private_regular_at, the path-based branch checks S_ISREG but drops the POSIX branch's hard-link rejection. An existing hard-linked regular file is therefore accepted as private control state; opening it with caller-supplied truncation flags or appending to it can modify a file outside the run. validate_no_symlink_chain plus a later path open also does not provide the same anchored guarantee as directory descriptors, so the fallback must not silently be described as equivalent isolation.
Please retain the unaliased-file invariant (including checking the opened handle before any destructive write), cover junction/reparse-point and ancestor-replacement cases, and add Windows regression tests for those boundaries. Please also separate the unrelated structured-audit parsing, token-accounting, and OpenCode MCP changes from the Windows portability patch so the platform change can be reviewed independently. A passing aggregate suite is useful, but it does not yet establish these invariants. Tracking the native Windows work in #70; the duplicate reports will link there.
修复 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 平台的任何既有行为。
问题与修复对照
control_bus要求O_NOFOLLOW+dir_fd锚定目录遍历,Windows 无此能力_SECURE_DIRFD能力检测;回退为「路径创建 + 逐级符号链接扫描」fcntl.flockWindows 不存在_process_lock:msvcrt 字节锁实现等价跨进程互斥O_NOFOLLOW_append_event增加「校验后普通追加」分支mkdir -p/chmod是 POSIX 命令,create_subprocess_shell在 Windows 走 cmd.exeLocalEnvironment原生ensure_dir/chmod;remote_files优先调用并兼容\路径分隔VAR=x cmd前缀,cmd.exe 不解析utils/platform_shell.py(shell_quote/cd_command/env_prefix),4 个适配器 +cli_agent全部接入os.killpg/SIGHUP缺失process_group按 PID 回退 + 信号按存在性收集;service.py4 处killpg加护栏改动范围
13 个文件,+379/−79(
git diff --stat见 commit):验证
python -m compileall src/lh_harness通过(append/commands/conflict 检测)、事件追加、全部改动模块可导入
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 类问题
_signal_worker接缝、#!/bin/sh桩翻译为 .cmd、引号断言感知平台)
.github/workflows/ci.yml:ubuntu + windows + macos 三平台 pytest矩阵(此前仓库无任何测试 CI)
Fixes #70