# Windows work notes
Read this file whenever you're working from Windows and add new findings so the next agent can stay unblocked.
- Browser engine now allowed on Windows; expect more flakiness. If automation fails, rerun with
--engine api --waitor point--remote-chrometo a running Chrome with remote debugging. - Chrome DevTools via mcporter:
chrome-devtoolsserver needsCHROME_DEVTOOLS_URLfrom a live session; without itmcporter call chrome-devtools.*fails. Expect this to be unset on Windows unless you bring your own Chrome session/URL. - The agent-scripts
runnerhelper can fail under PowerShell/CMD because of CRLF and bash expectations. If it explodes, run commands directly (pnpm ...,git add/commit) instead. - browser-tools binary: not built in
agent-scripts/binon Windows;pnpm tsx scripts/browser-tools.tsalso fails there (no package manifest). Use a macOS-built binary or run from macOS if you need it. - Prefer PowerShell + pnpm directly; watch for CRLF warnings when touching tracked files.
- WSL browser launch host detection: a systemd-resolved stub such as
nameserver 127.0.0.53is guest loopback, not the Windows host. Keep resolver-derived non-loopback hosts for Windows Chrome compatibility, but route resolver-derived127/8values to the standard local Chrome launcher. - Detached session workers launched by either CLI or MCP must use the shared launcher with
windowsHide: true; a bounded MCPwaitreleases only the waiter and leaves that hidden worker running. - A waiter can read
meta.jsonwhile the detached worker atomically replaces it. Windows may transiently reject that replacement withEPERM,EBUSY, orEACCES; retry only those lock-like errors with a short bounded backoff. - Resolve the session directory with
realpathSync.nativebeforefs.watch; Windows short-path aliases can otherwise hit a native libuv path-prefix assertion instead of a catchable watcher error.
Future Windows gotchas belong here. Update this doc when you learn something new.
- Tab-lease tests run real PowerShell process-identity probes (up to five seconds each). Their Windows test budget must cover multiple probes and registry cleanup. Failed self-identity probes are retried on the next lookup; only a successful identity is cached for the controller lifetime.
- Detached-worker proofs must wait for the worker PID to exit before deleting its temporary working directory. Sending SIGTERM alone races Windows handle release and can fail cleanup with EBUSY; use a bounded exit wait and bounded filesystem retries without skipping the lifecycle assertions.
- A fresh Windows worktree with
core.autocrlf=truecan makeoxfmt --checkflag otherwise unchanged files. Use LF checkout contents for validation and inspect the staged diff to keep checkout-only line-ending changes out of the PR.
- ChatGPT sidebar/history labels can include phrases like "Login setup instruction"; login probes must match exact auth CTAs, not any visible text starting with login, or manual-login automation loops forever before typing.
- For Windows PR refreshes, use
git -c core.autocrlf=falsefor merge and review commands; preserve untracked.codex-tmp/handoff state and leave it out of commits. - ChatGPT's composer plus button can sit close to Work suggestions. A coordinate click observed on Windows entered a new
/c/WEB:...Work task even though the attachment tile later appeared. Activate only#composer-plus-btn/button[data-testid="composer-plus-btn"], then fail closed if the conversation id changes or Work becomes selected before file assignment. Preserve that page identity through upload and check it again at final dispatch. After upload, close an expanded plus menu and keyboard-activate only the exactbutton[data-testid="send-button"]; a trusted coordinate click was observed dismissing UI without committing the staged attachment prompt. scripts/browser-tools.tsmay attach its "active page" command to anabout:blanktarget when several DevTools targets exist. For signed-in evidence, select the exact target id recorded by the Oracle session and verify its URL and composer state directly.
- Shared manual-login Chrome is detached from its native Windows controller and launched with
windowsHide; temporary and copied profiles retain their existing process lifecycle. The final verified lease owner terminates the matching Chrome PID/profile. - After upgrading this lease protocol, restart all Oracle browser controllers before sharing a profile. Older live controllers can forcibly remove a registry lock after their timeout; stored legacy records remain readable, but simultaneous mixed-version controllers are not a safe upgrade path.
- Run
node scripts/shared-chrome-lifecycle-proof.mjsafter building for the native two-controller check. It uses a freshly initialized, signed-out profile and locally supplied pages, verifies peer CDP access after the owner exits, then checks final registry/process/endpoint cleanup. It does not prove signed-in ChatGPT concurrency or backend model identity.
- After merging a dependency update that changes oxfmt, CRLF checkouts may fail format checks across otherwise unchanged files. Normalize tracked text working copies to LF and use
git -c core.autocrlf=falsefor staging; verify the resulting diff contains only intended changes.
Provider-native conversation evidence uses the existing browser connection on Windows too. Files follow the account/directory ACLs; the POSIX owner-only file mode assertion is skipped on Windows. Capture failures leave the normal answer available.