From a0851d6f2e049d15bdd5b7407d78772db98ea028 Mon Sep 17 00:00:00 2001 From: Bryant Date: Sat, 27 Jun 2026 19:09:02 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=90=9B=20(release):=20Manage=20npm=20?= =?UTF-8?q?latest=20dist-tag=20in=20release-node=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-releases publish to channel dist-tags (alpha/beta/rc) and never advance `latest`, so a bare `npm install @agent-assembly/sdk` resolved to an ancient alpha. Add a publish step that re-points `latest` at the highest SemVer version on npm after every real publish, in both publish modes. Refs AAASM-3840 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01WbVLjc1fAoDEFuVWDRsPts --- .github/workflows/release-node.yml | 56 ++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/.github/workflows/release-node.yml b/.github/workflows/release-node.yml index 1f828408..f02df723 100644 --- a/.github/workflows/release-node.yml +++ b/.github/workflows/release-node.yml @@ -378,6 +378,62 @@ jobs: # shellcheck disable=SC2086 pnpm publish --access public --no-git-checks $DIST_TAG + # AAASM-3840: keep the npm `latest` dist-tag current. + # + # The publish steps above route each build to a *channel* dist-tag derived + # from its SemVer pre-release identifier (alpha/beta/rc), and only a bare + # GA `X.Y.Z` publish moves `latest` implicitly. Nothing ever advances + # `latest` for a pre-release, so on a pre-1.0 project that has only ever + # shipped pre-releases, `latest` stays frozen at whatever the very first + # publish happened to be — making a bare `npm install @agent-assembly/sdk` + # resolve to an ancient alpha (the bug this fixes). + # + # POLICY (pre-1.0 / pre-GA): `latest` always points at the *highest* + # SemVer version currently on npm, across every channel. SemVer precedence + # (alpha < beta < rc < GA) makes this self-correcting and monotonic: + # - a GA publish wins over any pre-release of the same base, so `latest` + # naturally tracks the newest GA once one exists; + # - while only pre-releases exist, `latest` tracks the newest pre-release + # (e.g. rc.2 over beta.5 over alpha.9.1) instead of the oldest; + # - it never regresses `latest` to an older build, even if an out-of-band + # hotfix to an older channel is published after a newer one. + # Runs in BOTH publish modes (gated only on dry_run, like the main publish + # step) so every real publish re-asserts the invariant. + - name: Update `latest` dist-tag to newest published version + if: steps.tag.outputs.dry_run != 'true' + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + set -euo pipefail + PKG="@agent-assembly/sdk" + # All versions on npm (the just-published one included). `npm view + # versions --json` returns an array, or a bare string if exactly one + # version exists — normalize both to newline-delimited output. + mapfile -t VERSIONS < <(npm view "$PKG" versions --json | node -e ' + const raw = require("fs").readFileSync(0, "utf8").trim(); + const data = JSON.parse(raw); + for (const v of Array.isArray(data) ? data : [data]) console.log(v); + ') + if [[ ${#VERSIONS[@]} -eq 0 ]]; then + echo "::error::no published versions found for $PKG" + exit 1 + fi + # `semver` sorts ascending and includes pre-releases when passed + # explicitly, so the last line is the highest by SemVer precedence. + NEWEST=$(pnpm dlx semver "${VERSIONS[@]}" | tail -n1) + CURRENT=$(npm view "$PKG" dist-tags.latest 2>/dev/null || echo "") + echo "newest published=$NEWEST current latest=$CURRENT" + if [[ -z "$NEWEST" ]]; then + echo "::error::could not determine newest version for $PKG" + exit 1 + fi + if [[ "$NEWEST" == "$CURRENT" ]]; then + echo "latest already points at $NEWEST — nothing to do" + else + npm dist-tag add "${PKG}@${NEWEST}" latest + echo "moved latest: ${CURRENT:-} -> $NEWEST" + fi + # AAASM-2955: cut a git tag + GitHub Release at the published npm # version so the README "GitHub release" badge tracks the current # release. Runs only on the real-publish path (dry-run skips it, same From f3c22715ec4a73cc7c0768d747d9f1e94ee3ced4 Mon Sep 17 00:00:00 2001 From: Bryant Date: Sat, 27 Jun 2026 19:09:02 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=93=9D=20(release):=20Document=20npm?= =?UTF-8?q?=20dist-tag=20policy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs AAASM-3840 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01WbVLjc1fAoDEFuVWDRsPts --- docs/release/npm-dist-tags.md | 56 +++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 docs/release/npm-dist-tags.md diff --git a/docs/release/npm-dist-tags.md b/docs/release/npm-dist-tags.md new file mode 100644 index 00000000..3e65ad8f --- /dev/null +++ b/docs/release/npm-dist-tags.md @@ -0,0 +1,56 @@ +# npm dist-tag policy — `@agent-assembly/sdk` + +This package publishes to npm via [`.github/workflows/release-node.yml`](../../.github/workflows/release-node.yml). +Each release is routed to a **channel** dist-tag derived from its SemVer +pre-release identifier, and the floating `latest` tag is kept current +automatically. + +## Channel dist-tags + +The publish steps derive the channel tag from the version's pre-release +identifier: + +| Version example | dist-tag | +| --------------------- | ------------------ | +| `0.0.1-alpha.9.1` | `alpha` | +| `0.0.1-beta.5` | `beta` | +| `0.0.1-rc.2` | `rc` | +| `0.1.0` (bare `X.Y.Z`) | `latest` (implicit) | + +Install a specific channel with, e.g., `npm install @agent-assembly/sdk@rc`. + +## `latest` policy (pre-1.0 / pre-GA) + +While the project is pre-1.0 and has shipped only pre-releases, `latest` always +points at the **highest SemVer version currently on npm, across every channel**. + +SemVer precedence (`alpha < beta < rc < GA`) makes this rule self-correcting and +monotonic: + +- A GA publish wins over any pre-release of the same base, so `latest` naturally + tracks the newest GA once one exists. +- While only pre-releases exist, `latest` tracks the newest pre-release + (e.g. `rc.2` over `beta.5` over `alpha.9.1`) — never the oldest. +- It never regresses `latest` to an older build, even if a hotfix to an older + channel is published after a newer one. + +The release workflow re-asserts this invariant on every real (non-dry-run) +publish, in both `all` and `main-only` publish modes. + +## One-time manual correction + +The workflow only advances `latest` on the **next** publish. If `latest` is +currently stale (the bug tracked in AAASM-3840 left it pinned at +`0.0.1-alpha.3`), a package owner with an npm publish token must run the +correction once, out of band: + +```bash +# Replace with the highest version currently on npm +# (e.g. the current rc), then: +npm dist-tag add @agent-assembly/sdk@ latest +``` + +After that one-time fix, the workflow keeps `latest` current going forward. + +See the canonical docs at https://docs.agent-assembly.com for end-user install +guidance.