← Về thư mục
📄 / / profiles / r-and-d / raw_evidence / OpenAI4S / docs / windows-wsl-parity-audit_zh.md

Windows / WSL2 与 macOS 对齐调查

English

基线:bca1183fcc1cd9f4820bb54139ecdaaf63adfcf6,2026-09-07。 目标用户已经有 WSL2;目标是下载 Windows 包后,完成与 macOS 相同的科研操作。 分支现已在原调查之后加入产品修复。下方基线观察是历史记录,不能当作当前分支的运行结果。本次验收针对 WSL2 x86_64,不代表所有 Windows 配置均已认证。

已实现的修复

对应问题 实现 使用者看到的变化
W0 独立 WSL 适配层、Windows/系统会话挂载遮罩、私有 PID 和 AF_VSOCK 过滤;复用现有沙箱及中断机制 分析代码不能借 Windows 互操作绕过边界;Python/R 取消后仍可接着使用分析变量
W1、W5 排除 Docker 等基础设施发行版,检查架构、普通用户和可用 bubblewrap,提供 Ubuntu 依赖准备入口 首次启动选择用户发行版,并说明、准备缺失的隔离组件
W2 SecretBroker 增加 Windows 用户 DPAPI 后端,保留已有可用 Linux 钥匙串 新 WSL 安装无需配置 Linux 桌面钥匙串,也能加密保存模型密钥
W3、W4、W9 CLI 使用随包 runtime,过滤 Windows 工具搜索路径,明确使用 Bash 科学命令跟随所选分析环境,source、[[ ]] 等 Bash 语法正常工作
W6–W8 按包摘要暂存、成功后原子切换,管理命令脱离新包,报告实际运行版本 坏更新保留旧安装;更新时提示稍后重启,不自动结束正在进行的分析
C1、C2 同时修正 macOS/Linux 构建的默认网页和 Python 命令重定位,增强包校验 下载后能打开真正的工作台,科学工具搬到安装目录后仍能运行
W10 — 浏览器验收新发现 由随后台服务存活的单个线程创建分析进程,不依赖短暂请求线程 网页执行完一格后内核不再被误杀,后续 Cell 和刷新页面仍可使用已有变量
W11 — 独立启动验收新发现 Windows 启动器持有隐藏的 WSL 进程,Linux 服务以前台方式运行 无需保持终端开启;服务停止时后台 WSL 进程一起退出
C3 — 安装包验收新发现 Linux/macOS 包内 CLI 用 -P 关闭 Python 对当前目录的隐式导入 项目中的 openai4s.py 或源码 checkout 不会遮蔽已安装应用,同时保留用户工作目录

最终全量回归也复现了原有 HTTP 超时竞争:连接自身先超时、看门狗后触发时,异常类型不一致。修复集中在共用的有界读取层;测试分别覆盖两种顺序,并避免把无关的 TLS 证书加载放进仅 200 ms 的 socketpair 测试时限。

分层上,WSL 策略放在 security/wsl.py,加密存储放在 security/windows_dpapi.py,沿用现有内核协议、科学服务和 UI。环境检查、自定义工具这两个短进程也补齐沙箱描述符传递。硬链接扫描保留在团队隔离和 Windows 文件系统工作区,不再给每个普通 Linux 开发目录增加全树扫描。

验收包括 harness.smoke.wsl_sandbox、真实 Python/R 中断、离线回归、原生 PowerShell 5.1 启动器测试,以及实际 Windows 浏览器和安装包操作。最终结果见本文末尾的修复验收记录。

基线结论

WSL 能运行现有 Linux Python 内核,问题集中在产品没有准备齐运行条件,以及 Windows 与 Linux 之间多出的边界。 macOS 包已经准备了 Python、科学依赖和启动环境,系统另外提供沙箱与钥匙串。 WSL2 的存在并不保证发行版里有可用的沙箱、已解锁的密钥服务,或正确的 Linux 工具环境。

