# ServerFS v0.11 Windows Native Agent Bridge Discovery Report Task: pre-development investigation only. No source, test, config or provider state was modified. Executed on: WorkPC, native Windows 11 x64, repository `D:\Downloads\Project\ServerFS_MCP`. Report date: 2026-10-04. Status vocabulary used throughout: `VERIFIED` (executed command or read source), `INFERRED` (reasoned from verified facts, not directly measured), `UNKNOWN`, `NOT TESTED` (deliberately not attempted), `BLOCKED` (attempt refused by environment or by the task boundary). --- ## 1. Executive Summary 1. **The v0.10 native Windows ServerFS baseline on this machine is green.** `serverfs 0.10.0` + `serverfs-windows-native 0.10.0` on uv-managed CPython 3.13.3 AMD64; `serverfs doctor` exit 0 with `0 FAIL, 0 WARN`; `ruff check` and `ruff format --check` pass; the 15 Windows-targeted test files run `359 passed, 3 skipped` in 13 s. `VERIFIED`. 2. **The Agent Bridge is not portable as-is, and the blocker is narrow and well separated.** Four Bridge subsystems are Linux-only: UDS transport (`protocol.py:54`), peer identity (`protocol.py:240-252`), the `fcntl.flock` writer lease (`leases.py:5,106`) and POSIX ownership/mode guards (`store.py:43`, `recovery.py:37-49`, `result_spool.py`, `protocol.py:61-71`). The task model, state machine, event envelopes, manifest, spool, idempotency, approvals/questions, model discovery/override and the SQLite persistence *format* are provider-neutral and platform-neutral in logic. `VERIFIED` by source. 3. **Windows already provides an equal-or-stronger replacement for the two hard platform primitives, and both were measured working on this machine.** (a) Named pipes round-trip and the server side can read the **client PID and client session ID** (`win32pipe.GetNamedPipeClientProcessId` → observed PID equal to the connecting process, `MATCH`); (b) `msvcrt.locking` byte-range locks are **cross-process exclusive** (second process blocked). `SO_PEERCRED` semantics are therefore reproducible; they are *not* reproducible via AF_UNIX, because this CPython exposes no `socket.AF_UNIX` at all. `VERIFIED`. 4. **Runtime availability is asymmetric across the three runtimes.** Codex is fully usable on this Windows host (CLI 0.159.2, managed app-server daemon *running* at 0.160.0, `codex login status` = "Logged in using ChatGPT") and **every app-server method/field the current adapter assumes still exists**, proven against the protocol JSON Schema that the installed CLI itself generated. Claude Code runs natively (2.1.285) but **`claude-agent-sdk` publishes no Windows wheel** (macOS + manylinux + sdist only) → hard environment blocker. Qoder CLI works (`1.1.65`, authenticated, `--list-models` returns a catalog) and **`qoder-agent-sdk` does ship a `win_amd64` wheel**, but neither SDK is installed here, so their APIs are `NOT TESTED`. `VERIFIED`. 5. **The native MCP process is structurally Agent-disabled, and by three independent mechanisms**, not one: no agent fields exist in the native TOML schema; `cli.py` builds `Settings` without `agent_bridge_enabled`; and even if the flag were forced on, `agent_tools.py` would crash on `fdio.py:34` (`os.O_DIRECTORY`) rather than refuse cleanly. Native workdirs additionally **cannot take a writer lease at all** — `workdirs.py:134-147` returns sentinel slot `0` and `agent_leases.py:32-33` refuses anything outside 1..16. `VERIFIED`. 6. **Codex's own Windows transport is the open design question.** The running daemon advertises `socketPath = ~/.codex/app-server-control/app-server-control.sock`, i.e. Codex (Rust) uses an AF_UNIX-shaped endpoint that CPython cannot reach (`socket.AF_UNIX` absent). Two Codex-supplied Windows-viable seams exist and are documented in the installed CLI: `codex app-server proxy` (relays stdio bytes to the control socket) and `--listen ws://IP:PORT`. Neither is exercised yet. `VERIFIED` that they exist; `NOT TESTED` whether they work here. 7. **The largest non-obvious cost is the test suite, not the Bridge.** The root pytest suite does not even collect on Windows: 12 collection errors (`fcntl`, `os.O_DIRECTORY`, module-scope `os.geteuid`). Of the 941 collectible tests, 122 fail + 3 error on Windows, concentrated in `test_agent_deployment` (38), `test_agent_tools` (22), `test_edit_text_file` (14), `test_tunnel_proxy_deployment` (12). `agent_bridge/tests/` (138 tests) is not importable here at all. `VERIFIED` by execution. 8. **Recommended v0.11 boundary (§24)**: native Bridge process, named-pipe IPC + PID/SID peer identity, alias-keyed (not slot-keyed) writer lease, NTFS-ACL-based state-directory guards, Codex as the single first runtime, Qoder second, Claude deferred until a Windows SDK wheel exists. No BLOCKED item stopped the investigation; two sub-questions could not be answered inside the read-only boundary and are listed as `NOT TESTED` in §23. No secret, credential, host path, proxy endpoint or workdir root is recorded in this report. --- ## 2. Investigation Scope and Safety Boundaries In scope (all executed read-only): repository/git state; Windows host and toolchain facts; v0.10 native ServerFS verification commands and root gates; complete Agent Bridge source reading; POSIX assumption inventory; native config/tool-registration/lease break-point tracing; installed Codex/Claude/Qoder CLI probing (`--version`, `--help`, official read-only status/doctor, local protocol-schema generation); Python package metadata; local IPC and byte-range-lock capability probes; test-suite collection/execution evidence. Out of scope and not performed: no source/test/doc/config edit; no `serverfs.toml`/`.env`/provider config change; no install/uninstall/upgrade of anything (including `uv sync` in `agent_bridge/`); no provider login/logout/refresh; no model-default or permission change; no commit/branch/tag/push; no production ServerFS/Tunnel start-stop; no Windows service/scheduler/registry/system-env change; no elevation; **no provider model inference** (therefore no live agent turn, no approval round-trip, no `submit_agent_task` against a real runtime); no reading of credential file contents (`~/.codex/auth.json`, `~/.claude/settings.json`, `~/.qoder/*` were checked for **existence and file names only**). Redactions applied by policy: workdir host roots → aliases only; proxy endpoint → not recorded; account identifiers → not recorded; `ANTHROPIC_BASE_URL` value → not recorded; all env vars → `set`/`unset` only. One safety note worth recording: a first attempt to enumerate the *key names* of `~/.claude/settings.json` was refused by the harness classifier. The refusal was correct and the file was not read; the same fact (a custom endpoint is configured) was obtained from `claude doctor`'s own output instead. --- ## 3. Git / Repository Baseline ``` git status --short ?? .workbuddy/ ?? debug.log git branch --show-current main git rev-parse HEAD 2616d5aa23cce2c5a993d7fc9fea8fc778a3f920 git log -1 --oneline -decorate 2616d5a (HEAD -> main, origin/main, origin/HEAD) feat(windows): add native deployment layout under deployment/windows git describe --tags --always --dirty v0.10.0-2-g2616d5a git remote -v origin git@github.com:NTLx/ServerFS_MCP.git (fetch/push) ``` - Branch `main`, HEAD = `2616d5a`, 2 commits after tag `v0.10.0`; `describe` shows no `-dirty`, so tracked content matches HEAD. `origin/main` == HEAD (nothing to push). - Working tree contains **two pre-existing untracked entries that this task did not create and did not touch**: `.workbuddy/` (local agent memory; contains a prior note that independently reached the POSIX-only conclusion) and `debug.log`. Both left in place. - Repository layout relevant to v0.11: `agent_bridge/` (separate uv project, own `pyproject.toml` + `uv.lock` + `tests/`), `src/serverfs_mcp/` (MCP product layer + both platform backends), `native/windows/` (Rust kernel), `deployment/{agent-bridge,native,tunnel,windows}/`, `tests/` (root suite), `docs/`, `site/`. - There is **no `dev_plan_v0.11.md` and no `v0.11` marker anywhere** in `*.md`, `*.py`, `*.rs`, `*.toml`, `*.yml`, `*.sh`, `*.ps1`. The only forward statement is `dev_plan_v0.10.md:1589-1601`. `VERIFIED`. --- ## 4. Windows Host and Toolchain | Item | Value | Status | |---|---|---| | OS | Windows 11 Professional (zh-CN), `10.0.26200` build 26200, 64-bit | VERIFIED | | Arch | AMD64 / x64 | VERIFIED | | Elevation | process is **not** elevated (`IsInRole(Administrator)` false) | VERIFIED | | Shell context | native Windows; `wsl.exe` present on host but repo/python are native (`sys.executable` under `%APPDATA%\uv\python\...`) | VERIFIED | | Repo volume | drive D: `FileSystemType=NTFS`, `HealthStatus=Healthy` | VERIFIED | | PowerShell | 5.1.26100.9444 (Windows PowerShell; no pwsh probe performed) | VERIFIED | | Python | 3.13.3 (MSC v.1943 64-bit AMD64), uv-managed, `C:\Users\lx\AppData\Roaming\uv\python\cpython-3.13.3-windows-x86_64-none\python.exe` | VERIFIED | | Project venv | `.venv` present, `serverfs-mcp` installed **editable** (`_editable_impl_serverfs_mcp.pth`) | VERIFIED | | uv | 0.7.3 (`~/.local/bin/uv`) | VERIFIED | | git | 2.46.0.windows.1 | VERIFIED | | gh | 2.102.0 | VERIFIED | | node / npm | v22.16.0 / 10.9.2 (npm global root `D:\Software\nvm\nodejs`) | VERIFIED | | cargo / rustc | 1.90.0 / 1.90.0 present — recorded as environment fact only; v0.10 runtime does not depend on them | VERIFIED | | npm global packages | `@playwright/cli`, `agent-browser`, `corepack`, `npm` — **no** `@openai/codex`, **no** `@anthropic-ai/claude-code` | VERIFIED | Python architecture facts that drive §8/§15/§16 (all measured in the project venv): ``` socket.AF_UNIX False socket.SO_PEERCRED False asyncio.open_unix_connection False asyncio.start_unix_server False os.O_DIRECTORY / os.O_NOFOLLOW False os.getuid/geteuid False os.chown False os.listxattr False msvcrt.locking True pywin32 312 installed (win32pipe/win32file) ``` `serverfs-mcp` package: `0.10.0`. `serverfs-windows-native`: `0.10.0`; the abi3 wheel is the live backend — `src/serverfs_mcp/backends.py:30` is the only product-path import of `serverfs_windows_native`, and the module surface is `open_workdir` + `NativeSessionError` (measured via `dir()`; there are **no** submodules such as `.mutation` / `.session`). `VERIFIED`. --- ## 5. v0.10 Native ServerFS Baseline Commands (all through `uv run`, exit codes captured): | Check | Result | Status | |---|---|---| | `serverfs --version` | `serverfs 0.10.0`, exit 0 | VERIFIED | | `serverfs --help` | subcommands `serve`, `doctor`, `tunnel`, `bootstrap` only | VERIFIED | | `serverfs serve --help` | `--config CONFIG` is the **only** option; help text: "operator configuration; never agent input" — no agent/bridge/runtime flag exists | VERIFIED | | `serverfs doctor --config serverfs.toml` | exit 0, `summary: 0 FAIL, 0 WARN` | VERIFIED | Doctor detail (host roots redacted; aliases are the agent-visible identity): - `config: OK (serverfs.toml)`; `native backend: OK -- serverfs-windows-native 0.10.0` — Python/package versions match (`serverfs-mcp 0.10.0` ↔ `serverfs-windows-native 0.10.0`). - Two workdirs, both `read-write`: alias `serverfs-mcp` and alias `cmtools`. For each: `reparse: OK -- not a reparse point`, `filesystem: OK -- NTFS`, `root: OK -- opened`, `read: OK -- root listing succeeded`, `write: OK -- add-file capability present (nothing was created)`. - `tunnel-client: OK -- 0.0.15+a390c168ff1b2d14e73a95991c186c6aba3ff5a0 (bootstrap directory)` — pinned launcher present and version-reported, matching the v0.10 pin. - `proxy: enabled http ... auth=none`; `proxy reachability: OK -- TCP connect succeeded`. Only enabled/state/auth-mode recorded; endpoint not recorded. No proxy env vars are set in the shell (`SERVERFS_PROXY_HOST: unset`), i.e. proxy comes from the untracked local `.env`. - Doctor has **zero** agent-related checks (no `agent` token in `doctor.py`). `VERIFIED`. Root gates executed on this Windows checkout: | Gate | Result | |---|---| | `uv run ruff check .` | `All checks passed!` — VERIFIED | | `uv run ruff format --check .` | `183 files already formatted` — VERIFIED | | `uv run pytest -q` (full, as-is) | **FAIL — 12 collection errors, "Interrupted: 12 errors during collection"** — VERIFIED | | `uv run pytest` on 15 Windows-targeted files | **PASS — `359 passed, 3 skipped in 12.95s`** — VERIFIED | | `uv run pytest` on remaining collectible tests | 941 collected; **122 failed + 3 errors** (see §19) — VERIFIED | | `uv sync --frozen` / `docker compose config` / image build | NOT RUN (Docker gate is not the subject; `uv sync` in `agent_bridge/` would mutate env → out of boundary) | | `bash -n deployment/agent-bridge/*.sh` | NOT RUN (Linux-only deployment scripts; not changed by this task) | The full-suite collection failure is the single most visible fact: **this repository's root gate is not currently executable on Windows**, independent of v0.11. --- ## 6. Existing Agent Bridge Architecture Actual module inventory, `agent_bridge/src/serverfs_agent_bridge/` (LOC from the files themselves): | Module | LOC | Role | |---|---|---| | `service.py` | 1715 | Bridge orchestration: submit/get/events/result/approve/answer/steer/cancel, leases+guards, redaction | | `store.py` | 778 | SQLite tasks/events/pending_requests, migrations, idempotency unique index | | `preflight.py` | 643 | Jev advisory preflight/router/model-approval advisor (lazy client import) | | `config.py` | 475 | JSON config load + strict unknown-key rejection, per-runtime settings | | `protocol.py` | 432 | UDS JSON-lines RPC server, dispatch, peer check, socket/dir hardening | | `recovery.py` | 184 | Persistent per-slot active guards (`lock_dir/active/NN`) | | `result_spool.py` | 175 | Oversized-result spool + chunked read | | `policy.py` | 154 | `WorkdirAgentPolicy`, `PolicyRegistry.authorize`, `resolve_relative_cwd`, `redact_host_path` | | `models.py` | 152 | Enums + frozen records (`TaskStatus`, `TaskRecord`, `BridgeEvent`, `RuntimeInfo`) | | `leases.py` | 113 | `LeaseManager` / `WorkdirLease` cross-process writer lease (flock) | | `main.py` | 114 | CLI entry, adapter assembly, signal-driven shutdown | | `manifest.py` | 87 | Canonical-JSON immutable execution manifest + sha256 | | `state.py` | 48 | Allowed transition table + `validate_transition` | | `util.py` / `errors.py` / `__init__.py` | 41/20/3 | IDs, `BridgeError(code,msg)`, version | | `adapters/codex.py` | 963 | Codex app-server adapter | | `adapters/qoder.py` | 685 | Qoder Agent SDK adapter | | `adapters/claude.py` | 565 | Claude Agent SDK adapter | | `adapters/codex_transport.py` | 300 | JSON-RPC over WebSocket **over AF_UNIX** to the Codex daemon | | `adapters/fake.py` / `base.py` / `__init__.py` | 145/109/17 | Deterministic test runtime; `AgentAdapter` ABC; eager re-export of all adapters | Bridge project metadata (`agent_bridge/pyproject.toml:1-19`): name `serverfs-agent-bridge`, **version `0.9.0`** (root project is `0.10.0` — the Bridge was not bumped by v0.10), `requires-python >=3.12`, script `serverfs-agent-bridge = serverfs_agent_bridge.main:main`. Actual call chain (each hop verified at the cited line): ``` MCP tool definition src/serverfs_mcp/agent_tools.py:90,132,216,340,353,372,399,433,461,490 registration gate src/serverfs_mcp/main.py:88-98 (settings.agent_bridge_enabled AND any workdir.agent_mode != "disabled") client call src/serverfs_mcp/agent_client.py:42 call -> :79 _exchange -> :83 asyncio.open_unix_connection (protocol_version=1 at agent_client.py:11,47) Bridge transport agent_bridge/.../protocol.py:51 start -> :54 asyncio.start_unix_server protocol.py:240 _check_peer -> :242 hasattr(socket,"SO_PEERCRED") -> getsockopt protocol.py:254 _dispatch_raw -> :288 _dispatch Service service.py:157 list_runtimes, :170 list_models, :272 submit_task, :710 get_task, :733 read_result, :760 read_events, :825 respond_approval, :896 answer_question, :934 send_message, :963 cancel_task, :1004 _run_task Store / lifecycle store.py:196 create_task, :286 transition_task (+ state.py:42), :379 append_event, :462 create_pending_request, :522 resolve_request, :697 interrupt_nonterminal_tasks Lease + guard leases.py:35 LeaseManager -> :67 _prepare_lock_files -> :91 acquire_exclusive -> :106 fcntl.flock recovery.py:25 guard_dir ; consumed MCP-side by tools.py:131-142 -> agent_leases.py:21-71 Adapters adapters/base.py:51 AgentAdapter -> codex.py:48, claude.py:53, qoder.py:51, fake.py:12 ``` `AgentAdapter` contract (`adapters/base.py`): `name`(52), `probe()`→`RuntimeInfo`(58), `list_models()` default "unsupported"(61-69), `run_task`(71), `continue_task`(75), `get_state`(79), `respond_approval`(82)/`answer_question`(86) — **defined but implemented and called by no real adapter**: approvals/questions flow through `TaskContext` callbacks (`emit_event`, `request_approval`, `ask_question`, `abandon_interaction`, `record_native_ids`, base.py:20-34), plus `reconcile`(92), `reconcile_task`(95), `send_message`(102), `cancel`(105), `close`(108). Ten Agent MCP tools exist today (not eight), in registration order (`agent_tools.py`): `list_agent_runtimes` (def :90, RPC `runtime.list` :102), `list_agent_models` (def :132, `runtime.models` :187), `submit_agent_task` (def :216, `task.submit` :306), `get_agent_task` (def :340, `task.get` :347), `read_agent_task_events` (def :353, `task.events` :366), `read_agent_task_result` (def :372, `task.result.read` :387), `respond_agent_approval` (def :399, `task.approval.respond` :425), `answer_agent_question` (def :433, `task.question.answer` :449), `send_agent_message` (def :461, `task.message.send` :483), `cancel_agent_task` (def :490, `task.cancel` :497). Decorators (`@mcp.tool(...)`) sit one line above each def (:89,131,215,339,352,371,398,432,460,489). `VERIFIED`. --- ## 7. Portability Matrix | Component | Current implementation (file:line) | Provider-neutral? | Linux-specific? | Reusable on Windows as-is? | v0.11 needs | |---|---|---|---|---|---| | Task model / state machine | `models.py:10-34,90-122`; `state.py:8-48` | yes | no | **yes** | nothing | | Event normalization, schema-versioned envelopes, correlation IDs | `models.py:141-152`; `store.py:379-427`; `service.py:1272-1282,1702` | yes | no | **yes** (logic) | note `service.py:1299,1317` uses `Path(...).is_absolute()` → Windows-separator-aware; audit only | | Immutable execution manifest | `manifest.py:13-87` (schema v2, canonical JSON, `policy_fingerprint`) | yes | no | **yes** | nothing | | Result spool | `result_spool.py:21-162`; `service.py:733-758,1104-1150` | yes | **partly** — dir/file ownership+mode guards `:27-33,100-106` | logic yes, guards no | NTFS-ACL/owner equivalent for the two guards | | Idempotency | `service.py:318-321,410-413,1478-1506,1523-1528,1688`; `store.py:182-188,257-268` | yes | no | **yes** | nothing | | Approvals | `service.py:825-894,1368-1463`; `models.py:59-63`; `store.py:462,522,622` | yes | no | **yes** | nothing | | Questions | `service.py:896-933,1627`; provider maps `codex.py:486-663`, `claude.py:443-530`, `qoder.py:427-587` | yes | no | **yes** | nothing | | Model discovery | `service.py:170-270`; `codex.py:82-117`; `qoder.py:107-150`; `claude.py:108-115` | yes | no | **yes** | re-prove against installed SDKs | | Model override (per task) | `service.py:1509-1520`; forwarded `codex.py:319-337`, `claude.py:233`, `qoder.py:264` | yes | no | **yes** | nothing | | Persistence (format) | `store.py:34-56` SQLite+WAL, JSON guards | yes | no | **yes** | nothing | | Persistence (path + perms) | `config.py:241-249` defaults; `store.py:43-45`; `recovery.py:37-49` | — | **yes** | **no** | Windows data-home + ACL guards (§18) | | Bridge RPC (envelope, limits) | `protocol.py:18-21,267-286`; `agent_client.py:11-13,46-51,105-131` | yes | no (framing only) | **yes** | reuse framing; swap the listener | | Local IPC | `protocol.py:54` `start_unix_server`; `agent_client.py:83` `open_unix_connection` | — | **yes** | **no** | named-pipe server+client pair (measured available, §15) | | Peer identity | `protocol.py:240-252` `SO_PEERCRED` + uid/gid compare; defaults `:38-47` | — | **yes** | **no** | pipe client-PID + SID/token check (measured available, §15) | | Writer lease (Bridge side) | `leases.py:5,35-113` flock, pre-created `NN.lock` for slots 1..16, `O_NOFOLLOW`, chown/chmod | yes (policy) | **yes** (primitive + keying) | **no** | `msvcrt`/`LockFileEx` equivalent keyed by alias (measured available, §16) | | Writer lease (MCP side) | `agent_leases.py:11,21-71` read-only open + flock; hook `tools.py:131-142` | hook neutral | **yes** | **no** | Windows reader of the same lease objects | | Recovery guard | `recovery.py:25,51-139,152-160`; `service.py:554-708` | yes | partly (uid/chown/chmod, `getattr(os,"O_NOFOLLOW",0)` already defensive) | logic yes | guard-dir authorization equivalent on NTFS | | Process lifecycle | `main.py:71-78` `add_signal_handler` (already `NotImplementedError`-tolerant); provider children are SDK-owned; `codex.py:800-808` daemon start | yes | mostly not | **yes**, but no containment | Job Object / tree-kill (absent, §17) | | Provider adapters | `codex.py`+`codex_transport.py`, `claude.py`, `qoder.py` | base yes; adapters provider-specific | Codex transport **yes** (AF_UNIX); Claude/Qoder no (SDK-level) | Codex **no** (transport), Claude blocked by SDK wheel, Qoder likely yes | Codex transport seam; SDK installs | | Deployment / lifecycle manager | `deployment/agent-bridge/{install.sh,serverfs-agent-bridge.service,render_config.py,verify_host.py,measure_peercred.py}`, `compose.agent.yml` | — | **yes entirely** | **no** | native Windows launch story (no systemd) | --- ## 8. Linux/POSIX Assumptions Bucketed as instructed. Nothing here is a mechanical grep dump; each row states the consequence. **A. Deployment-only Linux (does not block Windows import or runtime logic).** `deployment/agent-bridge/install.sh` (`systemctl --user` :81-95,338-354,397; `XDG_RUNTIME_DIR=/run/user/$(id -u)` :87-90; `$HOME/.local/{share,state}` :95-108), `rollback_app.sh:13-30,80-123`, `serverfs-agent-bridge.service:11-25` (`KillMode=control-group` :25), `verify_host.py:69-71,204-210`, `measure_peercred.py:33,54,68,99-101`, `render_config.py:181-186,404`, `compose.agent.yml:17-19,56-68` (binds `/run/serverfs-agent-bridge`, `/run/serverfs-agent-locks`), `compose.yml` (`user:`, `/workdirs/NN`, `read_only:`, `tmpfs:/tmp`, `cap_drop`), `Dockerfile:20-38` (ripgrep apt, `useradd -u 10001`, `USER 10001:10001`), `deployment/tunnel/tunnel-launcher.sh:1` (`#!/bin/sh`), and the bash-driven deployment tests `tests/test_agent_deployment.py:708-825,934,997`. **B. Inside Bridge core (executed regardless of deployment).** `leases.py` whole file; `protocol.py:39-162,240-252`; `recovery.py:26-49,152-160`; `result_spool.py:27-61,102`; `store.py:41-86`; `main.py:71-78`; `config.py:69-88,241-249`; `policy.py:132` (`PurePosixPath` for `relative_cwd`); `service.py:1468-1475` (same grammar); `codex_transport.py:15,68-79`; `adapters/codex.py:796-832`; `adapters/claude.py:80-97,549-553`; `adapters/qoder.py:79-88,121,643-647`. **C. Hard Windows import blockers (measured absent on this host).** - `import fcntl` — two production sites: `agent_bridge/.../leases.py:5` (reached eagerly: `main.py:12` → `service.py:14`, so **the Bridge cannot even import**) and `src/serverfs_mcp/agent_leases.py:11` (reached lazily from `tools.py:138-140`, only when `agent_bridge_enabled`, so an Agent-disabled native process is safe). Measured: `ModuleNotFoundError: No module named 'fcntl'`. Tests hit by the same: `tests/test_agent_leases.py:5`, `tests/test_binary_upload.py:8`. - `socket.AF_UNIX` / `socket.SO_PEERCRED` — `protocol.py:242,245`; `agent_client.py:83`; measured both attributes absent. `protocol.py:242` uses `hasattr(...)` so `_check_peer` **fails closed** rather than raising — good, and it means a Windows port must not lose that branch. - `asyncio.start_unix_server` / `asyncio.open_unix_connection` — `protocol.py:54,86`, `agent_client.py:83`; measured absent from `asyncio` on Windows. - `os.O_DIRECTORY` / `os.O_NOFOLLOW` at import — `src/serverfs_mcp/fdio.py:34-36` (module-level constants) → **`import serverfs_mcp.fdio` raises AttributeError on Windows**. This is the single cause of 8 of the 12 test collection errors. - `os.getuid/geteuid/getgid` — `protocol.py:39,44,80,113,149`; `leases.py:51`; `recovery.py:37`; `store.py:43`; `result_spool.py:31,103`; module-scope in `tests/test_create_text_file.py:172` and `tests/test_delete.py:183` (inside `skipif` → collection error). - `os.chown/fchown` — `leases.py:61,84`; `protocol.py:64,156`; `recovery.py:48,160`; `mutations.py:205`. `os.listxattr/getxattr/setxattr` — `mutations.py:223,231,237`. All measured absent. - `pwd`, `grp`, `resource`, `multiprocessing`: **zero occurrences in `agent_bridge/`** (good). - `websockets.asyncio.client.unix_connect` (`codex_transport.py:15,68`): importability of `websockets` on Windows is `UNKNOWN` here (not installed); even if importable, `unix_connect` cannot function without `socket.AF_UNIX`. `INFERRED` blocker. **D. Windows runtime blockers (would import, behave differently).** - flock semantics: inode-keyed, advisory, released on process death, work on an `O_RDONLY` fd (`leases.py:106`, `agent_leases.py:48,69`, contract asserted `tests/test_agent_leases.py:171-199`). `msvcrt.locking` is byte-range and handle-keyed; measured usable and cross-process exclusive (§16) but **release-on-death and read-only-handle semantics are NOT TESTED**. - `st_uid`/`st_gid`/mode-bit authorization: every "refuse if world/group-writable or not mine" check degenerates. Measured on real files: `st_uid=0`, `st_mode=0o100777` on `.exe` files. Affected: `leases.py:49-64,80-87`, `protocol.py:78-81,112-114,137-162`, `recovery.py:33-49`, `store.py:41-53,84-86`, `result_spool.py:27-33,102`. - `st_ino`/`st_dev` as object identity (`protocol.py:60,83,114,182-188` stale-socket unlink guard): Windows returns emulated values (measured, e.g. `st_ino=53198770598470945`); reliability for identity is `NOT TESTED`. - Signal-based graceful shutdown: `main.py:71-78` already swallows `NotImplementedError`, so on Windows the Bridge would start with **no shutdown handler** — silent capability loss rather than a crash. There is no `os.kill`/`killpg`/`setsid`/`start_new_session`/`waitpid`/`prctl` anywhere, i.e. child cleanup is delegated entirely to systemd `KillMode=control-group`. - `/proc/self/fd/N`: single producer `fdio.py:217-220 proc_fd_path()`, consumed only by `search.py:149,153` (`cwd=` + `pass_fds=(root_fd,)` for `rg`). Contained in the Linux path; `windows_backend.py:247-360` is an in-process native walk with **no rg.exe**, and `/proc/mounts` (`doctor.py:150`) is already guarded by `doctor.py:85-88`. `selectors` on pipe FDs (`search.py:160-162`) is likewise Linux-path-only. - Fixed-slot identity: `leases.py:68-69,94` (`{slot:02d}.lock`, 1..16), `agent_leases.py:37,54`, `recovery.py:25` (`active/{slot:02d}`), `workdirs.py:20,23` (`SLOT_COUNT=16`, `WORKDIR_ROOT=/workdirs`). - `~`-relative provider homes: `config.py:69-73` (`CODEX_HOME` else `~/.codex`), `:88` (`codex_home/app-server-control/app-server-control.sock`), `qoder.py:121` (`Path.home()`). - AF_UNIX `sun_path` ~107-byte limit baked into test helpers (`agent_bridge/tests/test_codex_adapter.py:30`, `tests/test_agent_deployment.py:437`) — disappears with a pipe/loopback transport. - `relative_cwd` is validated as `PurePosixPath` (`policy.py:132`, `service.py:1469-1475`). This is the **public Agent tool contract**, so it is a decision (§22), not a bug to "fix". **E. Only a platform adapter is needed — the seam already exists.** `backends.py:197-214` (filesystem kernel dispatch, already win32/linux); `tools.py:130-142` (`_mutation_lease`, lazy + Agent-gated; docstring already says "the future non-Linux kernel"); `main.py:88-98` (lazy Agent registration); `concurrency.py:16-28` (process-neutral `mutation_lock`); `leases.py:35,67,91` (exactly three public members to reimplement); `protocol.py:51 start()` + `:240 _check_peer()` (transport+identity isolated on one class); `tunnel_bootstrap.py:64-77 serverfs_data_dir()` (existing Windows data-home convention); `native_config.py:130-150` (absolute-path lexicity handled for both platforms); the `getattr(os,"O_NOFOLLOW",0)` pattern (`leases.py:73,98`, `recovery.py:152`, `result_spool.py:40,91`) is already the in-tree precedent for a defensive platform shim. **F. Contracts that must not change while porting.** Peer UID/GID is **measured, never synthesized** (`AGENTS.md:57-79`; enforced `protocol.py:240-252`, `config.py:250-251`) — the Windows equivalent must keep "measure then assert", not "assert then trust". Socket 0660 / dir 0750 / lock 0640, container never creates lock files (`AGENTS.md:82-86`; `protocol.py:61-71,141-162`, `agent_leases.py:38-40`, `tests/test_agent_leases.py:171-199`). `PROTOCOL_VERSION=1`, exactly four top-level request fields, 1 MiB caps, `request_id` echo (`protocol.py:18-21,267-286`; `agent_client.py:11-13,46-51`). flock+guard as sole writer-lease authority with live-lease precedence and fail-closed `WORKDIR_RECOVERY_REQUIRED` (`dev_plan_v0.7.md:89-104`, `dev_plan_v0.7.3.md:526`). `SERVERFS_AGENT_BRIDGE_ENABLED` default false + 11-tool base surface (`config.py:39`, `main.py:88-98,143-148`). Provider authorization/approval semantics and Jev advisory-only (`AGENTS.md:104-124`). Host paths never logged (`policy.py:109-124 redact_host_path`). User-scoped deployment only, no service/system dirs (`AGENTS.md:63-67`) — note v0.10 already lists "Windows Task Scheduler or Windows Service installation" as a non-goal (`dev_plan_v0.10.md:91`; the adjacent non-goals are `:88` Windows Agent Bridge, `:89` Windows Codex/Claude/Qoder runtime integration, `:90` Windows writer-lease replacement, `:92` native file-parameter ingress). Model-selection invariant (`AGENTS.md:44`). No generic shell/argv/env tool (`AGENTS.md:116`). --- ## 9. Windows Native Config and Agent-Surface Gaps ### A. Native configuration currently has no Agent policy fields Status: **VERIFIED** Evidence: `serverfs.toml.example:17-47` declares only `[server] log_level`, `[defaults] max_read_bytes, max_read_lines, max_write_bytes, allow_hidden, binary_transfer_enabled, max_binary_transfer_bytes`, and `[[workdirs]] alias, path, description, read_only`. Grepping the example for `agent|bridge|runtime` returns only prose comments and example `path` values. Parsers: `native_config.py:45-46` (`NativeServerSettings`), `:50-58,94-101` (`NativeDefaults` known-key set), `:158,196-215` (`Workdir`, "Native workdirs carry no legacy slot"), `:236-243` (only `server`/`defaults`/`workdirs` are read). Two important asymmetries in unknown-key handling: unknown `[defaults]` keys (`:102-104`) and unknown `[[workdirs]]` keys (`:158-161`) are **hard errors**, while an unknown **top-level table** (e.g. `[agent]`) and unknown `[server]` keys are **silently ignored** (`:236-243`, `:79`) — there is no `_reject_unknown_keys` at that scope, unlike the Bridge's own `config.py:153`. Impact: native MCP startup cannot derive per-workdir Agent authorization from TOML today; and adding an `[agent]` section later is a **breaking** change in one direction (previously-ignored typo becomes a startup error) and a **no-op** in the other (a `[agent]` table in an existing file parses fine and does nothing). Both must be designed explicitly. `EffectiveWorkdirPolicy.agent_mode/agent_runtimes` keep `disabled`/`frozenset()` in the native path (`native_config.py:205-214`; defaults `workdirs.py:62-63`), while the Linux env surface has `SERVERFS_AGENT_MODE`/`SERVERFS_AGENT_RUNTIMES` (`config.py:113-129`), `SERVERFS_AGENT_BRIDGE_ENABLED/_SOCKET/_TIMEOUT_SECONDS/…_LOCK_DIR` (`config.py:36-42,166-173`) and per-workdir `WORKDIR_XX_AGENT_MODE/_AGENT_RUNTIMES` (`workdirs.py:347-359,393-394`). ### B. `serverfs serve` on Windows cannot register Agent tools — five distinct blockers Status: **VERIFIED** (each mechanism separately confirmed by reading and/or measuring) | # | Blocker | Kind | Evidence | |---|---|---|---| | 1 | Gate is false by construction: `cli.cmd_serve` builds `Settings(log_level=...)` only → `agent_bridge_enabled=False`; every native workdir `agent_mode="disabled"` | fails closed by policy | `cli.py:119`; `config.py:39`; `main.py:88-98`; `native_config.py:205-214` | | 2 | If the flag were forced, `agent_tools.py:22` imports `fdio`, and `fdio.py:34` evaluates `os.O_DIRECTORY \| os.O_NOFOLLOW` at import → **AttributeError, not a clean refusal** | hard crash on Windows | measured `hasattr(os,'O_DIRECTORY')==False`; `fdio.py:34-36`; the same crash produces 8 of 12 collection errors | | 3 | `agent_leases.py:11 import fcntl` → ModuleNotFoundError | hard crash (lazy) | measured; `tools.py:138-140` | | 4 | `agent_client.py:83 asyncio.open_unix_connection` — attribute absent on Windows | hard crash at first RPC | measured `hasattr(asyncio,'open_unix_connection')==False` | | 5 | Lease keying: `tools.py:142` passes `resolved.workdir.slot`; native `legacy_slot=None` → `slot` returns sentinel `0` (`workdirs.py:134-147`) and `mutation_agent_lease` rejects anything outside 1..16 (`agent_leases.py:32-33`) | structural | code comments state this is deliberate in v0.10 | | 6 | Bridge-side: `agent_bridge/.../config.py:154-156` requires slots 1..16; peer-cred at `protocol.py:242-245` | structural + platform | source | `agent_tools.py:561-580 _validate_agent_cwd` additionally calls `root_fd(str(wd.container_path))` (`:570`), i.e. the Agent tool layer re-opens the workdir root through fdio — a seventh Linux-only dependency inside the tool layer itself. ### C. Writer lease: how Linux prevents concurrent ServerFS mutation vs workspace-write Agent Full Linux chain, `VERIFIED`: Bridge holds the lease for the whole active turn (`leases.py:91-106`, `fcntl.flock(LOCK_EX|LOCK_NB)` on `lock_dir/{slot:02d}.lock`, files pre-created by the Bridge at `:67-89`). Every ServerFS mutation takes the process-wide `mutation_lock()` **and** `_mutation_lease(settings, resolved)` (`tools.py:1026-1027` plus `:1072,1124,1172,1203,1240`), which re-opens the *same* pre-existing lock file **O_RDONLY without O_CREAT** (`agent_leases.py:37-48`) and attempts `flock(LOCK_EX|LOCK_NB)`; `BlockingIOError` → `WorkdirBusyError` (`:49-50`); a live `active/{slot:02d}` guard file with no owner → `WorkdirRecoveryRequiredError` (`:54-64`). So the mutual exclusion is a single shared inode that both processes flock. Windows status: **absent**. The hook is platform-neutral and present (`tools.py:131-142`) but is permanently `nullcontext()` on native (`agent_bridge_enabled` false, `cli.py:119`). Native mutations serialize only on the process-local `threading.RLock` (`concurrency.py:15`, used at `windows_backend.py:393,436,524,544,561`) — no cross-process guarantee at all. The Rust kernel holds `FILE_SHARE_READ` during commit (`native/windows/src/mutation.rs:42`, `REPLACEMENT_HELD_SHARE` `:51`, share constants in `ffi.rs:156-160`), which blocks external write opens *only for the commit window* and is not a persistent named lease. Grepping `src/` and `native/windows/` for `msvcrt|LockFileEx|CreateNamedPipe|win32pipe|Job|CREATE_NEW_PROCESS_GROUP|creationflags` returns **zero** hits. `mutations.py` (the fdio+flock pipeline) is not on the Windows path at all. `VERIFIED`. ### D. Workdir identity: the native model is already the right anchor Status: **VERIFIED** — identity is the **alias** (`workdirs.py:167-171` `_by_alias`; `list_workdirs` exposes aliases only); native has no slot concept (`native_config.py:196-215`) and allows up to 64 workdirs (`native_config.py:37`); `WindowsBackend.open_session()` keys `(alias, str(root), read_only)` and **retains the native root handle for the process lifetime** (`windows_backend.py:587-600`, lifecycle contract `:7-11`), and `identity_token()` returns a handle-derived object identity (`:138-143`), behind the neutral `backends.WorkdirSession` / `FilesystemBackend` protocol (`backends.py:108-194`) selected by `get_backend()` on `sys.platform == "win32"` (`backends.py:208-211`). Judgement: a Windows Bridge can reuse `WindowsWorkdirSession`/`identity_token` as the host-side identity anchor — the objects exist and are path-agnostic. It **cannot** reuse the numeric-slot lease keying (§9C) or `agent_tools._validate_agent_cwd`'s fdio root probe (§9B.7). The fixed `/workdirs/01..16` assumption lives only in the env adapter (`workdirs.py:20-23,252-257`) + compose/deployment, **not** in native code — consistent with `dev_plan_v0.10.md:1601` ("must build on the native workdir identity/model rather than restoring fixed Docker slots as a core concept"). --- ## 10. Codex Runtime ### 10.1 Installation and provenance Status: **VERIFIED** - `Get-Command codex -All` → single entry: `C:\Users\lx\AppData\Local\Programs\OpenAI\Codex\bin\codex.exe` (PowerShell reports `Version 0.0.0.0`; `where.exe codex` → same single path). No multi-version conflict. - `codex --version` → `codex-cli 0.159.2`. - `codex doctor` self-reports install channel: **standalone Windows package**, `~\.codex\packages\standalone\releases\0.159.2-x86_64-pc-windows-msvc`, "managed by npm: no · bun: no · pnpm: no"; `npm ls -g --depth=0` contains no `@openai/codex`. → **not** an npm install. - Bundled search provider present: `codex-path\rg.exe` (bundled, readiness OK). - Locale/env facts from doctor: `LC_ALL=C.UTF-8`, `LANG=C.UTF-8`, system locale zh-CN. - Update available: daemon-managed app-server is newer than the CLI (see below). ### 10.2 App Server capability vs the current `CodexAdapter` Status: **VERIFIED against the installed version**, by two independent read-only methods: (a) `--help` of the installed CLI, (b) the protocol JSON Schema generated locally by the installed CLI (`codex app-server generate-json-schema --experimental --out %TEMP%\codex_schema_probe`, 47 files, exit 0; temp dir deleted afterwards). No provider call, no inference, no state change. | Adapter assumption (coded) | Installed reality | Verdict | |---|---|---| | App server exists | `codex app-server` (+ `daemon`, `proxy`, `generate-ts`, `generate-json-schema`) | MATCH | | Daemon capability | `app-server daemon {bootstrap,start,restart,update,enable-/disable-remote-control,stop,version}`; `codex app-server daemon version` → `{"status":"running","backend":"pid",...}` | MATCH (daemon is **already running** on this host) | | Transport = WebSocket **over AF_UNIX** to `codex_home/app-server-control/app-server-control.sock` (`codex_transport.py:15,68-79`; `config.py:88`) | daemon reports `socketPath = C:\Users\lx\.codex\app-server-control\app-server-control.sock`; a 0-byte `app-server-control.sock` exists | CHANGED-FOR-PLATFORM: the *endpoint* exists; the *client primitive* does not — `socket.AF_UNIX` measured absent, so Python (and `websockets.unix_connect`) cannot reach it | | Alternative transports offered | `app-server --listen` supports `stdio://` (default), `unix://[PATH]`, `ws://IP:PORT`, `off`; `codex --remote` accepts `ws://`, `wss://`, `unix://` | NEW SEAM (Windows-viable in principle; NOT TESTED) | | stdio↔socket relay | `codex app-server proxy --sock ` "Proxy stdio bytes to the running app-server control socket" | NEW SEAM (Windows-viable in principle; NOT TESTED) | | `initialize` + `capabilities.experimentalApi` + notify `initialized`; consume `userAgent`, `codexHome` | `initialize` in ClientRequest (167 methods); `initialized` is the only ClientNotification | MATCH | | `thread/start {cwd, serviceName, model?}` | `ThreadStartParams` contains `cwd`, `serviceName`, `model` (+ `sandbox`, `permissions`, `approvalPolicy`, `runtimeWorkspaceRoots`, `threadSource`, …) | MATCH (superset) | | `thread/resume {threadId, cwd, model?}` (required `threadId`) | same fields present | MATCH | | `turn/start {threadId, input, cwd}` (required `input, threadId`) | `TurnStartParams` required `['input','threadId']`, has `cwd`, `model`, `effort`, `approvalPolicy` | MATCH | | `turn/steer {threadId, input, expectedTurnId}` | required `['expectedTurnId','input','threadId']` | MATCH | | `turn/interrupt {threadId, turnId}` | method present | MATCH | | `thread/read {threadId, includeTurns}` | `ThreadReadParams` `['includeTurns','threadId']`, required `threadId` | MATCH | | `model/list {limit, includeHidden, cursor}` → `data[]`,`nextCursor` | `ModelListParams` `['cursor','includeHidden','limit']` | MATCH | | Notifications `turn/started`, `turn/completed`, `item/started`, `item/completed`, `error(willRetry)`; `serverRequest/resolved` | all present in ServerNotification (83 methods) / ServerRequest | MATCH | | Server→client approvals: `item/commandExecution/requestApproval` (`availableDecisions,reason,command,cwd,additionalPermissions,networkApprovalContext`), `item/fileChange/requestApproval` (`grantRoot`), `item/permissions/requestApproval` (`permissions`), `item/tool/requestUserInput` (`questions[{id,question,options[{label,description}],isOther,isSecret}]`) | all four present; `CommandExecutionRequestApprovalParams` = `additionalPermissions, approvalId, availableDecisions, command, commandActions, cwd, environmentId, itemId, kind, networkApprovalContext, proposedExecpolicyAmendment, proposedNetworkPolicyAmendments, reason, startedAtMs`; `FileChangeRequestApprovalParams` = `grantRoot,itemId,reason,startedAtMs,threadId,turnId`; `ToolRequestUserInputParams` = `autoResolutionMs,isBlocking,itemId,questions,threadId,turnId`; `ToolRequestUserInputQuestion` = `header,id,isOther,isSecret,options,question` (required `header,id,question`) | MATCH, with additive drift: new `approvalId`/`kind`/`commandActions`/`environmentId`/`proposedExecpolicyAmendment`/`proposedNetworkPolicyAmendments` on approvals and new required `header` + `autoResolutionMs`/`isBlocking` on questions | | Decisions `accept \| acceptForSession \| decline \| cancel` | `CommandExecutionApprovalDecision` oneOf starts with `enum:["accept"]`, `enum:["acceptForSession"]`, then **an additional variant for "apply the proposed execpolicy"** (truncated in the oneOf listing) | MATCH + NEW VARIANT. Risk: the adapter maps an unrecognized decision to `decline` (`codex.py:959-963`), so an execpolicy-amendment approval would degrade rather than fail loudly | | Version skew handling | CLI 0.159.2 vs running app-server 0.160.0 (managed daemon) | CHANGED (adapter parses `userAgent`; two versions in play on one host) | | Windows-specific provider features the plan does not use | `windowsSandbox/readiness`, `windowsSandbox/setupStart` ClientRequests; `windows/worldWritableWarning`, `windowsSandbox/setupCompleted` notifications | NEW (evidence that Codex itself supports Windows natively, and that a Windows readiness probe is available for `serverfs doctor`-style verification) | | Extra ServerRequests the adapter replies `-32601` to | `item/tool/call`, `mcpServer/elicitation/request`, `attestation/generate`, `currentTime/read`, `applyPatchApproval`, `execCommandApproval`, `account/chatgptAuthTokens/refresh` | UNKNOWN-impact (fail-with-error is the current designed behaviour) | ### 10.3 Authentication Status: **VERIFIED through a provider-supported read-only API.** `codex login status` (help: "Show login status") → `Logged in using ChatGPT`, exit 0. No login/logout/refresh executed, no credential file read. `~/.codex/auth.json` exists (name only, content not read). `CODEX_HOME` env: unset → Bridge default `~/.codex` applies (`config.py:69-73`). ### 10.4 Provider network health (real risk for v0.11 live work) `codex doctor` on this host reported: `⚠ websocket — Responses WebSocket timed out; HTTPS fallback may still work`; and `✗ reachability — one or more required provider endpoints are unreachable over HTTP`. Plus `⚠ security — Microsoft Defender can interfere with Codex` (exclusions not verified) and `⚠ git — this worktree is not on a Windows Dev Drive`. `VERIFIED` as observations; whether an actual model turn succeeds from this machine is **NOT TESTED** (out of boundary). This is a environment-level risk to schedule early, not a code defect. --- ## 11. Claude Runtime ### 11.1 CLI Status: **VERIFIED** - `Get-Command claude -All` → single entry `C:\Users\lx\.local\bin\claude.exe` (`Version 2.1.285.0`); `where.exe claude` → same. No multi-version conflict. - `claude --version` → `2.1.285 (Claude Code)`. - Provenance: **native standalone build**, not npm — `npm ls -g --depth=0` has no `@anthropic-ai/claude-code`; `claude doctor` self-reports `Running: native (2.1.285)`, `Platform: win32-x64`, `Config install method: native`, `Commit: afb212976052`, `Search: OK (bundled)`, `Auto-updates: disabled (set by env: DISABLE_AUTOUPDATER)`, `No installation issues found.` - CLI has first-class Windows support and read-only `doctor` + `auth status` + `agents`/`attach`/ `logs`/`stop` background-session commands (from `--help`). ### 11.2 Authentication Status: **VERIFIED via provider read-only API.** `claude auth status` → `loggedIn: true`, `authMethod: "api_key"`, `apiProvider: "firstParty"`, `apiKeySource: "ANTHROPIC_API_KEY"` (value not read; the shell env var `ANTHROPIC_API_KEY` is **unset** here, so the key is supplied from Claude's own configuration rather than this shell). `claude doctor` additionally reports that a **custom `ANTHROPIC_BASE_URL` is configured** (value not recorded) pointing away from `api.anthropic.com`, that `ANTHROPIC_BASE_URL` is **unset as a shell variable**, that Remote Control is unavailable under a custom endpoint, and that feature-flag traffic is disabled by `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`. `~/.claude/` exists with `settings.json`, `projects`, `sessions`, `tasks`, `teams`, `plugins`, `skills`, `file-history`, `history.jsonl`, `ide`, `security`, `shell-snapshots`, `paste-cache`, `backups`, `cache`, `security` (names only; no file contents read — one attempt to enumerate settings key names was refused and respected). Consequence for v0.11: live Claude validation on this machine would run against a **non-Anthropic custom endpoint** under an api_key auth mode; approval/Remote-Control semantics differ from the subscribed first-party path. Treat "Claude live E2E on WorkPC" as a separately scheduled experiment, not an assumption. ### 11.3 SDK Status: **NOT INSTALLED / BLOCKED (platform)** - `importlib.metadata.version('claude-agent-sdk')` in the project venv → `PackageNotFoundError`. `agent_bridge/.venv` does **not exist** on this machine (`ls` → No such file or directory), so the pinned dependency is installed nowhere here. - PyPI (queried read-only over HTTPS, no install): latest `claude-agent-sdk` = **0.2.163**, `requires_python >=3.10`, published files for that version are `py3-none-macosx_11_0_arm64`, `py3-none-macosx_11_0_x86_64`, `py3-none-manylinux_2_17_aarch64`, `py3-none-manylinux_2_17_x86_64`, and one sdist — **no Windows wheel**. `VERIFIED`. - The Bridge pins `claude-agent-sdk==0.2.156` (`agent_bridge/pyproject.toml:7`); the current line is 0.2.163, i.e. the pin is 7 patch releases behind, and the pin's platform tags are the same family. - Therefore every §13 "Claude SDK API" row is `NOT TESTED`: `ClaudeSDKClient`, `ClaudeAgentOptions`, `can_use_tool`, `interrupt`, `resume`, `permission_mode`, `setting_sources`, `PermissionResultAllow/Deny`, `PermissionUpdate`, `ToolPermissionContext` (imports at `claude.py:20-33`) could not be introspected because the package is absent and installing it is outside the boundary. - What **is** verifiable is the CLI-resolution helper the adapter uses, and it works on Windows: `shutil.which('claude')` → `...\claude.EXE`, `os.path.isfile` True, `os.access(path, os.X_OK)` True (`claude.py:549-553` logic measured healthy). The adapter's `--version` probe (`claude.py:80-89`) is the exact command verified in §11.1, so adapter *readiness probing* should pass; the *SDK session* path is the open question. - Model discovery: adapter hardcodes `status:"unsupported"` (`claude.py:108-115`), consistent with `AGENTS.md` ("Claude returns discovery `unsupported` until a stable native Claude Code account enumeration API exists"). No CLI-level enumeration API was found in `claude --help` for a read-only model catalog (only `--model`, betas, client-data-url). --- ## 12. Qoder Runtime ### 12.1 CLI Status: **VERIFIED** - Executable name on this host is **`qodercli`** (the task book's guess matches reality). `Get-Command qodercli -All` → single entry `C:\Users\lx\.qoder\bin\qodercli\qodercli.exe` (`Version 1.1.65.0`); `where.exe qodercli` → same. No multi-version conflict. - `qodercli --version` → `1.1.65`. - Provenance: installed under the Qoder user bin directory (`~\.qoder\bin\qodercli\`), not npm (npm global list has no qoder package). `qodercli` help shows `rollback`/`update` subcommands, i.e. self-managed version channel. - Capability-relevant CLI surface for the adapter: `--list-models`, `--list-sessions`, `-r/--resume`, `--session-id`, `--fork-session`, `--permission-mode {default, accept_edits, bypass_permissions, dont_ask, auto}`, `--allowed-tools/--disallowed-tools`, `--mcp-config`-adjacent `mcp` subcommand, `--tools`, `-w/--cwd`, `--config-dir`, `--attachment`, `--no-session-persistence`, `-p/--print`, `--output-format/--input-format`. `status` subcommand exists ("Show session status", `-o text|json`). - Auth: `qodercli status` (read-only) → authenticated, login method `browser`, auth source `local`, account type `personal_standard`. **Username/email/avatar omitted deliberately.** No token read, no login/logout. `~/.qoder/` names: `bin`, `settings.json`, `state.json`, `argv.json`, `mcp.json`, `installation_id`, `cache`, `logs`, `qoder.log`, `projects`, `sessions`(via `session-env`), `tasks`, `agents`, `skills`, `plugins`, `extensions`, `knowledges`, `memories`, `shell-snapshots`, `umid-cache.json`, `enterprise`, `security-resources`, `tmp`, `canvas`, `app`, `entry`, `events`, `external-commands`, `permission-match-for-bash`, `file-history` (existence/names only). - **Model catalog measured on this host**: `qodercli --list-models` → `Qwen3.8-Max`, `Qwen3.8-Flash` (exit 0, no inference invoked). Recorded as a catalog listing only. Per instruction, **no claim is made about any model being free**; the historical Qwen3.8-Flash free-tier statement in older docs is not treated as a current fact and `--list-models` output here carries no pricing field. ### 12.2 SDK Status: **NOT INSTALLED (availability: YES)** - `importlib.metadata.version('qoder-agent-sdk')` → `PackageNotFoundError`; no `agent_bridge/.venv`. - PyPI: latest `qoder-agent-sdk` = **1.0.15** — exactly the Bridge pin (`agent_bridge/pyproject.toml:8`) — and its release files include **`qoder_agent_sdk-1.0.15-py3-none-win_amd64.whl`** alongside macOS/manylinux/musllinux wheels. `VERIFIED` that a Windows wheel is published. - Consequently the Qoder adapter is the most plausible *second* runtime for native Windows, but the actual API surface is `NOT TESTED`: `QoderSDKClient`, `QoderAgentOptions`, `qodercli_auth`, `get_available_models`, `AssistantMessage`/`SystemMessage`/`ResultMessage`, `ToolPermissionContext`, `PermissionResultAllow/Deny`, `connect(prompt)`, `interrupt`, `resume`, `setting_sources=["user"]` (imports at `qoder.py:19-31`; usage at `:119-150,262,264,284,379-400,539,548,603-640,650-661`). - CLI-resolution helper works on Windows (measured: `shutil.which('qodercli')` → `...\qodercli.EXE`, `isfile` True, `X_OK` True — `qoder.py:643-647`, `:79-88`). - Adapter-side note that matters for v0.11 parity: Qoder model discovery calls `client.get_available_models()` with `cwd=Path.home()` (`qoder.py:119-121`) and `live_steer` is **disabled** because native `priority="now"` produces multiple Result boundaries (`qoder.py:166-179`, `dev_plan_v0.8.md:44`). --- ## 13. Runtime Capability Matrix Legend: `V` = VERIFIED on this machine this session; `I` = INFERRED from verified facts; `U` = UNKNOWN; `NT` = NOT TESTED (boundary); `—` = not applicable. | Capability | Codex | Claude | Qoder | |---|---|---|---| | CLI installed | V (`codex.exe`, 1 PATH entry) | V (`claude.exe`, 1 entry) | V (`qodercli.exe`, 1 entry) | | Version | V CLI 0.159.2 / daemon-managed app-server 0.160.0 | V 2.1.285 | V 1.1.65 | | Executable provenance | V standalone Windows package under `~\.codex\packages\standalone\releases\0.159.2-x86_64-pc-windows-msvc`; not npm | V native build, `Config install method: native`; not npm | V `~\.qoder\bin\qodercli\`, self-managed update/rollback; not npm | | SDK / protocol | I app-server JSON-RPC over WS/AF_UNIX (daemon alive, socketPath reported V); `websockets` not installed NT | `claude-agent-sdk` NOT INSTALLED; **no Windows wheel published V** | `qoder-agent-sdk` NOT INSTALLED; **win_amd64 wheel published V** | | Auth state safely verifiable | V `codex login status` → ChatGPT | V `claude auth status` → loggedIn/api_key/firstParty (+ custom base URL configured) | V `qodercli status` → browser login, local auth | | Persistent session | V `thread/*` methods present in installed schema; rollout/session stores under `~/.codex` (names only) | V `--resume`/`--session-id`/`projects`/`sessions` present | V `--resume`,`--session-id`,`--list-sessions`,`sessions` dir present | | Resume | V `thread/resume` in schema (required `threadId`) | U (SDK-level, not installed) | U (SDK-level, not installed) | | Approval | V 4 approval-related ServerRequests present with assumed fields | U | U | | User question | V `item/tool/requestUserInput` with questions/options/isOther/isSecret (+ new required `header`) | U | U | | Interrupt / cancel | V `turn/interrupt` present | U | U | | Model override (per request) | V `model` field on `ThreadStartParams`/`ThreadResumeParams`/`TurnStartParams` | U | U | | Model discovery | V `model/list` with `limit/includeHidden/cursor` | V CLI has no read-only model-enumeration command; adapter hardcodes unsupported | V `qodercli --list-models` works; SDK-level `get_available_models` NT | | Runtime daemon | V `app-server daemon` present, **status running** (backend `pid`) | — (no daemon; `--bg`/`agents` background sessions exist V) | V `remote-control` daemon subcommand exists (not started NT) | | Windows native | V `windowsSandbox/readiness`, `windowsSandbox/setupStart`, `windows/*` notifications in installed schema | V `Platform: win32-x64`, "No installation issues found" | V runs natively here | | Existing adapter appears compatible | I **protocol-level MATCH** (all methods/fields present) but **transport-level broken** (AF_UNIX client absent V) | U — cannot import SDK; CLI probe path measured healthy | U — cannot import SDK; CLI probe path measured healthy | | Known incompatibility | V transport: `socket.AF_UNIX` absent → `websockets...unix_connect` cannot work; `codex doctor` network reachability ⚠/✗ | V **platform packaging gap**: no Windows wheel for `claude-agent-sdk` | none found | | Free/cost assumptions | not assumed | not assumed | **explicitly not carried over** | --- ## 14. Python / SDK Dependency Matrix Two independent uv projects exist. Root: `pyproject.toml` (`serverfs-mcp 0.10.0`, `testpaths=["tests"]` `:34`, `addopts="-q"` `:35`, dev deps `:19-24`, no pytest-asyncio — root tests drive async via `asyncio.run`). Bridge: `agent_bridge/pyproject.toml` (own `uv.lock`, own `[dependency-groups] dev` with `pytest>=8.3`, `pytest-asyncio>=0.24`, `ruff>=0.8`; `[tool.pytest.ini_options] testpaths=["tests"]`, `asyncio_mode="auto"`, `addopts="-q"`). The root configuration therefore **never collects `agent_bridge/tests/`** — confirmed from `pyproject.toml:34`. `VERIFIED`. | Package | Required by | Declared pin | Installed in root `.venv` (Windows) | Installed in an agent_bridge venv | Imports on Windows | |---|---|---|---|---|---| | `serverfs-mcp` | root | self, 0.10.0 | 0.10.0 (editable) | n/a | yes (native profile) | | `serverfs-windows-native` | root (win32 extra) | — | **0.10.0** | n/a | **yes** (`open_workdir`, abi3 wheel is the live backend) | | `mcp` | root | `==2.2.0` | 2.2.0 | n/a | yes | | `pydantic` | root | — | 2.13.5 | n/a | yes | | `pytest` / `ruff` | root dev | — | 9.1.1 / 0.16.8 | n/a | yes | | `pywin32` | transitive: **`mcp` 2.2.0 on `sys_platform=='win32'`** (`uv.lock:398`, package row `uv.lock:615`) | — | **312** | n/a | yes (`win32pipe`, `win32file`) | | `claude-agent-sdk` | agent_bridge | `==0.2.156` | **NOT INSTALLED** | **NOT INSTALLED** (no venv) | NOT TESTED; **no Windows wheel on PyPI** | | `qoder-agent-sdk` | agent_bridge | `==1.0.15` | NOT INSTALLED | NOT INSTALLED | NOT TESTED; **win_amd64 wheel exists on PyPI** | | `websockets` | agent_bridge | `==17.1` | NOT INSTALLED | NOT INSTALLED | NOT TESTED (PyPI query timed out) | | `typesafe-sdk` (Jev) | agent_bridge | `==0.7.1` | NOT INSTALLED | NOT INSTALLED | NOT TESTED; latest 0.7.2 is `py3-none-any` → portable | | `uv` (as a package) | — | — | NOT INSTALLED (CLI only, 0.7.3) | — | n/a | Linux deployment installs these via the Bridge's own `uv sync` in `agent_bridge/` plus `deployment/agent-bridge/install.sh` user-scoped lifecycle; the MCP container never carries provider SDKs (it only speaks UDS RPC). On Windows **no** Bridge dependency set exists at all: no `agent_bridge/.venv`, and the root venv deliberately lacks `fcntl`-adjacent and provider packages. That absence is itself v0.11 input, recorded rather than fixed. `VERIFIED`. --- ## 15. Local IPC and Peer-Identity Findings ### 15.1 AF_UNIX on Windows — two separate questions, both answered Status: **VERIFIED (probe executed in this project venv, CPython 3.13.3 AMD64)** 1. *Does AF_UNIX exist?* **No, not for Python.** `hasattr(socket,'AF_UNIX')` → `False`; `hasattr(socket,'SO_PEERCRED')` → `False`; `hasattr(asyncio,'open_unix_connection')` → `False`; `hasattr(asyncio,'start_unix_server')` → `False`. Consequently no `%TEMP%` socket probe was possible: there is nothing to probe. The Linux contract's transport and its peer-credential socket option are both unavailable *through CPython*, so the second question is moot at the Python layer: **AF_UNIX cannot reproduce SO_PEERCRED semantics here.** `VERIFIED`. 2. *Does the OS expose an AF_UNIX-shaped endpoint anyway?* Evidence says yes for Codex's own process: the running app-server daemon reports `socketPath = ~/.codex/app-server-control/app-server-control.sock` and a 0-byte file of that name exists. Whether that endpoint is a working Windows AF_UNIX socket, and whether any non-CPython client can connect to it, is `UNKNOWN` (a probe would need Rust/C or a different Python build — out of this task's boundary). `NOT TESTED`. ### 15.2 Named pipes — measured working, including peer identity Status: **VERIFIED** (local, in-process server + in-process client, message-mode pipe, no files created, no repo state touched) ``` pipe name: \\\.\pipe\serverfs-v011-probe (built at runtime to avoid shell backslash mangling) CreateNamedPipeW / ConnectNamedPipe / CreateFileW / ReadFile / WriteFile -> roundtrip payload b'pong' server-side: win32pipe.GetNamedPipeClientProcessId(h) -> 23020 == os.getpid() 23020 -> MATCH server-side: win32pipe.GetNamedPipeClientSessionId(h) -> 2 ``` So on this host a local pipe server **can measure the connecting peer's process identity** — the structural analogue of "SO_PEERCRED is measured, never synthesized" is available, and it is *stronger* (PID + session, and via `ImpersonateNamedPipeClient` → `OpenThreadToken` → `GetTokenInformation` → `ConvertSidToStringSid` the full client SID; those four exports were found in `advapi32` and `api-ms-win-{core-namedpipe,security-base}-l1-1-0`). `VERIFIED` at export level; `NOT TESTED` end-to-end for the SID path. Export-level facts that save the next reader time: - The API is **`GetNamedPipeClientProcessId`** (and `GetNamedPipeServerProcessId`, `GetNamedPipeClientSessionId`, `GetNamedPipeServerSessionId`) — present in `win32pipe` (pywin32 312). - A probe using the plausible-but-wrong name `GetPipeClientProcessId` fails with `AttributeError` from ctypes **and** is absent from `pywin32`; that name does not exist on this OS build. `VERIFIED`. - `win32pipe.CreateNamedPipe` returned `(123, ERROR_INVALID_NAME)` in three probes where the pipe name came from a literal with doubled backslashes passed through this shell, and succeeded once the name was built programmatically (`chr(92)*2 + '.' + chr(92) + 'pipe' + ...`). This was a **transcript/backslash artifact of the probe harness, not a Windows limitation** — recorded so it is not chased again. `VERIFIED`. - Current repository code contains **no** named-pipe implementation, dependency or test (`grep` for `CreateNamedPipe|win32pipe|msvcrt` in `src/` and `agent_bridge/src/` → zero hits). So: `MISSING PLATFORM PRIMITIVE` *in the project*, `AVAILABLE` *on the platform*. ### 15.3 What the Linux side actually does today `protocol.py:240-252` — `writer.get_extra_info('socket')`, then `sock.getsockopt(socket.SOL_SOCKET, socket.SO_PEERCRED, struct.calcsize("3i"))` → uid/gid/pid, compared against `config.allowed_peer_uid/allowed_peer_gid` (defaults `protocol.py:38-47` = own uid/gid), with `hasattr(socket,'SO_PEERCRED')` false → **reject** (fail closed). Filesystem permissions are the second layer: socket 0660 owned by the peer group, parent dir 0750, refuse if `st_mode & 0o022`, refuse if `st_uid != os.getuid()` (`protocol.py:61-71,78-81,112-114,137-162`). Measured on real Windows files the second layer has no meaning (`st_uid=0`, `st_mode=0o100777`). `VERIFIED`. Windows has no equivalent implementation yet. **MISSING PLATFORM PRIMITIVE (implementation), AVAILABLE (OS)** — the two facts are reported separately as instructed. --- ## 16. Writer Lease and Recovery Findings Relationship today (three cooperating objects, all keyed by the numeric slot 1..16): 1. **flock lease** — Bridge `leases.py:35-113`: `_prepare_lock_files` (`:67-89`) pre-creates `{slot:02d}.lock` mode 0640 (Bridge-owned, group-shared), `acquire_exclusive` (`:91-106`) opens O_RDWR|O_CREAT|O_CLOEXEC (+`O_NOFOLLOW`) and takes `fcntl.flock(LOCK_EX|LOCK_NB)`. MCP side `agent_leases.py:21-71` opens the **same inode read-only without O_CREAT** (`:38-48`) and flocks; it never creates (`AGENTS.md:82-86`; `tests/test_agent_leases.py:171-199`). 2. **Persistent active-slot guard** — `recovery.py:25` `lock_dir/active`, per-slot JSON guard (`:51-139`), directory ownership/mode validated (`:37-49`). 3. **mutation_lock** — process-wide `threading.RLock` (`concurrency.py:16-28`), held around every mutation together with the lease (`tools.py:1026-1027` + 5 more sites). Live lease takes precedence over the guard; a guard with no live lease yields `WORKDIR_RECOVERY_REQUIRED` fail-closed (`agent_leases.py:54-64`, `recovery.py:90`). Windows status by sub-question: | Question | Answer | Evidence / status | |---|---|---| | Does the Windows mutation path already consume an Agent lease? | **No — permanently `nullcontext()`** | `tools.py:138-139`, `cli.py:119` — VERIFIED | | Is any Windows lease implemented in repo? | **Absent** | grep zero hits for `msvcrt/LockFileEx/CreateMutex/named mutex` in `src/`, `agent_bridge/`, `native/windows/` — VERIFIED | | Blocked by Linux import? | `fcntl` yes (`agent_leases.py:11`, `leases.py:5`); the *native* path is not blocked because the import is lazy (`tests/test_import_safety.py:24-61` is the existing guard) | VERIFIED | | Can native workdirs be keyed at all? | **No** — `slot` → `0` sentinel, refused by `agent_leases.py:32-33`; the code comment states the intent: "mutations in Agent-integrated deployments are a frozen Linux/Docker capability in v0.10" | `workdirs.py:134-147` — VERIFIED | | Does the OS offer an exclusive cross-process file lock? | **Yes, measured**: `msvcrt.locking(fd, LK_NBLCK, 1)` acquired; a **second process** opening the same path and locking offset 0 was blocked (`Permission denied`); a **second handle in the same process** also blocked; closing that unrelated second handle did **not** release the parent's lock | VERIFIED (temp file under `%TEMP%`, removed after) | | Is the Rust native kernel exposing any lock primitive to Python? | **No** — module surface is `open_workdir` + `NativeSessionError`; share masks (`mutation.rs:42,51`, `ffi.rs:156-160`) are commit-window handle protections, not a persistent named lease | VERIFIED | | flock-equivalent semantics still unproven | release-on-process-death; whether an `O_RDONLY` handle can hold the lock (Linux lease reader opens read-only on purpose, `agent_leases.py:38-48` — my probe used `O_RDWR`); path-vs-inode keying under NTFS; advisory vs OS-enforced; whether `st_ino/st_dev` emulated values are stable enough for an unlink guard | NOT TESTED → §23 | | Recovery-guard portability | logic portable (JSON per slot); guard-dir authorization (`recovery.py:37-49`) and `O_NOFOLLOW` usage need Windows equivalents; guard keying is slot-based → must become alias-based | VERIFIED | --- ## 17. Provider Process Lifecycle Findings Who owns which process (from source, `VERIFIED` unless noted): | Runtime | Who creates the provider process | Interrupt path | After Bridge crash | |---|---|---|---| | Codex | **Neither.** The adapter attaches as a client to a pre-existing managed daemon (`codex.py:800-808` only runs `codex app-server daemon start` when `codex.autostart` is on and the daemon is absent; `config.py:88` control socket). Daemon lifetime is Codex's own. | `turn/interrupt` RPC with a 10 s bound (`service.py:509-517` `_request_provider_cancel`) | daemon and its threads keep running; Bridge relies on `thread/read {includeTurns}` reconciliation (`codex.py:196-249`) to prove in-flight state — **the strongest reconciliation of the three** | | Claude | `claude-agent-sdk` spawns the CLI as its child (`cli_path` at `claude.py:226`, resolution `:549-553`, `--version` probe `:80-89`) | `await client.interrupt()` (`claude.py:151,189,251`) | SDK-owned child; no provider state query available, so reconciliation can only report `SESSION_RESUMABLE` / `_locally_stopped` (`claude.py:156-181`) — in-flight is **not provable** | | Qoder | same shape (`cli_path` `qoder.py:643-647`, `--version` probe `:79-88`) | SDK `interrupt` (`qoder.py:91-96`) | same limitation as Claude (`qoder.py:194-219`) | | fake | in-process | n/a | n/a | Linux lifecycle dependencies, exactly where they live: `main.py:71-78` `loop.add_signal_handler(SIGTERM/SIGINT)` (wrapped in `try: except (NotImplementedError, RuntimeError): continue` → on Windows this simply installs nothing); `main.py:94-97` handler removal; systemd `deployment/agent-bridge/serverfs-agent-bridge.service:25` `KillMode=control-group` (the *only* thing that guarantees provider children die with the Bridge); `install.sh:81-95` `systemctl --user`. There is **no** `setsid`, `killpg`, `os.kill`, `start_new_session`, `waitpid` or `prctl` anywhere in the Bridge. `VERIFIED`. Windows status: **absent**. `supervisor.py:45-52` launches `serve` as a plain `Popen` child and forwards stdio; `:64-72` does `child.terminate()` → 5 s grace → `child.kill()` on parent exception (portable asyncio/subprocess calls, not signals). No `creationflags`, no `CREATE_NEW_PROCESS_GROUP`, no Job Object, no `CTRL_BREAK_EVENT`, no named mutex anywhere (grep: zero hits). Tree kill of the supervisor's descendants is **documented as a manual operator action** (`taskkill /T /F`, `deployment/windows/README.md:39`). `serverfs tunnel` launches the pinned tunnel-client as a child (`native_tunnel.py:194-211`, argv encoding `:122-131`, `subprocess.run` `:233`) with env stripping (`:212-223`) and a **loopback-only ephemeral health bind** (`HEALTH_LISTEN_ADDR` default `127.0.0.1:0`, validation `:236-251`) plus a `file:` credential boundary (`:206-208,134-151`). Reusable seam for v0.11 containment: that launch/forward/env-strip chain plus the loopback-only bind policy; containment itself is the gap the README currently hands to the operator. `VERIFIED`. --- ## 18. Persistence and State-Directory Findings Current Linux Bridge state inventory and defaults: | State | Where computed | Artifact | |---|---|---| | tasks, events, pending requests | `store.py:46` | `state_dir/state.sqlite3` (+ `-wal`/`-shm`, mode 0600 enforced `:77-86`) | | result spool | `result_spool.py:23` | `state_dir/results` | | active-slot recovery guards | `recovery.py:25` | `lock_dir/active/{slot:02d}` | | lease files | `leases.py:68-69` | `lock_dir/{slot:02d}.lock` | | RPC socket | `protocol.py:54` | `socket_path` | | Codex home / control socket | `config.py:69-73,88` | `$CODEX_HOME` else `~/.codex`, `app-server-control/app-server-control.sock` | | config itself | JSON file, unknown keys rejected (`config.py:153`) | operator path | | Defaults | `config.py:241` `/run/serverfs-agent-bridge/bridge.sock`; `:245` `~/.local/state/serverfs-agent-bridge`; `:249` `/run/serverfs-agent-locks` | — | Portability verdict per aspect: - **Logical state model: reusable.** SQLite+WAL, JSON guards, JSON-lines RPC frames, canonical-JSON manifest — nothing POSIX-specific (`store.py:34-56`, `manifest.py:13-87`). `VERIFIED`. - **Path construction: not portable as written.** `/run/...` and `~/.local/state/...` are core defaults reachable from code, i.e. they are Bridge-core, not deployment-only (`config.py:241-249`). Do **not** mechanically map `~/.local/share`/`~/.local/state`/`~/.config`. - **An existing, already-tested Windows convention is available to reuse:** `tunnel_bootstrap.py:64-77 serverfs_data_dir()` → `SERVERFS_DATA_HOME` override, else `%LOCALAPPDATA%\ServerFS` on `sys.platform=='win32'`, else `XDG_DATA_HOME`/`~/.local/share/serverfs`, and it **fails closed** if `LOCALAPPDATA` is missing on Windows. That is exactly the platform seam v0.11 needs for `state_dir`; note `native_config.py` has **no** data-home notion today (only `_is_absolute_native_path`, `:130-145`). `VERIFIED`. - **Permissions: not portable.** Every guard is uid/mode based: `store.py:43` (`st_uid != os.getuid()` or `st_mode & 0o077` → refuse), `:45` chmod 0700, `:86` chmod 0600, `recovery.py:37-49` (ownership + 0700 / "not group-writable or public" + chown), `result_spool.py:27-33,102`, `protocol.py:61-71,137-162` (0660/0750 + `st_mode & 0o022` + `st_uid`), `leases.py:49-64,80-87`. Measured NTFS reality: `st_uid=0`, `st_gid=0`, `st_mode=0o100777` on ordinary executables → these checks would either all pass or all fail depending on how they are read, i.e. they must be replaced by an ACL/owner semantic, not translated numerically. `VERIFIED`. - **Locking: not portable** (see §16). - **No state path is currently configurable through native TOML** — `serverfs.toml.example` has no state/lock/endpoint keys (§9A). --- ## 19. Test and CI Readiness Root suite: 843 `def test_` occurrences across 47 files in `tests/` (grep count). Bridge suite: 138 `def test_` across 14 files in `agent_bridge/tests/`. `agent_bridge` version is 0.9.0 and the root config never collects it (`pyproject.toml:34`). `VERIFIED`. Windows execution evidence gathered this session (executed, not inferred): 1. `uv run pytest -q` (as the gate specifies) → **FAIL: "Interrupted: 12 errors during collection"**. Errors split exactly three ways: `ModuleNotFoundError: No module named 'fcntl'` (`tests/test_agent_leases.py:5`, `tests/test_binary_upload.py:8`); `AttributeError: module 'os' has no attribute 'O_DIRECTORY'` raised at `fdio.py:34` through the import chain (`test_backends.py:24`→`linux_backend`→`binary.py:17`→`fdio`, plus `test_binary_download`, `test_binary_overwrite`, `test_fdio`, `test_find_files`, `test_list_directory`, `test_search_text`, `test_stat_file`); `AttributeError: module 'os' has no attribute 'geteuid'` from **module-scope `skipif`** (`test_create_text_file.py:172`, `test_delete.py:183`). 2. Remaining collectible set: **941 tests collected**; run result **122 failed + 3 errors**. Per file: `test_agent_deployment` 38, `test_agent_tools` 22, `test_edit_text_file` 14, `test_tunnel_proxy_deployment` 12, `test_security` 11, `test_read_text_file` 7, `test_agent_client` 5, `test_mutations` 5, `test_revision` 5, `test_directory_mutations` 3, + `test_mutations` 3 fixture errors. (Remaining ~816 passed — `INFERRED` by subtraction; the final summary line was truncated by output capture, so it is not reported as `VERIFIED`.) 3. Windows-targeted set — `tests/test_windows_backend.py`, `test_native_windows.py`, `test_windows_native_stdio.py`, `test_windows_path_acceptance.py`, `test_native_config.py`, `test_native_tunnel.py`, `test_tunnel_bootstrap.py`, `test_doctor.py`, `test_cli.py`, `test_search_glob.py`, `test_search_find_contract.py`, `test_import_safety.py`, `test_workdirs.py`, `test_config.py`, `test_paths.py` → **359 passed, 3 skipped in 12.95s**. `VERIFIED`. This is the working v0.10 Windows gate on this machine. Migration map for Agent-related tests: | Test file | Category | LOC | Windows status | Blocker / evidence | |---|---|---|---|---| | `tests/test_agent_tools.py` | Phase D MCP surface | 644 | **collects (24 items); 22 fail at runtime** | in-process `FakeAgentClient` (`:26`) is portable; failures trace to fdio/Linux paths (`agent_tools.py:22`→`fdio.py:34`), `_validate_agent_cwd` `:561-580` — NOT fully diagnosed | | `tests/test_agent_client.py` | MCP UDS client | 186 | collects (5), **5 fail** | body uses `asyncio.start_unix_server` (`:35`) — measured absent; client hard-wires `agent_client.py:83` | | `tests/test_agent_leases.py` | shared writer lease | 199 | **import error** | `import fcntl` `:5`; `agent_leases.py:11` | | `tests/test_agent_deployment.py` | Phase E deployment | 1096 | collects (54), **38 fail** | `os.getuid` `:43-65`, AF_UNIX listeners `:477,505,540`, POSIX modes `:33,296`, `subprocess.run(["bash", .../install.sh])` `:934,997` | | `tests/e2e/run_e2e.py`, `bridge_harness.py` | cross-process E2E | — | Linux-only, not collected | `:48` `agent_bridge/.venv/bin/python`, `:211` `mode=0o700` | | `agent_bridge/tests/test_state.py` | state machine | 19 | portable candidate | `state.py` imports clean | | `.../test_policy.py` | policy registry | 134 | portable candidate | `policy.py` imports clean (note `PurePosixPath` `:132`) | | `.../test_config.py` | config parsing | 371 | portable candidate | `config.py` imports clean; POSIX default *strings* at `:241-249` | | `.../test_preflight.py` | Jev advisory | 332 | portable candidate | `preflight.py` imports clean; lazy client `:105` | | `.../test_store.py` | SQLite store | 252 | **no** | `store.py:43` `os.getuid()` + `0o077` mode gate at init | | `.../test_leases.py` | writer lease | 60 | **no** | `leases.py:5` fcntl; modes/`getgid` `:37-51` | | `.../test_service.py` | RPC service semantics | 1266 (33) | **no** | imports service→leases (`:12,43`) | | `.../test_protocol.py` | UDS + peercred | 477 (3) | **no** | `protocol.py:54,242-245`; tests use AF_UNIX/`getuid`/modes (`:109-110,164,344-356,449`) | | `.../test_main.py` | serve lifecycle | 57 (1) | **no** | `_serve` builds protocol server (`main.py:60-66`), signals `:71-73` | | `.../test_runtime_reliability.py` | events/manifest/spool/deadline/idempotency | 688 (12) | **no** | imports service+protocol+leases (header `:16-24`) | | `.../test_codex_adapter.py` | Codex adapter | 1097 (17) | **no** | chain above + `websockets...unix_serve` `:13`, AF_UNIX 107-byte cap `:30` | | `.../test_codex_transport.py` | WS-over-UDS | 100 (1) | **no** | `:9` `unix_serve`; `codex_transport.py:15,68` | | `.../test_claude_adapter.py` | Claude adapter | 603 (11) | **no** | `claude-agent-sdk` absent + chain | | `.../test_qoder_adapter.py` | Qoder adapter | 541 (8) | **no** | `qoder-agent-sdk` absent + chain | | live smoke (`agent_bridge/scripts/{codex,claude,qoder}_live_smoke.py`) | real provider | 258/330/362 | scripts, not tests | need real auth + inference → out of this task's boundary | Natural v0.11 regression anchors (contract already encoded): `agent_bridge/tests/test_runtime_reliability.py`, `test_service.py`, `test_protocol.py` (envelope + auth), `tests/test_agent_tools.py` (best MCP-side anchor — already `FakeAgentClient`), `tests/test_import_safety.py` (the deliberate "product layer must not pull fdio/fcntl" gate — v0.11 must keep it green *while* wiring Windows leases, which is the interesting tension), and the v0.10 `test_windows_*` / `test_native_*` / `test_doctor` / `test_cli` set. Coverage gaps with **no test today**: Windows lease (none exists); named-pipe transport and pipe ACL/impersonation (none); Windows peer-identity negative cases (`protocol.py:242` fail-closed branch is never exercised even on Linux); store/spool/guard authorization without uid+mode (no NTFS-ACL analogue tested); Windows Bridge E2E harness (`run_e2e.py` is Linux-path-only); Windows deployment lifecycle (no PowerShell counterpart to `install.sh`, whose test shells `bash`); and the 12 Linux-channel root test files have no Windows-collectible form. CI note: `native/windows` evidence in this checkout comes from Linux-host CI gates only (`.github` workflow for windows-native), so "green in CI" does not yet mean "green on Windows 11". --- ## 20. Confirmed Reusable Components Category **A — reuse directly, no platform work** (each `VERIFIED` by source): - Task model, status enums, frozen records: `models.py:10-34,90-122`; transition table `state.py:8-48`. - Event envelopes, schema_version, correlation IDs, redaction: `models.py:141-152`, `store.py:379-427`, `service.py:1272-1282,1297-1353,1702`. - Immutable execution manifest (schema v2 + policy fingerprint): `manifest.py:13-87`. - Result spool chunking/UTF-8 boundary logic and its size policy: `result_spool.py:34-162`, `service.py:733-758,1104-1150`. - Idempotency: fingerprint + unique index + conflict handling: `service.py:318-321,410-413,1478-1528,1688`, `store.py:182-188,257-268`. - Approvals and questions lifecycle (incl. expiry and interaction timeout): `service.py:780-933,1368-1463,1627`. - Model discovery/override orchestration: `service.py:170-270,1509-1520`, `base.py:61-69`. - Persistence *format* and migration path: `store.py:34-95` (SQLite, WAL, sidecar modes aside). - RPC envelope, limits, dispatch semantics: `protocol.py:18-21,254-286`, `agent_client.py:11-13,46-51,105-131`. - Policy + redaction: `policy.py:72-124` (host-path redaction is platform-neutral; `:132` is a deliberate contract-level path grammar, see §22). - Recovery/reconciliation logic (not its guards): `service.py:554-708`, adapter reconcile methods. - Jev advisory layer as a whole (fail-open, lazy import): `preflight.py` — and `SERVERFS_JEV_API_KEY` is `unset` here, so no Jev client is constructed, per the frozen rule. - MCP-side seams: lazy Agent registration `main.py:88-98`; lazy lease hook `tools.py:130-142`; neutral `mutation_lock` `concurrency.py:16-28`; already-portable `FakeAgentClient` test harness. - v0.10 native workdir identity: `backends.py:108-194`, `windows_backend.py:587-600` + `identity_token` `:138-143` — the alias-keyed anchor `dev_plan_v0.10.md:1601` points at. - Windows data-home precedent: `tunnel_bootstrap.py:64-77`. --- ## 21. Confirmed Windows Gaps Category **B — platform adapter suffices** (clean seam exists, contract unchanged): - Local IPC: replace `protocol.py:51 start()` + `agent_client.py:79 _exchange()` with a named-pipe pair; message framing, 1 MiB caps and the four-field envelope stay (`protocol.py:18-21,267-286`). Platform support VERIFIED §15.2. - Peer identity: replace `_check_peer` (`protocol.py:240-252`) with client-PID (+ optional SID/session) measurement; keep the measured-never-synthesized rule (F-bucket invariant). Availability VERIFIED §15.2; SID chain NOT TESTED. - Writer lease: reimplement `leases.py` `acquire_exclusive`/`_prepare_lock_files` and the `agent_leases.py` reader on `msvcrt.locking`/`LockFileEx`; the three-method seam is tiny. Cross-process exclusivity VERIFIED §16; full flock-equivalence NOT TESTED. - State/socket/lock defaults: `config.py:241-249` → route through a `serverfs_data_dir()`-style helper (`tunnel_bootstrap.py:64-77`) instead of `/run` and `~/.local/state`. - Ownership/mode guards → NTFS ACL/owner checks in `store.py:41-53`, `recovery.py:33-49`, `result_spool.py:27-33,102`, `protocol.py:61-71,137-162`. - Lease keying: slot 1..16 → workdir alias (Bridge `config.py:154-156`, `leases.py:68-69`, `recovery.py:25`, `agent_leases.py:37,54`, `tools.py:142`, `workdirs.py:134-147`). - Graceful shutdown on Windows: `main.py:71-78` currently installs nothing; needs a Windows stop path (event/console control/pipe-driven shutdown) rather than signals. - Codex transport: `codex_transport.py:15,68-79` (WS-over-AF_UNIX) → `codex app-server proxy` stdio relay or `--listen ws://127.0.0.1:`, both offered by the installed CLI. - Child containment: Job Object / process-tree kill; today zero implementation, tree-kill is manual (`deployment/windows/README.md:39`). Category **C — existing abstraction needs adjustment** (source-proven, adapter alone won't do): - `agent_tools.py:561-580 _validate_agent_cwd` + `:22` `fdio` import: the Agent tool layer is welded to Linux FD traversal primitives, so the *native* Agent surface needs a backend-neutral cwd check (`backends.WorkdirSession`), not a shim. Also proves blocker class "crash", not "refuse" (§9B.2). - `fdio.py:34-36` module-level constant evaluation: makes 8 test modules uncollectible. A lazy/platform-guarded form is required to run the root suite on Windows at all. - Module-scope `os.geteuid()` inside `skipif` (`test_create_text_file.py:172`, `test_delete.py:183`): test-layer abstraction defect (a skip predicate must never itself be platform-unsafe). - `policy.py:132` / `service.py:1469-1475` `PurePosixPath` `relative_cwd` grammar is part of the public tool contract → cross-platform decision, not a swap-in adapter. - `adapters/__init__.py:3-6` eagerly re-exports all three SDK adapters → importing `serverfs_agent_bridge.main` unconditionally imports `claude_agent_sdk` and `qoder_agent_sdk`. With no Windows Claude wheel, this import shape is itself a portability defect; make it lazy or optional-per-config. - `BridgeEvent`/redaction absolute-path tests (`service.py:1299,1317`) use `Path(...).is_absolute()`, which is separator-aware → must be pinned by tests before relying on it cross-platform. Category **D — environment blockers (not code)**: - Claude: **no Windows wheel for `claude-agent-sdk`** (PyPI 0.2.163 files) → native Windows Claude runtime is blocked at the dependency layer, independent of Bridge code. `VERIFIED`. - Bridge dependencies are entirely uninstalled on this machine (no `agent_bridge/.venv`) → nothing in `agent_bridge/` can be imported or tested without an install step, which this task forbade. - Codex network: `codex doctor` reports provider endpoints unreachable over HTTP and Responses WebSocket timeout on this host → live Codex validation may fail for network reasons (proxy/Defender/CA), and that is a scheduling risk, not a v0.11 defect. `VERIFIED` observation. - Claude is configured against a custom `ANTHROPIC_BASE_URL` with api_key auth and Remote Control unavailable → any "Claude works locally" claim would be measuring the wrong thing. - Root pytest gate is not Windows-collectible today (§19) → v0.11 cannot inherit a green full gate on Windows without first fixing C-bucket items. - Provider model inference was excluded by this task → no runtime has been proven to *execute* a task from Windows. `NOT TESTED` by design. --- ## 22. v0.11 Design Decisions Required Only items the investigation shows are genuinely open, with the evidence that forces the decision: 1. **Windows local IPC primitive.** Named pipe vs loopback TCP vs (rejected) AF_UNIX-through- CPython. Evidence: `socket.AF_UNIX`/`SO_PEERCRED`/`asyncio.*_unix_*` all absent (§15.1); pipe roundtrip + client PID measured working (§15.2). Decision includes pipe name/ACL exposure, message vs byte mode, and whether the 1 MiB envelope limits (`protocol.py:19-20`) carry over unchanged. 2. **Peer identity semantics on Windows.** Client PID + session vs full SID via impersonation vs an additional token. Must preserve "measure, then assert; never synthesize" (`AGENTS.md:57-79`, `protocol.py:240-252`) and must not become "trust the pipe name". Note the `hasattr(socket,'SO_PEERCRED')` fail-closed branch has no test today (§19 gap 3). 3. **Writer-lease primitive.** `msvcrt.locking` vs `LockFileEx` via the Rust kernel (which currently exposes no lock at all, §16) vs a mutex object. Must keep: reader opens without creating (`agent_leases.py:38-48`), exclusive-for-the-whole-active-turn, live-lease precedence over the recovery guard, release only after cleanup (`dev_plan_v0.7.md:87-121`). 4. **Lease/guard keying.** Numeric slot 1..16 → alias. Forced by `workdirs.py:134-147` + `native_config.py:196-215` (native workdirs have no slot) and by `dev_plan_v0.10.md:1601` (must not restore fixed Docker slots). This is the one decision with cross-boundary blast radius: `agent_bridge/.../config.py:154-156`, `leases.py:68-69`, `recovery.py:25`, `tools.py:142`, Bridge config render, and both test suites. 5. **Windows state directory convention.** Reuse `serverfs_data_dir()` (`%LOCALAPPDATA%\ServerFS`, `SERVERFS_DATA_HOME` override, fail-closed if `LOCALAPPDATA` missing) or define a `serverfs-agent-bridge` sibling; and how state/spool/active-guard subdirs are derived. Explicitly *not* a mechanical `~/.local/state` translation (`config.py:245`). 6. **State-directory authorization.** NTFS ACL/owner model replacing `st_uid != os.getuid()` and `st_mode & 0o077` (`store.py:43`, `recovery.py:37-49`, `result_spool.py:31,103`, `protocol.py:149`), given measured `st_uid=0`/`mode=0o777` degeneracy (§15.3, §18). 7. **Native config surface.** Whether Agent fields live in `[server]`, a new `[agent]` table and/or per-`[[workdirs]]` `agent_mode`/`agent_runtimes`, and how to handle the asymmetric unknown-key behaviour (§9A: top-level unknown keys silently ignored today) without silently converting previously-ignored config into a startup error. Also whether the native Bridge endpoint is `serverfs.toml` (TOML) or keeps the Bridge's own JSON config (`config.py:153`) — two config surfaces must be reconciled. 8. **Native Bridge lifecycle model.** No systemd, no Windows service, no Task Scheduler (`AGENTS.md:63-67`; `dev_plan_v0.10.md:91` non-goal) — so: supervisor-managed child like `supervisor.py`, `serverfs agent` subcommand, or operator-started process; plus graceful-stop and crash-restart semantics, and Job Object containment of provider children (§17). 9. **Runtime set for the first Windows release.** Codex-only vs Codex+Qoder vs all three, given D-bucket facts (Claude wheel gap; Qoder wheel exists; Codex transport seam needed). 10. **`relative_cwd` path grammar on Windows** (`PurePosixPath` at `policy.py:132`, `service.py:1469-1475`) — keep POSIX-relative vocabulary (recommended for contract stability) or accept native separators. Public-tool-contract decision. 11. **Whether Agent tools become importable-but-refusing on Windows** (fix `fdio.py:34` import-time evaluation + make `agent_tools` import lazily behind a platform guard) — required to turn today's "crash" into the documented "fail closed", and also unlocks 8 root test modules. 12. **CI shape.** Which Windows CI job runs which subset (the four portable Bridge test modules + the v0.10 `test_windows_*` set are the natural first slice, §19), given today's windows-native evidence is Linux-host-only. Deliberately **not** listed as decisions (no evidence they need changing): RPC envelope and protocol version, tool counts/surface, manifest schema, approval decision vocabulary, Jev advisory limits, transport-security settings for Streamable HTTP, file-ingress boundaries. --- ## 23. Open Questions / Experiments Still Needed Each needs a real experiment; nothing below is answerable by reading, and no answer is guessed here. 1. **Is the running Codex daemon's `app-server-control.sock` actually connectable on this OS build, and by what?** Requires a Rust/C or non-CPython client probe (or a Python build with AF_UNIX). Determines whether "reuse the existing WS transport" is viable at all vs "must use a seam". `NOT TESTED`. 2. **Does `codex app-server proxy` work on Windows against the running daemon** (stdio relay, framing preserved, approval server→client requests flowing back through stdio)? Cheap, decisive, no model spend if limited to `initialize`/`initialized`. `NOT TESTED`. 3. **Does `codex app-server --listen ws://127.0.0.1:` start on Windows**, and does it require bearer auth (`--remote-auth-token-env` exists)? Third transport option; also a loopback exposure question for the security review. `NOT TESTED`. 4. **flock-equivalence of Windows byte-range locks**: (a) release on abnormal process death (kill -9 analogue); (b) whether a lock can be taken on a **read-only** handle — the frozen Linux design deliberately opens `O_RDONLY|no O_CREAT` (`agent_leases.py:38-48`) so the container can never create a lock file; (c) behaviour across a handle duplicate vs a fresh open; (d) whether `msvcrt.locking` is advisory-only in practice when a third-party writer ignores it, vs OS-enforced. All `NOT TESTED`; (b) is the highest-risk unknown because it can silently break the "container never creates host lock files" invariant. 5. **NTFS `st_ino`/`st_dev` stability** for the stale-artifact unlink guard (`protocol.py:60,83,114,182-188`), and whether `FILE_ID_INFO` (exposed at `native/windows/src/ffi.rs:612`) is the right identity. `NOT TESTED`. 6. **Named-pipe security surface**: default DACL of `\\.\pipe\*`, whether an ACL can express "current user + this specific client SID only", whether a low-integrity or different-session process can open the pipe, and the cost/correctness of `ImpersonateNamedPipeClient` + `GetTokenInformation(TokenUser)` round-trip. `NOT TESTED` (only `GetNamedPipeClientProcessId` was measured). 7. **Does `qoder-agent-sdk` import and run on Windows** with the local `qodercli` auth, including `qodercli_auth()`, `get_available_models()` and `interrupt()`? Requires an install → deferred. `NOT TESTED`. 8. **Does `claude-agent-sdk` from sdist work on Windows** (it may be pure Python that shells out to a CLI, in which case the missing wheel is a packaging formality rather than a real blocker — or it may embed platform resources). Determinative for whether Claude is in or out of v0.11 scope. `NOT TESTED`, and cannot be answered without installing. 9. **Does `websockets==17.1` import cleanly on Windows** and what does `unix_connect` do there (raise vs hang)? PyPI query timed out; package not installed. `UNKNOWN`. 10. **Approval/question round-trip through a native Windows Bridge** — needs one real provider turn, i.e. a spend + auth-state-bearing action explicitly excluded here. `NOT TESTED`. 11. **Provider-children survival when the Bridge is killed without a graceful stop** (the exact hole `KillMode=control-group` covers on Linux) — needs a Job Object experiment with a live SDK child. `NOT TESTED`. 12. **Whether `Codex` on Windows needs its sandbox prepared** — `windowsSandbox/readiness` / `windowsSandbox/setupStart` exist in the installed schema and `codex doctor` warns about Defender exclusions; whether workspace-write turns work here without that setup is `NOT TESTED`. 13. **Codex CLI 0.159.2 vs managed daemon 0.160.0 skew**: which version actually answers `initialize.userAgent`, and whether the adapter's parsing tolerates the mismatch. `NOT TESTED`. --- ## 24. Recommended v0.11 Scope Boundary Evidence-shaped, offered as a starting point for the maintainer's plan (not a plan): **In scope (A + B buckets only):** - Native Windows Bridge process for the *provider-neutral core* already proven reusable (§20), unmodified in task/state/event/manifest/spool/idempotency/approval/question/model semantics, UDS `PROTOCOL_VERSION=1` envelope kept as the wire contract. - Named-pipe transport + measured client-PID (+ SID) peer identity, replacing `protocol.start`/`_check_peer` behind the same two seams. - Alias-keyed writer lease on a Windows lock primitive, with the same "reader never creates", "exclusive for the active turn", "live lease beats guard", "guard ⇒ `WORKDIR_RECOVERY_REQUIRED`" rules, consuming `backends.WorkdirSession`/`identity_token` instead of `/workdirs/NN`. - Windows state directory via the existing `serverfs_data_dir()` convention + NTFS-ACL authorization checks replacing uid/mode checks. - Supervisor-style native lifecycle with process-tree containment (Job Object) and a real graceful-stop path. - **Codex as the first runtime**, via a Windows-supported app-server seam (proxy stdio or loopback `ws://`) — protocol-level MATCH already proven against the installed schema. - Native TOML `[agent]`/per-workdir agent fields + the `SERVERFS_AGENT_BRIDGE_ENABLED`-equivalent default-false gate, preserving the 11-tool base surface. - Test slice that can go green on Windows now: `agent_bridge/tests/{test_state,test_policy,test_config,test_preflight}.py` + the 15-file v0.10 Windows set + `tests/test_agent_tools.py` (after C-bucket import fixes). **Explicitly out of scope (deferred, each with a proven reason):** - Claude runtime (no Windows `claude-agent-sdk` wheel, D-bucket; plus a custom `ANTHROPIC_BASE_URL` on this host makes the validation misleading). - systemd-era deployment parity, `install.sh`/`compose.agent.yml` ports (the v0.10 non-goal list already excludes Windows Service/Task Scheduler). - Any change to Agent tool counts, RPC shape, approval vocabulary, manifest schema, Jev limits, or Streamable-HTTP transport security — no evidence of need (per `dev_plan_v0.10.md:1515` and the frozen-Phase-D rule). - Native file-parameter ingress (separate v0.10 exclusion, `dev_plan_v0.10.md:92`). **Sequencing recommendation:** the three §23 experiments about transport seam (Q2/Q3) and lease semantics (Q4a/Q4b) are the two places where a wrong assumption is expensive. Prove those with throwaway harnesses *before* touching `leases.py`/`protocol.py`. Fixing `fdio.py:34` and the two module-scope `skipif`s is a cheap, independent first step because it converts 10 uncollectible root test modules into Windows-runnable regression cover for everything else. --- ## 25. Verification Evidence Commands executed (all read-only or temp-scoped; each returned before it is cited): | Command | Result used in | |---|---| | `git status/branch/rev-parse/log/describe/remote` | §3 | | `Get-CimInstance Win32_OperatingSystem`, `Get-Volume -DriveLetter D`, `$PSVersionTable`, elevation check | §4 | | `python -c platform/sys` , `uv --version`, `git --version`, `gh --version`, `node/npm --version`, `cargo/rustc --version` | §4 | | `importlib.metadata.version(...)` for 14 packages | §4, §14 | | `python -c "hasattr(socket,'AF_UNIX')/SO_PEERCRED; asyncio.*; os.O_DIRECTORY/O_NOFOLLOW/getuid/chown/listxattr; msvcrt.locking"` | §4, §15, §16 | | `uv run serverfs --version` / `--help` / `serve --help` | §5, §9B | | `uv run serverfs doctor --config serverfs.toml` (exit 0, `0 FAIL, 0 WARN`) | §5 | | `uv run ruff check .` / `ruff format --check .` | §5 | | `uv run pytest -q` (full) → 12 collection errors; `--collect-only` count 941; Windows set → 359 passed/3 skipped; remaining set → 122 failed/3 errors | §5, §19 | | `Get-Command -All` + `where.exe` for `codex`/`claude`/`qodercli` | §10-12 | | `codex --version/--help`, `codex app-server --help`, `app-server daemon --help`, `daemon start --help`, `app-server proxy --help`, `codex agents --help`, `remote-control --help`, `login --help` | §10 | | `codex app-server daemon version` (running, backend pid, cliVersion/appServerVersion, socketPath) | §10, §15 | | `codex login status`; `codex doctor` | §10.3, §10.4 | | `codex app-server generate-json-schema --experimental --out %TEMP%\codex_schema_probe` (47 files, exit 0), then method/param extraction; directory deleted afterwards | §10.2 | | `claude --version/--help`, `claude auth --help`, `claude auth status`, `claude doctor` | §11 | | `qodercli --version/--help`, `status --help`, `qodercli status`, `qodercli --list-models` | §12 | | `shutil.which` / `os.access(X_OK)` / `os.stat` on the three CLIs | §11.3, §12.2, §15.3, §16 | | Named-pipe probe: `CreateNamedPipeW`+`ConnectNamedPipe`+`CreateFileW`+`ReadFile/WriteFile`, `GetNamedPipeClientProcessId`, `GetNamedPipeClientSessionId` | §15.2 | | Lock probe: `msvcrt.locking(LK_NBLCK)` in parent, subprocess re-attempt, unrelated-handle close | §16 | | ctypes export probe (`kernel32`, `advapi32`, `api-ms-win-core-namedpipe-l1-1-0`, `api-ms-win-security-base-l1-1-0`) for identity/token APIs | §15.2 | | PyPI JSON queries (no install) for `claude-agent-sdk`, `qoder-agent-sdk`, `typesafe-sdk`, `websockets` | §14, §21-D | | Source reading + `grep -n` of `agent_bridge/src/serverfs_agent_bridge/*`, `src/serverfs_mcp/*`, `native/windows/src/*`, `deployment/*`, `tests/*`, `agent_bridge/tests/*`, docs and plans | §6-9, §16-20 | | Temp artifacts: `%TEMP%\codex_schema_probe\` (deleted), `%TEMP%\win_subset_summary.txt` (deleted), lock-probe temp dirs (auto-removed by each probe script) | — | `Not verified` / explicitly not claimed: - No provider executed a task; no approval, question, interrupt or resume round-trip was exercised. - No Windows Bridge process was run (dependencies uninstalled by design). - No `agent_bridge/tests/` execution (nothing importable on this host). - No Docker gate (`docker compose config`, image build) executed — out of this task's subject. - End-to-end acceptance through ChatGPT/Tunnel belongs to the maintainer; nothing here verifies it. - Exact "passed" count of the remaining 941-test Windows run is `INFERRED` by subtraction (§19) — the final summary line was truncated by the output capture, so it is reported as such rather than as a measured figure. - Every "works on Windows" claim in §15-17 rests on a probe executed on this machine; every claim marked INFERRED or NOT TESTED must not be treated as measured. --- ## 26. Final Git Status Executed after this report was written (the report is the only intended addition): ``` $ git status --short ?? .workbuddy/ ?? debug.log ?? v0.11_windows_agent_bridge_discovery.md ``` - Branch `main`, HEAD unchanged (`2616d5a`), no staged changes, no commit/branch/tag created, no push. - `.workbuddy/` and `debug.log` were **already untracked before this task started** and were not touched, moved or deleted. - No tracked file was modified. `git describe --dirty` showed no `-dirty` at baseline and the tree remains content-identical to HEAD for tracked paths. - Temporary probe artifacts were created only under `%TEMP%` and removed (§25 last row).