Files
OpenResearch/.gitattributes
T
Myles AndersonandClaude Opus 5 9b05f9771c OR-199 Windows support for the CLI and dashboard (#299)
* Make orx build and resolve its tools on Windows

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 <noreply@anthropic.com>

* Allow CI to be dispatched against a branch

* Compile the remaining unix-only code paths out on Windows

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 <noreply@anthropic.com>

* Gate the remote-host command surface off on Windows

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 <noreply@anthropic.com>

* Keep the compiled-out remote-host half rather than gate it item by item

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 <noreply@anthropic.com>

* Stop handing git a verbatim path, and let test cleanup fail

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 <noreply@anthropic.com>

* Check out LF on every platform

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 <noreply@anthropic.com>

* Fix what Windows found: exec bits, remote paths, cancellation

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 <noreply@anthropic.com>

* Let the index own the demo's exec bit

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 <noreply@anthropic.com>

* Keep the demo's origin path native, and judge cancel by liveness

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 <noreply@anthropic.com>

* Read the request before answering it in the telemetry mock servers

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 <noreply@anthropic.com>

* Canonicalize into paths a child process can actually use

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 <noreply@anthropic.com>

* Look for the harness binaries under the names they actually have

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 <noreply@anthropic.com>

* Address review round one

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 <noreply@anthropic.com>

* Address review round two

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 <noreply@anthropic.com>

* Polish from review round three

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 <noreply@anthropic.com>

* Keep the windows job out of the release gate

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 <noreply@anthropic.com>

* Upload a release orx.exe, not a debug one

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 <noreply@anthropic.com>

* Turn SSH multiplexing off explicitly on Windows

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 <noreply@anthropic.com>

* Address review

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 <noreply@anthropic.com>

* Address review round two

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 <noreply@anthropic.com>

* Point git's config paths at an empty file, not the null device

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 <noreply@anthropic.com>

* Give the spawned bash its own toolchain on PATH

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 <noreply@anthropic.com>

* Fold the toolchain into the PATH run.sh exports

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 <noreply@anthropic.com>

* Let the demo probe run under Git Bash

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 <noreply@anthropic.com>

* Stop writing a Windows PATH into a bash script

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 <noreply@anthropic.com>

* Hand the shell paths it can read, not Windows ones

`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 <noreply@anthropic.com>

* Give runs a UTF-8 stdout

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 <noreply@anthropic.com>

* Let git write paths past Windows' 260-character cap

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 <noreply@anthropic.com>

* Link the agent's config without asking Windows for a privilege

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 <noreply@anthropic.com>

* Stop asking Windows' ssh to multiplex

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 <noreply@anthropic.com>

* Count the Windows ssh opts the Windows way

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Revert the duplicate multiplexing fix

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 <noreply@anthropic.com>

* Give Windows a real file for the host keys it must forget

`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 <noreply@anthropic.com>

* Ship a Windows binary and a way to install it

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 <noreply@anthropic.com>

* Tell Windows testers what they need before they need it

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Let Windows hold up a release now that a release ships it

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 <noreply@anthropic.com>

* Say what signing will and will not fix

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Say Git for Windows is missing at startup, not at the first run

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 <noreply@anthropic.com>

* Make a second click reach the dashboard, not die on its port

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 <noreply@anthropic.com>

* Make a failed start say so where a console cannot

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 <noreply@anthropic.com>

* Start the dashboard when orx.exe is double-clicked

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 <noreply@anthropic.com>

* Keep demo installs seeded before the Windows change working

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 <noreply@anthropic.com>

* Address review round four

- 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 <noreply@anthropic.com>

* Address review round five

- 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 <noreply@anthropic.com>

* Address review round six

- 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 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 18:26:35 -07:00

13 lines
513 B
Plaintext

# LF in the working tree on every platform. SYSTEM_PROMPT.md and the
# agent-skills SKILL.md files are include_str!'d at build time, and a CRLF
# checkout ships a binary whose own `\n`-boundary parsing of them fails.
#
# An existing clone keeps its CRLF working tree until each file next changes;
# `git add --renormalize .` converts it in one step.
* text=auto eol=lf
# The demo's commit ids are asserted against hardcoded SHAs, so these must never
# be treated as text and normalized.
*.pt binary
*.pkl binary