最优先的问题是 Cell 能通过 Windows 互操作绕过现有沙箱。 普通 Linux 边界 smoke 全部通过后,真实的 enforced Cell 仍然成功运行 Windows PowerShell,修改了沙箱工作区外的一个合成标记文件。 现有 smoke 的绿色结果不能作为 WSL 安全边界验收。

实测边界与证据

官方镜像 SHA-256:9b2f7730dc68227dd04a9f3e5eab86ad85caf556b8606ad94f1f29ff5c4fd3f5,与 Ubuntu 校验清单一致。

完整产物也已实测:Linux payload 约 406 MiB、解包约 1.4 GiB;现有验证器成功导入 38 个科学包。 从实际 Windows ZIP 解压到含中文和空格的目录后,PowerShell 5.1 启动器退出 0 并报告 ready,普通用户安装与 daemon 启动成功。 Windows 访问测试端口得到未认证 401、认证后首页 404,安装目录确实没有默认 UI。 这证明连接和认证能够工作,而产物及 ready 检查没有覆盖默认页面。 真实 launcher 的 status 返回运行中的同一 PID,重复打开复用该 daemon,stop 返回 0,停止后 status 返回 1 并报告 not running;测试 daemon 已停止。 补装密钥组件后,D-Bus ReadAlias(default) 返回 /,Collections 仅有 session;SecretBroker 的存储自检超时,未得到可持久使用的默认密钥库。

从上到下:用户流程

flowchart LR
    A[Windows 解压并双击] --> B[选择 WSL 发行版和用户]
    B --> C[检查依赖并安装 Linux payload]
    C --> D[启动 Linux daemon]
    D --> E[Windows 浏览器打开认证地址]
    E --> F[配置模型与保存密钥]
    F --> G[上传数据并执行 Python / R / Bash]
    G --> H[查看与下载产物]
    H --> I[停止、重开、更新与恢复]
用户动作 当前证据 达到 macOS 对齐所需验收
解压、双击 发行版选择器会把 docker-desktop 当候选;全新 Ubuntu 在 bwrap 预检处退出 排除基础设施发行版;验证实际发行版的架构、运行用户、运行时兼容性;缺依赖时提供可完成的准备流程
打开界面 实际 ZIP 安装后 launcher 报 ready,但认证后首页 404;完整 Linux verifier 仍通过 验证最终安装的默认 UI 及 HTML 引用的 JS/CSS;同时修复共享的 macOS 打包问题
配置模型 全新 WSL 的 SecretBroker 拒绝保存;仅安装密钥工具也没有建立可用服务 安全存储、解锁、保存、读取、退出后重开、WSL 重启后恢复;不能隐式改为明文
上传 CSV、做 Python 分析 真实内核的 Python、持久变量通过;包内 CLI 的 shell PATH 不正确 Cell 和 shell 使用预期解释器;完成上传、分析、图像/表格捕获、下载,使用 Windows 浏览器验证
执行 Bash POSIX 命令成功;source、[[ … ]] 在真实 host.bash 中以 127 退出 明确并统一前台与后台 shell;实际安装依赖后能够在对应环境中使用
执行 R / 专业工具 R、Conda 不在两端基础包;213 个领域脚本没有全依赖执行 按能力准备环境,检查真实工具和版本;R 的 fd 通道、停止后继续执行单独验收
隔离 Cell 普通 Linux smoke 通过;Windows 互操作改写沙箱外标记成功 Cell 不能借 Windows 进程、IPC 或路径别名绕过文件、进程、网络和凭据边界
停止和重开 199 项现有测试通过;真实 ZIP 的 status、重复打开复用、stop、停止后 status 通过 补测端口冲突、有活动任务时停止、停止后完整科研重跑和重启恢复
更新与故障恢复 同版本坏包会删掉原 CLI;status/stop 仍依赖并安装当前 ZIP payload;存活 daemon 未核对版本 有活动任务时不误停;报告真实运行版本;安装失败保留可用版本;坏包不阻断诊断和停止
WSL / Windows 重启 尚未完成产品全流程验收 发行版与用户选择稳定;密钥、会话、安装环境持久;地址变化后仍可连接

