Repository navigation
fix(js): unblock npm release by sizing the host-callback timeout for Rosetta - #2780
Merged
Merged
Conversation
`macos-latest` is arm64, so the shipped x86_64-apple-darwin binding only ever runs translated under Rosetta 2. Embedded Python/TypeScript runtime start-up costs several times native there, and publish-js.yml was the only workflow running that lane, so a timing regression on it could not surface until a release tag. That is what happened to v0.20.1: three `timeout aborts host work and permits reuse` cases failed on x86_64-apple-darwin while every native lane stayed green. `release-js` is gated on those test jobs, so both npm packages are still stuck on 0.19.0. This commit only adds the lane, so it reproduces the release failure in CI. The fix follows.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bashkit | b4d9e70 | Commit Preview URL Branch Preview URL |
Oct 26 2026, 10:11 AM |
The four `timeout aborts host work and permits reuse` cases gave the whole execution 2s. That clock starts at `execute`, so it covers interpreter start-up on first use in the process and scheduling latency while the rest of the suite runs concurrently; only the remainder is the hang the cases mean to cancel. When it expires first the run is cancelled before the host callback is ever dispatched, so the handler never registers an abort listener and the case reports a missing abort rather than a slow start. On x86_64-apple-darwin, where the shipped Intel binding runs translated under Rosetta 2, 2s was not enough. In the v0.20.1 `Publish JS` run the first `python host functions` call alone took 3.7s, already past the entire budget, and three of the four variants failed there. The fourth passed only because it ran later against a warm runtime, and still took 4.7s of wall time for its 2s timeout. Raise the budget to 20s, well clear of both figures, and assert the two outcomes apart so a run cancelled before the callback starts can no longer read as an abort regression. The cases now wait out the full timeout by design, but ava runs them concurrently: runtime-functions.spec.ts goes from ~5s to ~22s locally, 75/75 passing, and the cost is one timeout rather than four. Also scope the Rosetta lane's Rust cache by target and match publish-js.yml's MACOSX_DEPLOYMENT_TARGET, so the cross build cannot pick up the arm64 lane's `target/`.
Scratch commit. Reverted before merge. Verifies that the new Rosetta lane actually reproduces the v0.20.1 failure rather than passing for unrelated reasons: this lane runs only runtime-functions.spec.ts, while publish-js.yml runs the whole suite under far heavier concurrency.
…s regression
Reverts the scratch commit. With HOST_TIMEOUT_MS back at 2s the new Rosetta
lane failed exactly as the v0.20.1 release did, and the new assertion named
the cause outright:
x [fail]: Bash: typescript timeout aborts host work and permits reuse
host callback never ran; the timeout fired first
x [fail]: BashTool: typescript timeout aborts host work and permits reuse
host callback never ran; the timeout fired first
/ Bash: python timeout aborts host work and permits reuse (3s)
/ BashTool: python timeout aborts host work and permits reuse (3s)
So `entered` is false: the run is cancelled before the interpreter dispatches
the host callback, which is the root cause rather than any abort-delivery
race. Same split as the release failure, where some variants pass and some
lose the race. At 20s all four pass on that lane.
chaliy
marked this pull request as ready for review
October 26, 2026 10:11
2 tasks done
chaliy
added a commit
that referenced
this pull request
Oct 26, 2026
…2788) The x86_64-apple-darwin lane added in #2780 is flaky, and because `js-check` gates on `callback-platforms` it blocks unrelated PRs. It already did: #2787, a GitHub Actions bump touching none of this, went red on it and passed on re-run. Cross-linking to x86_64 on the arm64 runner intermittently dies with error: linking with `cc` failed: exit status: 1 clang: error: unable to execute command: Segmentation fault: 11 while linking a build script. Build scripts are host binaries, so no cross-compilation flag of ours is involved, and crates/bashkit-js/.cargo/ config.toml only sets rustflags for x86_64-pc-windows-msvc. It hit `libc` on one run and `getrandom` on another, roughly two runs in five, and recurred after #2780 scoped the Rust cache per target and again under a different Swatinem/rust-cache revision on #2787 -- so it is the toolchain crashing, not our cache or configuration. Retry the cross build up to three times, logging a ::warning:: per retry so the flake stays visible. cargo keeps whatever already compiled, so a retry resumes rather than rebuilds. This narrows rather than widens what passes: a genuine compile error fails all three attempts and still fails the job, and the test step is outside the loop, so the regression the lane exists to catch still fails it.
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.
What changed
The four
timeout aborts host work and permits reusecases incrates/bashkit-js/__test__/runtime-functions.spec.tsget a timeout budgetsized for the slowest lane we ship instead of 2s, and assert their two failure
modes apart.
js.ymlnow runs the runtime-callback suite on the Rosettax86_64-apple-darwinlane, which previously ran only at release time.No library behaviour changes — test and CI coverage only.
Why
Publish JSfor tag v0.20.1 failed, andrelease-jsis gated on its testjobs, so both npm packages are still on 0.19.0 while crates.io and PyPI are
on 0.20.1. v0.20.1 was cut specifically to unblock npm after #2776; this is what
is still holding it.
Two of fifteen test jobs failed, both on
x86_64-apple-darwin(node 22.23.4 and24.22.1), three cases each:
publish-js.ymlbuildsx86_64-apple-darwinbut runs it onmacos-latest,which is arm64. x64 Node, the x64 binding and the embedded Wasm runtimes
therefore all execute translated under Rosetta 2 — the only translated lane
in the matrix, and the only failing one. Every native lane passed.
The budget covers more than the hang.
timeoutMsstarts atexecute, so italso covers interpreter start-up on first use in the process and scheduling
latency while the rest of the suite runs concurrently. Only the remainder is the
hang these cases mean to cancel. When it expires first, the run is cancelled
before the host callback is ever dispatched, the handler never registers an
abort listener, and
abortedstaysfalse. The run still exits non-zero, sothe preceding assertion passes and the failure surfaces one line later as a bare
Value is not `true`.The same job's timings show 2s was simply too small there:
Bash: python host functions— first embedded-runtime callBashTool: python host functionsBash: python timeout aborts…— the one variant that passedThe first runtime call alone cost 3.7s, already past the entire 2s budget.
Before / After
Both directions were verified on the new lane rather than argued.
At 2s (budget temporarily reverted on this branch, commit since reverted),
the new lane reproduces the release failure — and the new assertion names the
cause outright instead of leaving a bare
false:enteredis false, so the run really is cancelled before the interpreterdispatches the callback — the root cause, not an abort-delivery race. Note the
same split as the release failure: some variants pass, some lose the race.
At 20s, the whole JS workflow is green, Rosetta lane included:
Locally
runtime-functions.spec.tsis 75/75 green; the file goes from ~5s to~22s wall, because the cases wait out the full timeout by design. ava runs them
concurrently, so the cost is one timeout rather than four.
One limit worth stating: the failure does not reproduce on Linux. A first
embedded-runtime call costs 1–3ms natively versus 3.7s translated, so the
budget is never close to exhausted here, and probing down to
timeoutMs: 1still entered and aborted the handler. The Rosetta lane is what actually
exercises this, which is the main reason for adding it.
Risk
the abort contract, or to any published artifact.
existing matrix entries. It earns that cost: it is demonstrably the only lane
in CI that catches this.
regression cannot pass vacuously.
clang: error: unable to execute command: Segmentation fault: 11linkinglibc's build script. TheRust cache is now scoped per target so a restored arm64
target/cannot reachthe x64 build, and
MACOSX_DEPLOYMENT_TARGETmatchespublish-js.yml; it hasbuilt cleanly on all three runs since.
Not fixed here
Publish Webfor v0.20.1 failed separately and is not addressed by this PR:@everruns/bashkit-wasmexists and is published through 0.19.0, andscripts/publish_npm.pyalready passes--access public, so this is npmmasking an authorization failure as a 404 — the
NPM_TOKENsecret needsrotating. That requires repository-secret access and cannot be fixed in code.
Both publish workflows read the same
secrets.NPM_TOKEN, so getting this PRgreen is necessary but probably not sufficient to publish npm; the token
likely needs rotating before the next release tag.
Checklist