mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-01 06:42:06 +01:00
fix(runner): build on Windows and add a Windows CI lane (#18250)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip Runner (`packages/paperclip-runner`) runs agent sessions
through a native Rust daemon, `paperclip-runnerd`
> - The daemon builds only for macOS and Linux. On Windows x64
(`x86_64-pc-windows-msvc`), the build stops with one compile error
> - No CI lane builds the daemon on Windows, so new Unix-only code can
break the Windows build again without notice
> - This pull request fixes the compile error and adds a Windows CI lane
for the Rust workspace. It is the first of a series of small pull
requests that port the Runner to Windows
> - The benefit is a Windows build that stays green. Each later pull
request in the series can then prove its Windows behavior in CI
## Linked Issues or Issue Description
Related: https://github.com/paperclipai/paperclip/issues/18142 and
https://github.com/paperclipai/paperclip/pull/18144 port the server and
the development scripts to native Windows. That work does not touch the
Rust Runner.
**What happened?**
On Windows x64 with the pinned Rust 1.97.1 MSVC toolchain, `cargo build
--manifest-path packages/paperclip-runner/runner/Cargo.toml --locked
--workspace` fails with `error[E0599]: no method named inherited_command
found` in `process_supervisor.rs`. `VerifiedProcessLaunch::spawn_stdio`
calls `inherited_command`, but that method exists only on Linux and
macOS.
**Expected behavior**
The Runner workspace builds on Windows x64. Features that do not have a
Windows implementation yet fail with a clear error at run time. They do
not stop the build.
**Steps to reproduce**
1. Use a Windows x64 host with Visual Studio 2022 Build Tools and
rustup.
2. Check out `master`.
3. Run `cargo build --manifest-path
packages/paperclip-runner/runner/Cargo.toml --locked --workspace`.
**Paperclip version or commit**
`master` at a8e07500db
## What Changed
- `process_supervisor.rs`: compile `VerifiedProcessLaunch::spawn_stdio`
only on Linux and macOS, the same as `inherited_command`. Its only
caller, the stdio MCP proxy, already returns an explicit error on
non-Unix hosts. Linux and macOS behavior does not change.
- New workflow `.github/workflows/runner-windows.yml`: on
`windows-2025`, it installs the pinned toolchain and runs `cargo test
--release --locked --workspace --no-fail-fast` for changes to Runner
Rust inputs. Release mode matches the Linux lane (`test:rust`). In a
debug build on a hosted runner, one recovery test missed its 5-second
deadline. It is a standalone workflow because `pr.yml` runs
`pr-trusted.yml` from `master`, so a lane added there does not run on
the pull request that adds it. The lane has read-only permissions, uses
SHA-pinned actions, and keeps its own Rust cache that only `master`
writes.
- New policy test
`.github/scripts/tests/runner-windows-workflow.test.mjs` for the
workflow triggers, commands, skip list, permissions, and cache.
- 22 tests need verified process launch, which does not exist on Windows
yet. 21 of them are now `#[cfg_attr(windows, ignore = "verified launch
is not supported on Windows yet")]`. The attribute has no effect on
Linux or macOS. The last one is in `acpx_provider_backend.rs`, which is
a Grok profile identity source file. An attribute there would change the
Grok profile identity, so the Windows lane skips that test by exact name
(`--exact --skip`). The policy test fixes the skip list to that one
name. The next pull requests in the series implement verified launch and
remove the attributes and the skip.
- Two test fixtures used Unix-only absolute paths
(`/missing-provider-pack` and `/materialized/skills/...`). These are not
absolute on Windows. On Windows, the fixtures now use a drive-letter
path. The Unix fixtures do not change.
## Verification
- On Windows x64 (Rust 1.97.1 MSVC), the lane's exact release-mode test
command passes: 686 passed, 0 failed, 23 ignored, and 1 test skipped by
name. The test that timed out in a debug build on the hosted runner
takes 3.6 seconds in release mode and 29.5 seconds in debug mode on a
local machine. The ignored tests are the 21 Windows-only ignores and 2
tests that are ignored on every platform. A partial name does not match
with `--exact`, so the skip cannot hide other tests.
- `cargo fmt --manifest-path packages/paperclip-runner/runner/Cargo.toml
--all -- --check` passes.
- `node packages/paperclip-runner/scripts/generate-acpx-profiles.mjs
--check` and `node
packages/paperclip-runner/scripts/grok-profile-identity.mjs --check`
pass. The Grok profile identity does not change.
- `node --test .github/scripts/tests/runner-windows-workflow.test.mjs`
passes.
- The Linux and macOS lanes in this pull request's CI confirm that Unix
behavior does not change.
- The new "Runner Windows" check runs on this pull request.
## Risks
- Low risk. The Rust changes are `cfg` attributes and test fixtures.
Linux and macOS compile the same code as before.
- The new lane adds one Windows job to pull requests that change Runner
Rust inputs. A cold build takes longer than a cached build. After the
first `master` run, pull requests restore the cache.
- The 22 ignored or skipped tests do not run on Windows until verified
launch exists. They still run on Linux and macOS.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- Anthropic Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with
tool use (shell, file edits, local builds and tests) and extended
thinking.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
This commit is contained in:
1 parent
493e229d93
commit
cdf843371b
8 files changed
+161
-3
No files matched your search
@@ -0,0 +1,48 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
const workflow = readFileSync(new URL("../../workflows/runner-windows.yml", import.meta.url), "utf8");
|
||||
|
||||
function pathFilter(event) {
|
||||
const block = workflow.split(`\n ${event}:\n`)[1]?.split(/\n \S/)[0];
|
||||
assert.ok(block, `${event} trigger is missing`);
|
||||
return [...block.matchAll(/^\s+- (\S+)$/gm)].map(match => match[1]);
|
||||
}
|
||||
|
||||
test("the Windows Runner lane runs on pull requests and master for every Runner input", () => {
|
||||
const pullRequest = pathFilter("pull_request");
|
||||
const push = pathFilter("push");
|
||||
assert.deepEqual(push, pullRequest);
|
||||
assert.match(workflow, /push:\s*\n\s*branches: \[master\]/);
|
||||
for (const path of [
|
||||
".github/workflows/runner-windows.yml",
|
||||
"packages/paperclip-runner/rust-toolchain.toml",
|
||||
"packages/paperclip-runner/runner/**",
|
||||
"packages/paperclip-runner/protocol/**",
|
||||
]) assert.ok(pullRequest.includes(path), `missing path filter ${path}`);
|
||||
});
|
||||
|
||||
test("the Windows Runner lane builds and tests the locked workspace on hosted Windows x64", () => {
|
||||
assert.match(workflow, /runs-on: windows-2025\n/);
|
||||
assert.match(workflow, /shell: bash\n/);
|
||||
assert.match(workflow, /run: \|\n\s+rustup toolchain install\n/);
|
||||
assert.match(workflow, /run: >-\n\s+cargo test --release --manifest-path runner\/Cargo.toml --locked --workspace --no-fail-fast --\n\s+--exact --skip /);
|
||||
assert.doesNotMatch(workflow, /continue-on-error|if: always\(/);
|
||||
});
|
||||
|
||||
test("the Windows Runner lane skips only exact named tests", () => {
|
||||
const skipped = [...workflow.matchAll(/--skip (\S+)/g)].map(match => match[1]);
|
||||
assert.deepEqual(skipped, ["acpx_provider_backend::tests::binds_sidecar_paths_arguments_and_contents_to_the_runner_profile"]);
|
||||
assert.match(workflow, /--exact --skip /);
|
||||
});
|
||||
|
||||
test("the Windows Runner lane is read-only and keeps its own master-written cache", () => {
|
||||
assert.match(workflow, /^permissions: \{\}$/m);
|
||||
assert.match(workflow, /permissions:\n\s+contents: read\n/);
|
||||
assert.doesNotMatch(workflow, /: write\b|secrets\.|pull_request_target/);
|
||||
assert.match(workflow, /persist-credentials: false/);
|
||||
for (const [, ref] of workflow.matchAll(/uses: \S+@(\S+)/g)) assert.match(ref, /^[0-9a-f]{40}$/);
|
||||
assert.match(workflow, /shared-key: runner-windows-v1\n/);
|
||||
assert.match(workflow, /save-if: \$\{\{ github.event_name == 'push' \}\}\n/);
|
||||
});
|
||||
Reference in new issue
Block a user