浏览器上传、下载和 WebSocket 已有 HTTP 通道,先验证这些通道,不需要先设计 Windows/Linux 全盘路径转换。 代理、Fake-IP、localhost 转发和回退已有实现,但本次未改变全局网络设置,不能把某一个网络模式通过推广到所有机器。

已确认的问题与调用链

ID / 优先级 观察与用户影响 代码入口与对齐方向
W0 / 发版阻断 enforced Python Cell 启动 Windows PowerShell,改写工作区外的自建文件;普通 Linux smoke 同时通过 kernel/manager.py → security/sandbox.py:wrap_bwrap_command。WSL 互操作的程序、socket、挂载与 proc 别名都需要边界;worker 环境去掉 WSL_INTEROP 仍不足以阻断本次复现
W1 / 首次运行 干净 Ubuntu 的 bootstrap.sh preflight 因缺 bwrap 返回 1,当前只打印 apt 指令 scripts/windows/bootstrap.sh:run_preflight。已有 WSL2 后仍需要一次可理解、可重试的 sandbox 准备及真实自测,维持 enforce
W2 / 模型配置 无可用系统密钥后端,SecretBroker fail-closed;装上 secret-tool 也不等于用户会话与已解锁的 Secret Service 可用 security/secret_broker.py:SecretServiceBackend / SecretBroker._resolve。先确定凭据归属、服务生命周期、重启与解锁契约,再实现安全后端;Windows 系统存储桥接也必须受 W0 边界保护
W3 / 科研执行 WSL 调用的 bundled CLI 未设置 runtime/bin 的 PATH;fixture 中 python3 解析为 /usr/bin/python3,python 缺失 build_linux_bundle.sh 的 CLI → bootstrap.sh serve → kernel/environment.py。macOS desktop LAUNCHER 设置了 PATH。两端 CLI 都有同样缺口,统一启动环境,并验证选中的 Conda 环境仍优先
W4 / 科研执行 host.bash 的 shell=True 实际使用 Ubuntu /bin/sh;source、[[ ]] 失败,而 Jobs 显式使用 bash sdk/bash.py:BashExecutor、jobs.py。统一约定 shell,保留授权、超时、进程组与审计。R 启动用 sh 做 POSIX fd 重定向本来正确,不能机械替换
W5 / 发行版选择 用真实选择函数和合成发行版列表复现:只有 docker-desktop 时,它被选中;未在 Docker 内执行程序 openai4s.ps1:Select-Distro。过滤基础设施发行版,再探测能力,保留现有数据归属及明确用户选择
W6 / 更新 新包安装后,只检查 TCP 与 CLI status 就复用已有 daemon,没有比较正在运行的构建 openai4s.ps1:Test-OpenAI4SServing 与主流程。区分安装版本、运行版本和有无活动任务,支持安全重启或明确的待重启状态
W7 / 故障恢复 管理命令跳过 bwrap 检查,但仍走 Get-PackageFacts → install → cli;坏 payload 能阻断 status/stop/doctor openai4s.ps1 的 Arguments 分支。管理已有安装不应依赖新 ZIP 完整,也不应为了查询状态而改写安装
W8 / 安装恢复 对已有同版本目录提供摘要正确、内容无效的归档:tar 失败,原 CLI 已不存在 bootstrap.sh install 的 rm → tar 顺序。临时目录解包并验证,成功后切换;并发与恢复规则需一并覆盖。不同版本目录已分开,不能声称所有升级都删旧版或会话
W9 / 工具发现与延迟 本机 WSL PATH 有 43 项,其中 34 项在 /mnt 下;5 次查找不存在命令的中位数为 209 ms,纯 Linux PATH 为 0.034 ms。还会找到缺少对应 Linux node 的 Windows npx CLI/daemon 继承的 PATH、cli/main.py:_find_conda_tool 等 which 调用。明确 Linux 工具解析范围,检查实际可执行能力并按环境变化失效缓存;不全局改用户 WSL 设置。这是本机实测,未对全量测试慢作完整归因
C1 / 共享发版阻断 真正执行 rsync 后 webui/dist/index.html 缺失;bundle_contract.check_sources 却通过,仍识别到 604 个 Skills 两个 bundle builder 都使用 --exclude 'dist';共享 REQUIRED_SOURCES 只要求旧 UI。精确包含默认 UI 与引用资源,在三个桌面产物验证,而不只是 wheel
C2 / 科学命令重定位 本次实际 payload 有 22 个 console scripts 写死构建机解释器路径。仅在临时 mount namespace 隐藏构建目录后,已安装的 f2py 入口失败;同一安装的 python3 -m numpy.f2py -v 与 pip 成功 Linux builder 的 pip 安装阶段及 runtime/bin;macOS 使用相同安装方式,需对照复验。修复 console script 重定位,并在看不到构建目录的环境中逐个检查;当前 verifier 的入口检查没有覆盖这些命令

