Context
PR #125 (chore(release): bump @aictrl/cli to 0.4.4 and guard tag/package version drift) added a guard to .github/workflows/publish.yml that fails the release when the tag version differs from packages/cli/package.json. The automated review on that PR raised follow-ups that were deferred at merge time, and the v0.4.4 release exposed a smoke-test timing problem. This issue collects the remaining hardening work for the release workflow.
Scope
(a) Run the version guard before the build
The step Verify the release tag matches the @aictrl/cli package version runs after bun turbo build, so a tag/package mismatch (the case the guard exists to catch) still pays for a full monorepo build before it fails.
- Export the stripped
AICTRL_VERSION to $GITHUB_ENV in its own step (or in the guard step itself) before bun turbo build.
- Move the guard step so that it runs before
Build Packages.
(b) Fail on an empty AICTRL_VERSION
The guard compares PKG_VERSION to AICTRL_VERSION without first checking that AICTRL_VERSION is set. The workflow only triggers on release: published, so the tag should always be present. Even so, an explicit check costs little and replaces a confusing Release tag version does not match ... message with a clear one:
if [ -z "${AICTRL_VERSION:-}" ]; then
echo "::error::AICTRL_VERSION is empty. This workflow must run from a published release tag."
exit 1
fi
(c) Lengthen the smoke test's npm propagation window
The Smoke test - verify @aictrl/cli installs cleanly via npm step retries npm install @aictrl/cli@<version> 5 times at 30 s intervals (about 2.5 min). On the v0.4.4 publish, the smoke test gave up while npm was still processing the new package, and the version became installable a few minutes later. The publish had succeeded, but the red run looked like a failed release.
- Extend the window, for example 10 attempts with backoff (30 s, then 60 s) for about 8 to 10 minutes in total, or poll
npm view @aictrl/cli@<version> version until it resolves before running the install.
- Keep the step failing loudly once the window expires.
(d) Keep the packages/cli workspace version in bun.lock in sync
bun.lock still records the packages/cli workspace as version 0.3.3, and it has been stale since 0.4.0. This has had no functional effect: bun install --frozen-lockfile passes in CI and the 0.4.4 publish succeeded. It is still lockfile drift, and it makes the version bump PRs look incomplete. Regenerate bun.lock as part of each version bump, or add a CI check that the workspace versions in bun.lock match their package.json.
(e) Document the release checklist
publish.yml stamps only the platform binary packages (@aictrl/cli-<platform>-<arch>) with the tag version. @aictrl/cli itself publishes with whatever version is in packages/cli/package.json. Add a short release checklist (in CONTRIBUTING.md or a new RELEASING.md):
- Bump
packages/cli/package.json (and regenerate bun.lock, see (d)) on main via PR.
- Merge that PR.
- Only then create the GitHub release with tag
v<same version>.
- Confirm the
Publish to npm run is green and npm install @aictrl/cli@<version> works.
Acceptance criteria
References
🤖 Generated with Claude Code
Context
PR #125 (
chore(release): bump @aictrl/cli to 0.4.4 and guard tag/package version drift) added a guard to.github/workflows/publish.ymlthat fails the release when the tag version differs frompackages/cli/package.json. The automated review on that PR raised follow-ups that were deferred at merge time, and the v0.4.4 release exposed a smoke-test timing problem. This issue collects the remaining hardening work for the release workflow.Scope
(a) Run the version guard before the build
The step
Verify the release tag matches the @aictrl/cli package versionruns afterbun turbo build, so a tag/package mismatch (the case the guard exists to catch) still pays for a full monorepo build before it fails.AICTRL_VERSIONto$GITHUB_ENVin its own step (or in the guard step itself) beforebun turbo build.Build Packages.(b) Fail on an empty
AICTRL_VERSIONThe guard compares
PKG_VERSIONtoAICTRL_VERSIONwithout first checking thatAICTRL_VERSIONis set. The workflow only triggers onrelease: published, so the tag should always be present. Even so, an explicit check costs little and replaces a confusingRelease tag version does not match ...message with a clear one:(c) Lengthen the smoke test's npm propagation window
The
Smoke test - verify @aictrl/cli installs cleanly via npmstep retriesnpm install @aictrl/cli@<version>5 times at 30 s intervals (about 2.5 min). On the v0.4.4 publish, the smoke test gave up while npm was still processing the new package, and the version became installable a few minutes later. The publish had succeeded, but the red run looked like a failed release.npm view @aictrl/cli@<version> versionuntil it resolves before running the install.(d) Keep the
packages/cliworkspace version inbun.lockin syncbun.lockstill records thepackages/cliworkspace as version0.3.3, and it has been stale since 0.4.0. This has had no functional effect:bun install --frozen-lockfilepasses in CI and the 0.4.4 publish succeeded. It is still lockfile drift, and it makes the version bump PRs look incomplete. Regeneratebun.lockas part of each version bump, or add a CI check that the workspace versions inbun.lockmatch theirpackage.json.(e) Document the release checklist
publish.ymlstamps only the platform binary packages (@aictrl/cli-<platform>-<arch>) with the tag version.@aictrl/cliitself publishes with whatever version is inpackages/cli/package.json. Add a short release checklist (inCONTRIBUTING.mdor a newRELEASING.md):packages/cli/package.json(and regeneratebun.lock, see (d)) onmainvia PR.v<same version>.Publish to npmrun is green andnpm install @aictrl/cli@<version>works.Acceptance criteria
publish.ymlbeforebun turbo buildruns.AICTRL_VERSIONfails with an explicit error message.bun.lockrecords the currentpackages/cliversion, and the release process keeps it in sync.packages/cli/package.jsononmainbefore creating the GitHub release.References
🤖 Generated with Claude Code