fix: refresh platform binary optionalDependencies on every release#46
fix: refresh platform binary optionalDependencies on every release#46
Conversation
The committed @aictrl/cli optionalDependencies hard-pinned @aictrl/cli-linux-x64 to 0.2.0, so 0.3.0 and 0.3.1 both shipped pointing at the old native binary — upgrades silently kept running the 0.2.0 build. Only @aictrl/cli-linux-x64 was even listed; other platforms (linux-arm64, darwin, windows, musl/baseline variants) were built but neither published nor referenced. The publish workflow now (1) loops over every platform binary the build produced under packages/cli/dist/@aictrl/ and publishes each, and (2) regenerates @aictrl/cli's optionalDependencies from those manifests at publish time, preserving genuine runtime optionals (@parcel/watcher, bun-pty) that the compiled binary still resolves at install time. Removed the stale @aictrl/cli-linux-x64 entry from the committed package.json so source-of-truth no longer lies about what ships. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Review: fix: refresh platform binary optionalDependencies on every releaseOverall: LGTM — this is a well-targeted fix for a real version-drift bug. The approach of regenerating Verified correct
Minor observations (not blocking)
No bugs, security issues, or logic errors found. The change is correct and ready to merge. Reviewed SHA: 417f0bc |
CI's bun install --frozen-lockfile rejected the previous commit because the lockfile still referenced @aictrl/cli-linux-x64@0.2.0 and the stale 0.2.0 version stamp on the cli workspace. Regenerated. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Review: fix: refresh platform binary optionalDependencies on every releaseVerdict: Approve with minor suggestions. This is a solid fix for a real version-drift bug. The approach of regenerating Good
Issue: Silent publish failures leave dangling references in optionalDependenciesThe publish loop uses Consider tracking which publishes succeeded and only including those in run: |
published=""
for dir in */; do
echo "Publishing ${dir%/}"
if (cd "$dir" && npm publish --access public --provenance); then
published="$published ${dir%/}"
else
echo "::warning::Failed to publish ${dir%/}"
fi
done
echo "PUBLISHED=$published" >> "$GITHUB_ENV"Then have the strip step only include platform packages in Minor:
|
Address review feedback: the dist/@aictrl/ scan should not blindly trust every entry. Filter to actual directories, skip ones without a package.json, and only accept packages whose name matches the platform binary shape (@aictrl/cli-<linux|darwin|windows>-...). Catches a real failure mode I verified locally: the dead-but-still-in-tree script/publish.ts creates dist/@aictrl/cli/package.json with name @aictrl/cli-ai. Without the name filter, that wrapper would silently get added to @aictrl/cli's optionalDependencies on the next release. Verified the regex against all 11 name shapes build.ts can produce (linux/darwin/windows × x64/arm64 × musl/baseline variants) and a representative set of negative cases. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Addressed in Observation 1 ( Observation 2 ( Observation 3 ( |
First release that picks up the platform binary optionalDependencies fix from #46 — 0.3.0 and 0.3.1 both shipped pointing at the stale 0.2.0 native binary and need to be superseded. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
First release that picks up the platform binary optionalDependencies fix from #46 — 0.3.0 and 0.3.1 both shipped pointing at the stale @aictrl/cli-linux-x64@0.2.0 native binary and need to be superseded. Refreshes bun.lock and adds CHANGELOG entry.
The publish workflow has been silently failing since v0.3.1 (2026-04-03) with MODULE_NOT_FOUND: promise-retry during `npm install -g npm@latest`. Root cause: the self-upgrade path on the runner's prebuilt npm tree leaves @npmcli/arborist's dependency graph half-replaced and the new binary crashes on first invocation. Symptoms confirmed today while trying to publish v0.3.2 — both v0.3.1 and v0.3.2 tags exist on GitHub but neither version is actually on npm; `npm view @aictrl/cli versions` tops out at 0.3.0. This is why the optionalDependencies pinning fix from #46 hadn't reached users. Switching to corepack sidesteps the failure mechanism entirely: corepack ships with Node 22, installs to its own shim directory, and doesn't touch the bundled npm tree. Pinning to 11.5.2 (minimum OIDC trusted publishing support + 2 patch releases to shake out regressions) instead of tracking a moving `@latest` target. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The publish workflow has been silently failing since v0.3.1 with MODULE_NOT_FOUND: promise-retry during `npm install -g npm@latest`. Root cause: the self-upgrade path on the runner's prebuilt npm tree leaves @npmcli/arborist's dependency graph half-replaced. Discovered while releasing v0.3.2 — both v0.3.1 and v0.3.2 tags exist on GitHub but neither version is actually on npm; `npm view @aictrl/cli versions` tops out at 0.3.0. This is why the optionalDependencies fix from #46 hadn't reached users. Switch to `corepack prepare npm@11.5.2 --activate`: corepack installs to its own shim directory and never touches the bundled npm tree, so the self-upgrade corruption path is unreachable. Pinning to 11.5.2 (minimum OIDC trusted publishing support + 2 patch releases) instead of tracking a moving `@latest` target.
Summary
@aictrl/clioptionalDependencieshard-pinned@aictrl/cli-linux-x64to0.2.0, so@aictrl/cli@0.3.0and@0.3.1shipped pointing at the old0.2.0native binary — vanilla upgrades silently kept running the stale build.@aictrl/cli-linux-x64was even listed;linux-arm64,darwin-*,windows-*, and the musl/baseline variants were built byscript/build.tsbut neither published to npm nor referenced from the top-leveloptionalDependencies.packages/cli/dist/@aictrl/and publishes each, and (2) regenerates@aictrl/cli'soptionalDependenciesfrom those manifests at publish time, preserving genuine runtime optionals (@parcel/watcher,bun-pty) that the compiled binary still resolves at install time.@aictrl/cli-linux-x64entry from the committedpackages/cli/package.jsonso the source-of-truth no longer lies about what ships.Why
@parcel/watcherandbun-ptyare preservedThe compiled Bun binary can't bundle these —
src/file/watcher.ts:38doesrequire(\@parcel/watcher-${process.platform}-${process.arch}-${libc}`)andsrc/pty/index.ts:37doesawait import("bun-pty"). Both are resolved against the host platform at runtime, so they must remain in the publishedoptionalDependencies`.Why the rewrite happens at publish time, not in source
script/build.tsalready stamps eachdist/@aictrl/cli-<target>/package.jsonwithScript.version(the release tag), so it's the authoritative source for what binaries actually exist for this release. Reading from there guarantees the top-level package and the platform packages always agree on a version, with no manual bump step to forget.If
dist/@aictrl/is empty when the strip step runs, the script throws — fail loud rather than silently ship@aictrl/cliwith no platform binaries.Test plan
dist/@aictrl/tree containingcli-linux-x64,cli-linux-arm64,cli-darwin-arm64,cli-windows-x64-baseline. Confirmed: stale@aictrl/cli-linux-x64: 0.2.0is replaced,@parcel/watcherandbun-ptyare preserved, all 4 platform packages are added at the correct version, and stripped fields (dependencies/devDependencies/peerDependencies/peerDependenciesMeta/overrides/exports/type) are gone.python3 -c "yaml.safe_load(...)"parses the updated workflow.bun turbo typecheckclean (pre-push hook).release: published. Recommend cutting a test tag (e.g.v0.3.2-test.0) against a scratch package first if you want belt-and-suspenders before the next user-facing release.npm view @aictrl/cli@<new-version>should show all platform binaries inoptionalDependenciesat the matching version, and a freshnpm install -g @aictrl/clion linux-x64 should pull@aictrl/cli-linux-x64@<new-version>(not 0.2.0).Out of scope (worth separate cleanup)
packages/cli/script/postinstall.mjs:52uses the unscoped nameaictrl-${platform}-${arch}while published packages are scoped@aictrl/cli-*. It silently no-ops on failure (process.exit(0)on line 130) andbin/aictrldoes the real resolution correctly using the scoped name, so this is latent rather than user-facing — but should be fixed.packages/cli/script/publish.tsappears to be dead code (publishes as@aictrl/cli-ai, not invoked by any workflow). Could probably be deleted.🤖 Generated with Claude Code