Python 的 POSIX shell=True 默认调用 /bin/sh,见 subprocess 文档。 这些问题不支持“WSL 的 Linux syscall 普遍不兼容”的结论;它们各有具体的环境或流程原因。

从下到上:脚本与进程盘点

逐文件清单记录了基线的全部 226 个 .sh / .command / .ps1 / .cmd 文件、6 个生成的 shell heredoc,以及 25 处直接可识别的 Python subprocess / host.bash 调用。 还有两个远程计算 .sh.tmpl 模板,需要按渲染后的运行环境判断,不能混同桌面启动器。 224 个 .sh / .command 在真实 Ubuntu Bash 下全部通过 bash -n;语法通过不是依赖齐全,也不是工作流通过。 AST 清单不保证捕获动态别名、生成代码或配置里的任意命令。 针对领域 shell 脚本检索 brew、osascript、pbcopy/pbpaste、/Applications、/Users、/opt/homebrew 等 macOS 特征未发现匹配;这只排除了这组明确特征,不能替代逐工作流运行。

层级 文件 / 模块 检查重点与边界
构建与分发 build_linux_bundle.sh、build_macos_dmg.sh、build_windows_zip.sh;签名与 tag 脚本 payload/架构、默认 UI、依赖、符号链接与权限、LF/CRLF;公证脚本在 macOS 构建机运行
桌面启动 Windows 三个启动文件;Linux/macOS CLI 与 LAUNCHER heredoc;Linux INSTALL/UNINSTALL heredoc Windows 参数 → wsl.exe argv → Linux shell 的路径、用户、PATH、环境、版本和生命周期
开发入口 setup.sh、start.sh、scripts/setup_envs.sh uv、已建 venv、Conda 是开发/扩展环境前提,不能拿开发环境成功冒充解压包成功
执行核心 kernel transport、R kernel、SDK Bash、Jobs、dynamic tools、preinstall 哪个进程持有何种身份与环境;shell 语义;权限;进程组;中断;stdout/stderr;持久命名空间
环境管理 CLI setup、env_generations、preinstall、环境描述 bundled base 与 named python/r 环境不同;Python、pip 和专业 CLI 必须属于用户实际选择的环境
外部程序 MCP client、Ark CLI、git、科学服务 在 Linux daemon 的实际环境中启动并验证。which 能找到 Windows shim 不代表它是可用 Linux 工具;本机初始能找到 Windows npx,但没有 Linux node
远程执行 compute manager、Slurm broker、local worker backend、fold_remote、run/wrapper 模板 SSH/远程 Linux 环境、GNU timeout/setsid、远程 R/科学依赖;需要独立凭据和服务器,未作真实远程验收
领域脚本 skills/ 下 213 个脚本 recipe 已分发不等于程序已安装。根据实际能力检查 STAR/samtools/mafft 等工具,不把全部工具塞成首次启动前提
构建/研究辅助 container smoke、benchmark、review scratch、BYOC confinement 独立部署或研究能力,不应成为普通 Windows 首次启动的隐性依赖

