OR-199 Windows support for the CLI and dashboard - #299
Merged
Conversation
Block A of the Windows beta: the fixes that need no Windows machine to write, so the branch is ready to clone the moment one is set up. - Extend a bare binary name with PATHEXT when searching PATH. Every harness is looked up by bare name, so on Windows the picker reported Claude Code, Codex and OpenCode all absent and the dashboard could do nothing. - Compose a child's PATH with `;` rather than a hardcoded `:`. - Find bash through `git` rather than PATH. The `bash.exe` usually on PATH is the WSL launcher in System32, which sees a different filesystem than the run directory it would be handed. - Give git an empty path it understands. `/dev/null` reads as a missing relative path on Windows, silently re-enabling repository hooks. - Check a local run's liveness with a zero-timeout wait; there is no `ps` to shell out to. - Link through native_store::create_symlink, which already has both branches, instead of calling the unix symlink directly. - Add a windows-latest CI job so none of this regresses. The Windows branches here have not been compiled yet — that is the next step, on the machine. Co-Authored-By: Claude Opus 5 <[email protected]>
Round one of the Windows compile, from the first CI run: 25 diagnostics, seven root causes. - Give remote_host the `#[cfg(not(unix))]` twins its uid/mode helpers were missing, matching what `directory_writable` already does. Windows keeps only the symlink and directory checks in `ensure_private_dir`, which is a weaker guarantee than the unix path makes. - Gate the control channel itself. It is a Unix domain socket, so `--remote-host` is now refused up front on Windows rather than starting a host nothing could attach to. Local `orx up` is untouched. - Annotate `signal`, whose type came only from the cfg(unix) block. - Gate the EXDEV rename fallback, the inode and mode assertions, and the shell-hook tests, all of which assert on things Windows does not have. - Silence the parameters that are only read on unix. Co-Authored-By: Claude Opus 5 <[email protected]>
Round two, from the cascade the unresolved socket import had been suppressing: every caller of the six control-channel functions. The module splits cleanly in two. `orx up` uses its types and its dashboard locking — HostDescriptor, DashboardLock, canonical_data_dir — and none of that needs a socket. The `orx remote-host` command surface is socket-backed all the way down, so it is gated as a unit and the command now refuses on Windows instead of failing somewhere further in. Also: the two imports only the gated half reached, and `signal`, which loses its one assignment on Windows and with it the need for `mut`. Co-Authored-By: Claude Opus 5 <[email protected]>
Round three was 24 dead-code and unused-import lints and no real compile errors — the tail of removing a feature's entry points while its machinery stays behind. One scoped allow on the module says that plainly. Gating each constant, helper and request type would be bulkier than the code it guards, would have to be unpicked the moment the control channel gets a Windows transport, and would say nothing the module comment does not. updates.rs is the same shape at smaller scale: relaunch_target and relaunch_args keep their tests on every platform and lose only their caller in the binary. Co-Authored-By: Claude Opus 5 <[email protected]>
The Windows suite ran for the first time: 662 passed, 46 failed. Two causes account for 27 of them. `repository_git_dir` canonicalizes, and Windows answers canonicalize with a `\\?\C:\…` verbatim path. Git rejects those outright — "not a git repository" — so importing any existing repository failed. This is a real bug, not a test artifact; the prefix now comes off for drive paths, which are the only ones with a plain form. The other 21 were `remove_dir_all(root).unwrap()` in test teardown, which Windows refuses while any handle into the tree is still open. The codebase already prefers a tolerant cleanup in 146 other places; these 86 now match. All of them are inside `#[cfg(test)]`. Also: the probe fixtures asserted on POSIX PATHs, which are not absolute on Windows and so were rejected by the parser under test. They are parameterized rather than gated — the parsing they cover is not platform-specific, only the fixture was. Co-Authored-By: Claude Opus 5 <[email protected]>
Git for Windows defaults to core.autocrlf=true, and SYSTEM_PROMPT.md and
the agent-skills SKILL.md files are include_str!'d at build time. A
Windows build therefore embedded CRLF and then failed to parse it:
playbook_md() splits the template on `-->\n\n` to drop its leading HTML
comment, that split stopped matching, and `unwrap_or` kept the comment —
so every agent session would have been handed OpenResearch's internal
notes as the opening of its system prompt.
Two tests caught it, one of them by scanning the rendered playbook for
unresolved `{token}` placeholders rather than a fixed list. That is the
only reason this surfaced as a test failure and not as a shipped binary.
Nothing renormalizes: no tracked text file has CRLF in the index today,
so this only governs what lands in a working tree.
Co-Authored-By: Claude Opus 5 <[email protected]>
Round two of the Windows suite: 691 passed, 17 failed. Four of those were real bugs rather than test artifacts. Demo onboarding rejected the demo it had just built. NTFS has no executable bit, so runcpu.sh committed as 100644, the tree hash moved, and the commit ids drifted off the BASELINE_SHA/EXPERIMENT_SHA the demo validates itself against. The mode now reaches the tree through the index, where it does not depend on the filesystem. Remote paths were validated with local rules. `storage_root` asked `Path::is_absolute` about `/home/me/.cargo/bin/orx`, which is false under Windows, so a Windows client rejected every legitimate remote path — and the PathBuf it built would have reached a Linux shell separated by backslashes. Remote paths are POSIX whatever the client runs, so they are handled as strings now. `manage_local_file` took `reports/result.md` from the dashboard and answered `reports\summary.md`, which the next request would have carried straight back. Relative paths cross the API `/`-separated. Cancelling a local run did nothing: there is no process group to TERM. `taskkill /T` takes the tree instead, which is what TERMing the group was for. The rest were fixtures asserting on POSIX shapes, a filename using a character Windows reserves, and two LaTeX tests that need a symlink and a shebang. Co-Authored-By: Claude Opus 5 <[email protected]>
Setting the mode through `update-index` fixed the commit ids but left the index at 100755 against a working tree NTFS always reports as 0644. With `core.filemode true` git believed the filesystem, read runcpu.sh as modified, and refused to check main back out. Windows trusts the index instead, which is where the mode came from. The two telemetry failures are not diagnosed. HTTP delivery itself works on Windows — `sender_posts_the_first_party_endpoint` asserts an Acknowledged post and passes — so what fails is the removal that follows it, and reading has not told me why. Both assertions now report what a second removal says, which separates a blocked delete from a delete that succeeded while the name lingered. Co-Authored-By: Claude Opus 5 <[email protected]>
Three down to two.
`join("demo-repos/nanochat.git")` embeds a separator the platform did not
choose, so on Windows the bare origin came out as
`...\data\demo-repos/nanochat.git` — mixed, and textually unequal to what
git echoes back for the remote it was given. Filesystem calls did not
care; comparing the recorded URL to git's answer did. Split into two
joins, in product code as well as the tests that caught it.
`taskkill /T` returns non-zero when any descendant has already exited on
its own, which says nothing about whether the run stopped. Cancellation
now reads the leader's liveness, which is the question actually being
asked.
The telemetry pair is one step from diagnosed. The retry in the previous
round answered Ok(()), so the file was there and freely deletable and the
removal was never reached — delivery must have come back Retryable. The
assertion now names the outcome, which separates a request that never
landed from a response classified unexpectedly.
Co-Authored-By: Claude Opus 5 <[email protected]>
The last two Windows failures, and they were the tests' fault rather than delivery's. Two of the three mock servers accepted a connection and wrote their response without ever reading the request. Closing a socket that still holds unread data aborts the connection on Windows instead of closing it gracefully, so the client saw a reset rather than the reply it had already been sent, and classified a 400 as Retryable — which left the outbox file the assertion expected to be gone. The third server always read, which is why it passed and misled the search toward the removal. The read loop it already had is now shared by all three. Co-Authored-By: Claude Opus 5 <[email protected]>
An audit of the `orx up` paths no test exercises. Every bug this branch has fixed so far was in code a test happened to touch; the dashboard's own request handling was the largest area where that was not true. Windows `canonicalize` answers with a verbatim `\\?\C:\…` path, and `CreateProcessW` will not take one as a working directory. The dashboard canonicalizes a session's checkout root and then runs git in it — twelve call sites behind `resolve_checkout_root`, covering the code tree, file reads and writes, and diffs — so none of that could have worked. The same root reaches `run_shell_command` as the cwd for a `!` command, and Codex receives canonicalized paths as its sandbox writable roots. Consistency matters as much as the spelling: containment checks compare a canonicalized child against a canonicalized root, and a mix of the two forms denies access to paths that are genuinely inside. So every canonicalization in the crate now goes through one helper rather than the handful that happened to be reachable from a failing test — that is what makes the property hold rather than the individual fixes. The earlier one-off in `repository_git_dir`, which the import failure found, is folded into it. Co-Authored-By: Claude Opus 5 <[email protected]>
Second audit pass, over the spawn path. `find_claude` and `find_opencode` fall back to their installers' drop locations when PATH does not answer, and both spell them as bare names — `~/.local/bin/claude`, `~/.opencode/bin/opencode` — which match no file on Windows. `~/.local/bin` is exactly where Claude Code's own Windows installer puts claude.exe, so the fallback was dead precisely where it was needed. Both now look for the same names PATH does. The rest of the spawn path holds up: config directories travel as environment variables, where a raw path is correct; Codex's config.toml is written through serde_json, whose backslash escape is the one TOML wants; and the binaries themselves come from find_on_path, which learned about PATHEXT earlier on this branch. One gap left deliberately: the PATH guard writes zsh and bash startup hooks and needs ZDOTDIR or HOME to place them, neither of which Windows sets, so it returns early and the guard is simply absent there. It fails closed — a session gets no hook rather than a broken one — and building a Windows equivalent is worth more than a beta needs. Co-Authored-By: Claude Opus 5 <[email protected]>
Both conflicts were additive: main gave ci.yml a `workflow_call` entry point and a new `local::browser_cookies`, alongside this branch's `workflow_dispatch`, windows job, and `local::bash`. Two things the merge is worth noting for: release.yml now calls ci.yml to gate publishing, so the windows job added here gates releases too — a Windows regression will hold up a macOS or Linux release for a platform that has no users yet. overleaf_live's tests arrived with a raw `std::fs::canonicalize`, which this branch had just finished routing through `crate::paths` everywhere. Folded in, so the property still holds tree-wide. Co-Authored-By: Claude Opus 5 <[email protected]>
Six blockers, all real. `orx_bin_dir` dropped any directory whose name contains a colon, which on Windows is every absolute path, so agents were never handed the running orx on PATH — and the separator constant added earlier sat in a branch that never ran. It filters on that constant now. A dashboard shell command that hit its timeout killed nothing on Windows and then waited on the child with no timeout, hanging the turn forever. The non-unix arm terminates the tree, the shape latex already used. BIBINPUTS/BSTINPUTS joined with a colon, so bibtex on Windows lost both the paper's own directory and the system defaults. `restore_local_repository` never pinned core.autocrlf, so a restored demo clone was written CRLF against LF blobs and then failed the cleanliness check that reads it with global config disabled — telling the user their demo "is not the OpenResearch nanochat demo". The remove_dir_all sweep had converted four setup removals along with the teardown, letting tests pass while testing nothing. The Windows uid/mode stubs were unreachable: their whole chain is rooted in cfg(unix) entry points. Gated instead, stubs deleted, and with them a comment claiming a weaker-but-working private runtime directory that does not exist. Also from review: prefix stripping now matches the parsed prefix, so a non-UTF-8 name cannot keep `\\?\` while its root loses it and fail its own containment check; UNC verbatim paths are decoded rather than passed through. PATHEXT set-but-empty no longer reports every tool missing. The bash fallback no longer resolves back to the WSL launcher it rejects. pdfPath goes through the same API path rule as every other relative path. The telemetry signature widened for a test message is reverted. Co-Authored-By: Claude Opus 5 <[email protected]>
Two blockers, both introduced by round one's own fixes. Deleting the Windows uid/mode stubs was wrong: `open_lock` is ungated and calls all three, and `DashboardLock::acquire` reaches it on every `orx up`. That is a Windows compile error on the one platform this branch exists for, and macOS cannot see it. The ownership and mode checks are gated inside `open_lock` now, which keeps the file-type check everywhere and states plainly what Windows does not enforce. Teaching `orx_bin_dir` about the platform separator also handed `ORX_BIN_DIR` to PATH_GUARD, a POSIX snippet that joins `PATH` on colons. Under Git Bash a drive letter split it in two and the guard re-prepended on every startup. The stamp is skipped off unix; `prepare_env` still fronts the directory, which was the point. Also: `cancel_job`'s cfg'd `return` is the function's last statement once stripped, which the Windows clippy job would have rejected; the restore path pins `core.eol` alongside autocrlf so a global `core.eol=crlf` cannot reproduce the same dirty checkout; the verbatim UNC head is built from `OsString` like the tail it is joined to; and the null-device comment no longer claims git falls back to the repository's own hooks, which it does not. Co-Authored-By: Claude Opus 5 <[email protected]>
No blockers in either round-three review. Taking the nits: `open_lock` fstats once instead of twice; the Windows branch of the `ORX_BIN_DIR` stamp is a `#[cfg]` rather than a runtime-false `cfg!`, so it stops resolving the executable only to discard it; two doc comments that described one platform now describe both; and the ownership comment names the case it does not cover — a lock under a shared `ORX_DATA_DIR` has only its ACL on Windows. Co-Authored-By: Claude Opus 5 <[email protected]>
release.yml calls ci.yml and requires it to pass, so the windows job added here would have held up a macOS or Linux release over a platform those releases ship nothing for. `plan` is passed only by release.yml, so skipping on it draws the line where it belongs: Windows still gates pull requests, which is what keeps it from rotting, and never gates a release. Co-Authored-By: Claude Opus 5 <[email protected]>
The artifact is what a tester is handed, and a debug build is slow enough to colour their impression of the dashboard rather than the code's. Co-Authored-By: Claude Opus 5 <[email protected]>
Win32-OpenSSH implements no connection multiplexing: it accepts ControlMaster and ControlPath and then never creates a master. Omitting them is not enough, because the user's own ssh_config can still set a ControlPath, and one it honours fails the connection outright with "getsockname failed: Not a socket". Passing ControlMaster=no and ControlPath=none is what overrides that. The socket machinery goes with it — the control directory, its path hash and its preparation are unix's, and `master_is_running` can only answer false where no master is ever created. Nothing changes on unix, where the shared master still covers later status, log and job commands. Co-Authored-By: Claude Opus 5 <[email protected]>
Two tests asserted the unix option set unconditionally and would have failed on Windows — the very platform this change is for, and one CI never builds. Both derive the expectation from the platform now. `PathBuf`'s only uses became unix-gated, so its import would have failed the Windows clippy job under -D warnings. git shells out to ssh with its own command line, which never carried the override, so a user's ssh_config could still break git-over-SSH on Windows for exactly the reason this change exists. Routed through one helper. The rest: a doc comment my new test had come between and its rightful test; four comments still promising multiplexing unconditionally, the `interactive_args` one worst because it states the invariant that is false on Windows; the new test pinned to the exact vector the way its neighbour already is; and BatchMode coverage split out, since it holds on every platform and was about to be gated away with the ControlPath half. Co-Authored-By: Claude Opus 5 <[email protected]>
No blockers in either round-two review. Taking the nits. Deriving the option count from `multiplexing_opts` had quietly removed the only assertion pinning the unix vector — ControlPersist could have been deleted and nothing in the tree would have failed. Pinned alongside the Windows one. The rest: a test whose name promised reuse on a platform that reuses nothing; `git_ssh_command` says which command it builds; two more doc comments of the class round one fixed, in the two other files that carry them; and the Windows sentence dropped from `ssh_opts`, where it was the third statement of the same fact within 150 lines. Co-Authored-By: Claude Opus 5 <[email protected]>
6 tasks
Onboarding died on Windows at the demo's first `git init`:
fatal: unable to access 'NUL': Invalid argument
`NUL` is right for `diff --no-index`, which recognizes it before touching
the path, and wrong everywhere a path is actually read. Git skips a
config file that reports ENOENT and fatals on any other error, and
Windows cannot `access()` its null device by name at all.
A real empty file behaves the same on every platform, and this file
already had that idiom in `clone_public_repository`. Shared now, and
pointing `hooksPath` at it still disables hooks, since git finds no hook
inside a file.
Co-Authored-By: Claude Opus 5 <[email protected]>
A local run died at `run.sh: line 6: mkdir: command not found`. bash was running; it just had no coreutils. Git for Windows keeps those in `usr\bin` and puts only `cmd` on the Windows PATH, so a bash started from a Windows process inherits a PATH with no Unix tools at all. Fronted for the two bash spawns only — a local run's launcher and the composer's `!` command. Doing it for every child would shadow Windows' own `find` and `sort` with the MSYS ones, which is a worse bug than the one being fixed. prepare_env's PATH is now a function the bash spawn can extend, so the running orx still comes first and the toolchain sits ahead of both. Co-Authored-By: Claude Opus 5 <[email protected]>
The previous commit set the toolchain PATH on the spawn, which run.sh then overwrote on its second line: localrun exports the shell's PATH into the script so a user's own command finds python and uv, and that export wins over anything the process was started with. Caught by the agent inside the dashboard, which read the generated run.sh rather than the launcher and saw the hardcoded Windows PATH for what it was. Co-Authored-By: Claude Opus 5 <[email protected]>
Two things differ there, and the probe hit both. uv ships no shell installer for Windows, so the setup uses its PowerShell one; and uv lays the venv out as `Scripts/` rather than `bin/`, so both activations are tried — either platform can have either layout depending on how the venv was made. These are Rust constants rather than files in the demo repository, so the commit ids the demo validates itself against do not move. `runs/runcpu.sh` carries the same POSIX assumption and is committed, so making the project's own run command portable is a separate change that regenerates those ids. Co-Authored-By: Claude Opus 5 <[email protected]>
The run script exported the process PATH so a user's command could find python and uv. On Windows that value is semicolon-separated, and bash splits PATH on `:` — so it collapsed at the first drive colon and nothing resolved, `mkdir` included. Adding the Git toolchain to it, as the last two commits did, only added more entries to the same unusable string. Git Bash converts the PATH it inherits into POSIX form for itself, so the fix is to leave it alone: the launcher sets the process PATH, and the script no longer overwrites it. The `cd` on the next line had the same shape — bash reads a quoted `C:\…` literally — so the run directory is forward-slashed now. Found by the agent running inside the dashboard, which read the generated run.sh, spotted the separator mismatch, and verified a candidate PATH against both splitters before proposing anything. Co-Authored-By: Claude Opus 5 <[email protected]>
`tar: Cannot connect to C: resolve failed` — GNU tar reads a colon before the first slash as a `host:path` remote spec, so the snapshot archive's Windows path made it try to resolve `C:` as a hostname. Forward slashes would not have helped; the colon is what triggers it. One conversion now covers both places a local path reaches the shell: the archive tar unpacks, and the `cd` the launcher writes. `/c/Users/…` has no colon at all. The HF backend passes a Linux container path and is deliberately left alone. Co-Authored-By: Claude Opus 5 <[email protected]>
A run died mid-training on `UnicodeEncodeError`: CPython encodes stdout in the console's codepage, cp1252 on a default Windows install, and the script printed a banner outside Latin-1. Nothing to do with the experiment — it had already trained a tokenizer by then. `PYTHONIOENCODING=utf-8` joins `PYTHONUNBUFFERED` as a default the author can override, through the same helper every backend already calls, and open-coded once more for kubernetes, whose env is a JSON array. Set on every platform rather than only Windows: a Linux container with `LANG=C` has the same ASCII default and would fail the same way. Co-Authored-By: Claude Opus 5 <[email protected]>
Creating a session worktree for a deep repository failed partway through with "Filename too long". Windows refuses paths over 260 characters unless a program opts into the wide API, and a session worktree spends about a hundred of them on `worktrees/<project>/chat_<session>` before the repository's own paths begin. `core.longpaths=true` on the invocations that write a working tree: the shared helper every worktree and checkout goes through, the authenticated fetch, and the anonymous clone. Read-only plumbing does not need it. This was graded Minor in the original survey. It is a blocker for any repository with a deep tree, which is most of them. Co-Authored-By: Claude Opus 5 <[email protected]>
Windows grants symlinks only to an elevated process or one in Developer Mode, so preparing the isolated agent home failed onboarding outright with os error 1314. Fall back to the links that need no privilege: a junction for a directory, which reads back exactly like a symlink, and a hard link for a file, which does not — so the reconcile now recognizes one instead of filing a conflict against it on every launch. Co-Authored-By: Claude Opus 5 <[email protected]>
Windows' OpenSSH cannot create the AF_UNIX socket ControlMaster needs, and rather than decline the option it kills the connection outright with "getsockname failed: Not a socket" — so every ssh call orx made from Windows failed, whatever the destination. Drop the Control* options there and pay a handshake per call. Same option vector, same platform: `UserKnownHostsFile=/dev/null` is a name Windows' ssh takes literally, creating a `\dev\null` on the current drive. It now gets a scratch file of orx's own. Co-Authored-By: Claude Opus 5 <[email protected]>
Co-Authored-By: Claude Opus 5 <[email protected]>
PR #302 already turns Windows' multiplexing off, and does it by setting ControlMaster=no explicitly rather than by omission — which is what overrides a ControlPath in the user's own ssh_config — and covers git's ssh besides. Take that one instead. Co-Authored-By: Claude Opus 5 <[email protected]>
…into myles/windows-version
`UserKnownHostsFile=/dev/null` is a name Windows' ssh takes literally, creating a `\dev\null` on whichever drive is current. Point it at a scratch file of orx's own instead; StrictHostKeyChecking=no is what makes a recycled provider key acceptable, so the behaviour is unchanged. Co-Authored-By: Claude Opus 5 <[email protected]>
The demo's own run command reached for a POSIX uv installer and `.venv/bin/activate`, neither of which exists on Windows, so the nanochat demo could not run there; both now branch at runtime. That changes the experiment tree, hence the new EXPERIMENT_SHA. Releases built no Windows artifact at all, which is what the CI job's non-blocking rationale rested on. Add the target and a PowerShell installer so a beta tester has something to install. Co-Authored-By: Claude Opus 5 <[email protected]>
Co-Authored-By: Claude Opus 5 <[email protected]>
The job skipped release runs because releases carried no Windows artifact. They do now, so a regression on main would ship broken rather than fail loudly. Co-Authored-By: Claude Opus 5 <[email protected]>
Co-Authored-By: Claude Opus 5 <[email protected]>
Without it there is no usable bash, so experiments and the demo die on their first command with a spawn error naming a path the user has never heard of. The agents check next to it already warns this way. Co-Authored-By: Claude Opus 5 <[email protected]>
Double-clicking orx.exe runs `orx up`. Doing it while orx is already running failed the bind, printed an error into a console Explorer then closed, and looked to the user like nothing happened at all. Open the running dashboard instead, and hold the console open for whatever else a startup failure has to say. Co-Authored-By: Claude Opus 5 <[email protected]>
The console Explorer opens for a double-clicked orx.exe dies with the process, so both ways this program exits with something to say — a returned error and a panic — reached a window that was already gone. Put the message in a dialog instead, and only when no terminal is attached to read the printed one. Co-Authored-By: Claude Opus 5 <[email protected]>
A bare `orx` prints usage and exits, which is right at a prompt and useless from Explorer: the console it printed into closes with the process, so the gesture looked like nothing happening. Owning the console is what tells the two apart, and it now starts `up` — the same trade the macOS .app makes for the same gesture. Co-Authored-By: Claude Opus 5 <[email protected]>
Moving EXPERIMENT_SHA left every existing mac and linux install pointing at a commit its repository does not contain. Each agent turn resolves the session worktree from that commit, so all three demo chats would have failed on their next message after upgrading. An install now starts its sessions from whichever known experiment its demo branch descends from. Co-Authored-By: Claude Opus 5 <[email protected]>
- Release verify job reads the Windows .zip and orx.exe; without it no release would have published for any platform. - "Already running" reuse only for a double-click, and only of a dashboard speaking this build's protocol. On mac and linux a second `orx up` fails on the port again, as it did before this branch. - An old demo install whose cache was wiped but origin kept restores its own experiment rather than building a sibling it cannot push. - Same-file probe errors fall through to the old reconcile; the demo probe only puts ~/.local/bin on PATH after installing uv; the panic hook is Windows-only. - Docs name the assets cargo-dist actually publishes. - Comments cut to the one-or-two-line house rule; three doc comments moved back onto the functions they describe. Co-Authored-By: Claude Opus 5 <[email protected]>
- A double-click keeps the flags it was given (`--no-telemetry` from a shortcut no longer turns telemetry back on). - A kept demo origin is validated before a clone is restored from it, so a foreign or moved origin is named as the problem instead of the cache. - The ssh opt-vector test reads the Windows known-hosts path once; it follows XDG_CONFIG_HOME, which telemetry tests mutate in parallel. - Every comment this branch adds is now one or two lines. Co-Authored-By: Claude Opus 5 <[email protected]>
- Remote storage paths accept an interior `.` again. `Path::components` skipped it on main, so `ORX_DATA_DIR=./orx` on a remote box — probed as `/home/u/./orx/orx.db` — broke `orx up --remote` on this branch. - A foreign kept demo origin is rejected before anything is restored from it, now with a test; `install_repository` keeps main's shape, since the restore helper creates its own parent. - Comments trimmed last round get back the why they lost, and the PYTHONIOENCODING one no longer claims the console codepage: redirected output uses the ANSI one. Co-Authored-By: Claude Opus 5 <[email protected]>
11 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
orxbuilds, tests and runs on Windows — double-clickorx.exeand the dashboard opens — and a release now ships a Windows zip and a PowerShell installer. Scope is the local CLI and dashboard, not a packaged desktop app.What was broken on Windows
Found by reading the code: verbatim
\\?\paths broke every git call; CRLF checkouts corrupted the agent system prompt; harness discovery found nothing without PATHEXT; NTFS exec bits moved the demo's pinned SHAs; remote paths were validated with local rules; cancel did nothing.Found by running it on a real machine, after CI was green: double-click printed usage and exited; every ssh call failed with
getsockname failedbecause Win32-OpenSSH cannot multiplex (#302, merged in here); git fatals on aNULconfig path;mkdir: command not foundbecause bash splitsPATHon:;tarreadC:as a host; cp1252UnicodeEncodeError; the 260-character path cap on worktrees;os error 1314on symlinks killed onboarding; the demo'sruncpu.shcould not run.Keeping macOS and Linux unchanged
Six review rounds — three before this PR opened, three after, the last three focused on mac/linux. They caught regressions this branch itself had introduced, all fixed and tested:
verify-build-channelcould not read the Windows.zip, and it gateshost.orx upsilently opened whichever dashboard held the port. Now only a Windows double-click reuses one, and only one speaking this build's protocol.EXPERIMENT_SHAstranded existing demo installs: every demo chat would have failed on its next turn. An install now resolves its own experiment commit;PREVIOUS_EXPERIMENT_SHAis accepted throughout.orx up --remoterejected a remote data path containing..The final round swept every shared code path against origin/main; each is identical or deliberately changed (for example
PYTHONIOENCODING=utf-8on all backends, which a user-set value still overrides).Load-bearing choices
orx up --remote-hostis refused on Windows (the control channel is a Unix socket); the PATH guard is absent; ownership and mode checks are unix-only.Testing
cargo fmt --check,clippy -D warnings,build --locked,test --locked— 778 tests, macOSwindows-latest, green on this HEADorx upserves the dashboard on a real Windows machineorx.exestarts the dashboardrelease.ymldry run (-f tag=dry-run) builds and verifies the Windows artifact — the only exercise of the verify-job fixFollow-ups, not in this PR:
orx updateon Windows suggests Unix commands; code signing; mirroringinstall.ps1on openresearch.sh.🤖 Generated with Claude Code