ci(host-filesystem): prove host-install slice on Ubuntu, macOS, and Windows - #787
Conversation
Keep Verify, examples, release-gates, and pinned-CLI host-install proofs on ubuntu-latest. Add a three-cell matrix (ubuntu/macOS/Windows, Node 22.19) that builds once and runs only the host-install/filesystem tests that can diverge by OS. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
🦋 Changeset detectedLatest commit: 89377e8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
…slice macOS compared lexical /tmp fixtures to realpath /private/tmp results. Windows died in rstest.config.ts while building the Workbench e2e example this slice never uses. Give the host-filesystem job its own config and publish a realpath worker temp root. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…aunch Judge project paths by on-disk identity so Windows 8.3 aliases and native config paths no longer raise AB7001. Tolerate directory FlushFileBuffers EPERM, replace development-host junctions by moving the previous pointer aside, and launch packed npm/framework bins through Node entrypoints with HOME plus USERPROFILE isolation. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
The packed-install-bin harness now checks Node's sibling npm, the official node_modules/npm path, and nvm's lib/node_modules layout before falling back to createRequire. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Preserve ENOENT-tolerant lexical canonicalization for not-yet-built hook/payload paths. Resolve npm-cli.js from official, nvm, and PATH layouts without createRequire. Report POSIX-only install tests as skipped on Windows, and treat file-parent state roots as unproven on Windows EEXIST. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…junctions Windows cannot follow pnpm's relative store links through a node_modules junction, so the host-install fixture build failed to resolve @modelcontextprotocol/server. Build that fixture under the package on Windows so walk-up finds the real tree. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Launch prepack's npm pack dry-run through process.execPath and a discovered npm-cli.js so hosted Windows (pnpm Node, no spawnable npm) no longer fails AB5000. Missing project paths resolve the nearest existing ancestor so escaping symlink leaves fail closed without throwing ENOENT. Windows directory publish uses absolute junction targets; worker roots hash cwd+pid on every OS; receipt-write rollback no longer depends on chmod. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…ilure A file at the receipts store blocked mkdir before host CLI verbs ran, so rollback never issued uninstall/remove. The receipt file path as a directory lets the host verbs succeed and then fails the atomic rename. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…table Occupying the receipt path broke the pre-write read. Spying writeStoredInstallReceipt fails after host verbs on every OS, including Windows where chmod 0555 is a no-op. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Compare packed-install-bin bundleRoot to the realpath npm root (8.3 vs long). Publish Windows generation junctions at realpath targets, attach the Cursor destination the manager actually installed, and junction realpath compile deps into the Windows host-install fixture so a coordinator rebuild can resolve them. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
A successful coordinator rebuild now treats the epoch it just built as active so host-install attaches when the prepare snapshot digest and compiler project revision disagree. Source snapshots record the same identity path createProjectContext uses. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Share resolvedProjectPath between snapshot inputs and createProjectContext so a Windows path.resolve spelling cannot fail the coordinator rebuild with AB7101 and skip artifact.available. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Nearest-ancestor containment still judges escaping dangling leaves, but a deleted configuration file or hashed source input must exist after that gate. Prebuilt hook and payload paths stay allowed to be missing. The coordinator host-install test now asserts an active epoch and artifact.available before attach. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Hosted Windows FlushFileBuffers can fail on newly written epoch files after a successful compile (AB7100). Keep the bytes and publish the epoch instead of dropping artifact.available. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Coordinator rebuilds publish a native Playground catalog snapshot after epoch sync. Hosted Windows FlushFileBuffers can EPERM that newly written file the same way it EPERMs staged epoch files, wrapping as AB7100 and leaving Cursor host-install unattached. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
File and directory FlushFileBuffers errors share one Windows tolerance path, so the directory argument tripped no-unused-vars on Docs CI. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
File and directory FlushFileBuffers errors share one Windows tolerance path, so the flag is unused. Call sites still pass true for directories; underscore it for rslint instead of dropping the argument. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Hosted Windows FlushFileBuffers refuses read-only regular-file handles (AB7100). EpochStore now opens existing files with non-truncating r+ on win32, keeps directory opens read-only, and restores directory-only EPERM tolerance so a genuine file sync failure still refuses publication. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 89377e8590
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } catch (error) { | ||
| if (!isErrno(error, 'ENOENT')) throw error; | ||
| cursor = parent; |
There was a problem hiding this comment.
Reject dangling symlink ancestors before containment
When a missing model or prebuilt path is below a dangling symlink whose target is outside the project, realpathSync() returns ENOENT for both the full path and the symlink parent, so this loop skips over the symlink and reconstructs an apparently in-project lexical path from the project root. The containment check therefore accepts the escape, and the original model path can resolve outside the project if the target materializes before it is consumed; inspect and resolve symlink components rather than treating every ENOENT parent as nonexistent.
Useful? React with 👍 / 👎.
| - `durable-fs.test.ts` (atomic publish, Windows directory fsync) | ||
| - `internal-child-resolution-policy.test.ts` (packaged child resolution, #769) | ||
| - `packed-install-bin.test.ts` (packaged installer bin from a consumer cwd) | ||
| - `rstest-worker-isolation.test.ts` (canonical TMPDIR; macOS `/tmp` → `/private/tmp`) |
There was a problem hiding this comment.
Include the npm CLI test in the documented slice
This list says it enumerates the tests run by pnpm test:host-filesystem, but it omits npm-cli-resolution.test.ts, which is included by rstest.host-filesystem.config.ts:29. Add that test to the list so the local-CI documentation matches the actual hosted matrix coverage.
AGENTS.md reference: AGENTS.md:L96-L99
Useful? React with 👍 / 👎.
| "agent-bundle": patch | ||
| --- | ||
|
|
||
| On Windows, `createProjectContext` now judges existing project paths by on-disk identity so 8.3 aliases and native config paths no longer raise `AB7001`; missing paths resolve the nearest existing ancestor (so a dangling leaf under an escaping symlink still fails closed) instead of throwing `ENOENT`. After that containment gate, the configuration file and every recorded source input must still exist (`ENOENT`); prebuilt hook and payload paths that are allowed not to exist yet stay valid. Source snapshots record the same identity path `createProjectContext` uses, so a development rebuild no longer fails with `AB7101` when `path.resolve` spelling differs from the realpath project identity, and a successful `agent-bundle dev` rebuild publishes `artifact.available` from the epoch it just built so host-install attaches even when a prepare snapshot digest and compiler project revision disagree. `agent-bundle prepack` launches npm's `npm-cli.js` through `process.execPath` and ignores a pnpm `npm_execpath`. Development host install publishes Windows directory junctions with absolute realpath targets and replaces an existing junction by moving the previous pointer aside; development epoch publication opens existing regular files with write-capable non-truncating `r+` on Windows before `fsync` so FlushFileBuffers is not refused as `EPERM` (`AB7100`), while directory FlushFileBuffers `EACCES`/`EINVAL`/`EPERM` stays best-effort and genuine file-sync failures still refuse publication; and a declared state root whose parent is a file records as unproven (`EEXIST`) instead of failing install. (#787) |
There was a problem hiding this comment.
Rewrite the changeset as an imperative user-facing summary
The changeset is a declarative, implementation-heavy account beginning with “now judges” and detailing internal flags, fsync behavior, and error handling, rather than an imperative release summary. Rewrite it in imperative voice around the user-visible commands and behavior while retaining the relevant diagnostic codes and (#787) suffix.
AGENTS.md reference: AGENTS.md:L124-L125
Useful? React with 👍 / 👎.
Why
A GPT 6 Pro review of
maintreated filesystem/process portability as Ubuntu-only, whiledev --install-hostalready has Windows junction publish, atomic rename/rollback, ownership manifests, path casing, and per-OS host config locations. The package is platform-generic (noosfield). Cloning the whole Verify/examples/release suite onto three OSes would make every PR much slower.What this does
Adds a three-cell hosted job, not a Node × OS product:
ubuntu-latest,macos-latest,windows-latestpnpm buildonce, thenpnpm test:host-filesystemviarstest.host-filesystem.config.tsThe slice is only the tests that can diverge by OS:
dev-host-install.test.ts(Claude/Codex CLI legs skip; the Unix-socket Codex app-server fake isit.skiponwin32)dev-host-install-manager.test.ts(rollback / stale ownership)install.test.ts/uninstall.test.ts(receipt ownership)durable-fs.test.ts(atomic publish, Windows directory fsync)internal-child-resolution-policy.test.ts(packaged child resolution, fix(dev): fail closed when packaged internal child executables are missing #769)npm-cli-resolution.test.ts(Windows official + Unix/nvm + PATH + split-prefix/pnpmnpm_execpath)packed-install-bin.test.ts(packaged installer bin from a consumer cwd)rstest-worker-isolation.test.ts(canonical TMPDIR, process-identity roots)Existing jobs stay Ubuntu-only: Verify, examples, release-gates, and the pinned-CLI
host-install-proofsjob.GPT 6 Pro FIX after
6e270b97(this head89377e85)Hosted Windows failed coordinator rebuild with
AB7100EPERM: operation not permitted, fsync. The config path in that diagnostic isfailureDiagnostics()attribution, not the synced file.Root cause: EpochStore
#syncPathopened regular files read-only ('r'). Windows FlushFileBuffers requires write-capable access.This tip implements the requested FIX and reverses the earlier file-EPERM swallow (
a377fbb8/2b0875cb):#syncPathopens existing regular files with non-truncating'r+'whenthis.#platform === 'win32'; directories stay'r'.EACCES/EINVAL/EPERMtolerance is restored. Regular-file sync failures stay fatal and refuse publication (previous active epoch + staging cleanup).EPERM(that handle is already write-capable via'wx').durable-fs.tsis unchanged: it already refuses file EPERM, and productionsyncPathis directory-only (staging files use'wx').dev-host-install.test.tsstill requiresartifact.state === 'active'andartifact.availablebefore attach.GPT 6 Pro FIX after
a8163c12(kept)temporaryRoot + workerId + cwd + pid. Unix under/tmp; Windows under host TEMP.89377e85. Still draft pending Zack / GPT 6 Pro re-review.Follow-up on the first CI head (
66f2a777) — keptrstestWorkerRoot()publishes a realpath asTMPDIR.rstest.host-filesystem.config.tsso this slice does not buildexamples/rsc-agent-runtime.Out of scope
hook/index.jsvshook\\index.jsmismatch is not claimed fixed; this slice simply no longer builds it.Verification
Local Linux (agent VM,
89377e85)Hosted (
89377e85CI run 34676060514)Windows job: https://github.com/ScriptedAlchemy/agent-bundle/actions/runs/34676060514/job/103506505809
Changeset:
.changeset/host-install-windows-path-identity.md.