R、Conda、完整领域 CLI 未随 macOS 基础包提供,属于共同的能力准备问题。 Stage 1 standard-profile 门禁默认关闭;不能根据它在开启时要求 named python/r 环境,推断默认 Python 必然不能运行。

验证记录

现有 CI 和 release 的 windows-launcher job 覆盖 PowerShell 解析及「机器没有 WSL 时正确拒绝」,没有执行有 WSL2 的安装成功路径。 Linux runner 的沙箱 smoke 也不包含 Windows 互操作。因此修复后需要独立的真实 WSL2 验收,不能只依赖这两个现有绿色检查。

检查 结果与限制
现有内核、Bash、平台、发版测试 199 项通过,真实 WSL 普通用户
Bash 语法盘点 224 个 shell 文件通过,未执行全部领域工作流
Linux 完整边界 smoke enforce、文件写边界、网络和子进程密钥隔离检查通过;仍漏掉 W0 的 Windows 通道
完整 Linux payload 验证 通过,执行了嵌入 Python 并导入 38 个科学包;仍漏掉 C1/C2
实际 Windows ZIP 中文/空格路径安装、认证、重复打开及状态/停止通过;默认首页 404
全文件 pre-commit / 类型检查 通过
目录双语覆盖 / 源码密钥扫描 160 个目录、1476 个直接文件覆盖通过;3787 个文件扫描通过
完整离线套件 已完成:8303 passed、136 skipped、2 failed、1 warning,耗时 852.74 秒。uv run --locked pytest -n auto --maxprocesses=4 --dist loadfile,明确 Linux PATH,私有 tmpfs 临时数据,tmp_path_retention_policy=failed
失败项复验 改用 uv run --locked pytest -n 0 和普通磁盘临时目录,两项均通过,耗时 17.92 秒;不能将全量结果改记为通过

测试条件需要区分:第一次直接调用 .venv/bin/pytest 时,uv 未进入 PATH,产生 1 个发布构建命令断言失败;中断时另有 5172 passed / 103 skipped。改用正确 PATH 与 uv run 后,该失败项单独通过。随后磁盘上的全量尝试因 I/O 等待中断,1951 passed / 38 skipped,未算作全量通过。最终离线回归使用普通用户、私有内存临时目录,并保留失败用例数据;实际安装、密钥、沙箱和重定位探针使用真实 Windows/WSL 文件系统。

完整运行中,tests/test_mcp_client.py::test_streamable_http_body_watchdog_interrupts_a_chunked_slow_drip 得到原始 TimeoutError,而测试期待 MCPTimeout;tests/test_skill_product_surface.py::test_team_member_cannot_reactivate_legacy_host_skill_poison_over_http 的 HTTP helper 没有收到状态行。两项单独复验通过,根因尚未确定,不能归因于 WSL、tmpfs 或本次文档/探针改动。唯一 warning 来自刻意模拟原生 win32 不支持沙箱的测试。

