Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/release-node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:-<unset>} -> $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
Expand Down
56 changes: 56 additions & 0 deletions docs/release/npm-dist-tags.md
Original file line number Diff line number Diff line change
@@ -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 <newest> with the highest version currently on npm
# (e.g. the current rc), then:
npm dist-tag add @agent-assembly/sdk@<newest> 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.
Loading