Skip to content

fix(windows): resolve WinError 5 SOC WebUI install race + Chinese-Windows console encoding crash#602

Open
HerryOS wants to merge 2 commits into
AgentFlocks:devfrom
HerryOS:fix/webui-soc-winerror5
Open

fix(windows): resolve WinError 5 SOC WebUI install race + Chinese-Windows console encoding crash#602
HerryOS wants to merge 2 commits into
AgentFlocks:devfrom
HerryOS:fix/webui-soc-winerror5

Conversation

@HerryOS

@HerryOS HerryOS commented Jul 22, 2026

Copy link
Copy Markdown

概述

两个 Windows 平台专属修复,均在中文 Windows 11 上安装 SOC 工作区 WebUI(soc_ui)插件时发现。

1. 安装 WebUI 插件时报 WinError 5(安装器与文件监听器竞态)

安装 webui 类 Hub 插件时,会先把它暂存到 ~/.flocks/plugins/contracts/webui 内部的临时目录(.<name>.<rand>),在其中完成页面构建,再原子替换到正式位置。但该目录同时被 WebUI 页面监听器递归监视——监听器发现临时目录里“半成品”页面后,会在安装进行中触发自己的 esbuild 重建。在 Windows 上,监听器持有的文件句柄导致原子替换失败并报 [WinError 5] 拒绝访问,留下 .soc_ui.<rand> / .soc_ui.bak 残留和幽灵页面目录。

修复(多层防御):

  • 监听器:在查询 store 之前就忽略点号前缀目录下的事件,使监听器永远不会和安装器的原子替换竞态(根因)。
  • store:扫描页面/工作区时跳过点号前缀目录,让残留的临时/备份副本永远不会作为真实页面出现。
  • 安装器:在 Windows 上遇到 PermissionError 时带退避重试原子替换,并在暂存前清理陈旧的 .<name>.* 临时目录,使上一次失败的安装能在下次自愈。

2. flocks restart 在中文 Windows 上刷屏 UnicodeDecodeError

重启时的存活/健康探测会调用 tasklist / netstat / powershell / node -v,使用 capture_output+text 但未指定编码。Python 的读取线程按 utf-8 解码 GBK 控制台输出,在字节 0xbb 处崩溃。为这四处 subprocess.run 加上 encoding="utf-8", errors="replace",与 _windows_process_snapshot 已有的写法保持一致。

测试

  • 为所有修复新增回归测试(每个测试在缺少对应修复时都会失败):tests/hub/test_installer_swap.py、store/watcher 套件中的临时目录用例,以及 tests/cli/test_service_manager.py 中的 GBK 解码用例。
  • 同时通过一个 autouse conftest fixture(让 Path.home() 遵循 HOME)使 hub/config/device 测试套件在 Windows 上可隔离,将 27 个原本失败的隔离测试转为通过,且未引入任何新失败。
  • 已实测验证:flocks restart 现在输出 0 条 UnicodeDecodeError,soc_ui 安装可干净完成。

HerryOS and others added 2 commits July 22, 2026 14:30
The Hub installer stages a WebUI plugin into a scratch dir
(.<name>.<rand>) inside ~/.flocks/plugins/contracts/webui and then
atomically swaps it into place. That directory is watched recursively
by the WebUI page watcher, which discovered the half-built pages inside
the scratch dir and launched its own esbuild rebuild mid-install. On
Windows the watcher's open handles blocked the swap with a WinError 5
access-denied, leaving .soc_ui.<rand> / .soc_ui.bak litter and phantom
top-level page dirs.

Fixes (defense-in-depth):
- watcher: drop events under dot-prefixed dirs before any store lookup,
  so the watcher never races the installer's atomic swap (root cause).
- store: skip dot-prefixed dirs when scanning pages/workspaces, so
  stranded scratch/backup copies never surface as real pages.
- installer: retry the atomic swap on Windows PermissionError with
  backoff, and purge stale .<name>.* scratch dirs before staging so a
  prior failed install self-heals on the next attempt.

Adds regression tests for all three (each fails without its fix):
tests/hub/test_installer_swap.py plus scratch-dir cases in the store
and watcher suites.

Co-Authored-By: Claude <noreply@anthropic.com>
…ke Path.home() tests hermetic

Bug 1 — `flocks restart` spewed UnicodeDecodeError on Chinese Windows.
The restart-time liveness/health probes shell out to tasklist / netstat /
powershell / `node -v` with capture_output+text but no explicit encoding,
so Python's reader thread decoded the GBK console output as utf-8 and
crashed (byte 0xbb). Add encoding="utf-8", errors="replace" to those four
subprocess.run calls, matching the pattern already used by
_windows_process_snapshot.

Bug 2 — hub/config/device tests were not hermetic on Windows. They isolate
the filesystem via monkeypatch.setenv("HOME", ...), but Windows Path.home()
reads %USERPROFILE% and ignores HOME, so production code resolving
Path.home()/".flocks" leaked into (and asserted against) the real user
home. Add an autouse conftest fixture that makes Path.home() and bare-tilde
os.path.expanduser honor HOME on every platform, aligning Windows with
Linux CI. Behavior-preserving when HOME already points at the real home.

Net effect on a Windows test run: 27 previously-failing hub/config/device
isolation tests now pass, with zero new failures. Adds regression tests for
the subprocess decode fix (each fails without it).

Co-Authored-By: Claude <noreply@anthropic.com>
@stephamie7
stephamie7 changed the base branch from main to dev July 22, 2026 09:01
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.

1 participant