修复顺序与完成条件

  1. 先闭合 WSL 边界并确定安全密钥方案。 新增真实 WSL 回归,涵盖 Windows 进程/IPC、文件与凭据读取、路径别名及网络通道。验证停止和持久 Python/R 仍然可用;不要依赖全局关闭用户机器的 WSL 功能来通过测试。
  2. 修正共享产物与执行环境。 默认 UI、资源检查、bundle CLI PATH、Linux 工具发现、console script 重定位、前后台 Bash;验收从 ZIP 解压后的应用出发,隐藏构建目录,不能依赖开发机已有 Python/Node/Conda。
  3. 完成 Windows 启动与恢复流程。 发行版能力探测、缺依赖准备、解包后切换、管理命令脱离新包、运行版本识别和安全重启;覆盖空格/中文路径、坏包、重复启动及并发。
  4. 走完整科研流程和重启回归。 配置真实模型、上传 CSV、Python/图表/下载、准备 R、代表性专业 CLI、Stop 后继续、关闭重开、WSL 重启和升级。MCP/Ark 登录作为相应扩展能力单独验收。

未完成项:真实 macOS 对照运行、Windows on ARM、多发行版、多网络模式、真实供应商登录/模型调用、所有领域脚本、WSL/Windows 重启后的完整产品恢复。 这些是明确的待验收范围,不应写成已经通过。

复现

在普通 WSL 用户的干净 checkout 中,先确保有 Bash、rsync;沙箱探针需要 bubblewrap。 将下例的 Windows 临时目录换成自己已有的目录;脚本只在其中新建并清理自己的随机子目录。

python3 scripts/probe_wsl_parity.py \
  --windows-scratch /mnt/c/path/to/audit-temp \
  --output /tmp/wsl-parity.json
OPENAI4S_KERNEL_SANDBOX=enforce python3 -m harness.smoke.linux_sandbox

第一条命令退出 0 只代表收集完毕,要读取 JSON 中的失败观察,特别是 windows_marker_changed。 第二条在当前基线能通过,却覆盖不到第一条证实的 Windows 互操作问题。

对实际安装包的重定位,使用自己构建时的目录和安装后的路径执行下面的检查。 --tmpfs 只在这个子进程的 mount namespace 内遮住构建目录,不会删掉或移动实际目录。 本次第一条 f2py 执行失败,后两条成功;包内另有 21 个命令带同类绝对 shebang,未声称全部逐个执行。

PARITY_APP=/path/to/installed/OpenAI4S-0.2.0-linux-x86_64
PARITY_BUILD_ROOT=/path/to/checkout/.build
bwrap --unshare-net --ro-bind / / --tmpfs "$PARITY_BUILD_ROOT" \
  --dev /dev --proc /proc -- "$PARITY_APP/runtime/bin/f2py" -v
bwrap --unshare-net --ro-bind / / --tmpfs "$PARITY_BUILD_ROOT" \
  --dev /dev --proc /proc -- "$PARITY_APP/runtime/bin/python3" -m numpy.f2py -v
bwrap --unshare-net --ro-bind / / --tmpfs "$PARITY_BUILD_ROOT" \
  --dev /dev --proc /proc -- "$PARITY_APP/runtime/bin/pip" --version

并发构建、安装和全量测试期间还观察到 WSL 服务连接超时 (0x8007274c);一次 launcher 将失败展示为目录不可达,而同一中文/空格目录此前已成功安装。 本轮没有确定服务超时的根因,也不据此认定 Unicode 路径不兼容。后续应单独验证 WSL 服务故障时的超时、重试和诊断信息。

修复验收记录 — 2026-09-07

以下结果更新了上方的基线失败记录。测试仍在隔离的 Ubuntu 24.04 WSL2 x86_64 发行版内以普通用户运行;Windows 浏览器为 Chromium,启动器使用 Windows PowerShell 5.1。核心没有新增第三方依赖。

归档摘要与最终运行回执记录在证据 JSON 的 fix_verification 中。构建验证复用了此前完整组装的科学运行时,并用产品修复后的代码重新执行构建器的源码复制、CLI 生成、字节码编译、完整 Linux 验证与 Windows 打包步骤。

这些证据覆盖 WSL2 x86_64,不代表已认证真实 macOS 执行、ARM、任意发行版/网络模式、真实供应商登录与推理、Windows 整机重启、Conda 环境准备或全部领域工作流。