Skip to content

docs(adr-017): TASK-070(b) — recommend against bare-name routing, and make the citation resolve #601

docs(adr-017): TASK-070(b) — recommend against bare-name routing, and make the citation resolve

docs(adr-017): TASK-070(b) — recommend against bare-name routing, and make the citation resolve #601

name: Package Version Guard
# A published package's version is the ONLY check available from outside this
# repo. When source ships without a version bump, that check silently passes
# while the artifact and the repo disagree — and nobody can tell.
#
# It has happened twice:
# #979 @commonlyai/mcp npm 0.3.0 and main 0.3.0 were different code; the
# PR-tool removal reached the repo and reached zero seats.
# #1017 @commonlyai/cli npm 0.1.9 and main 0.1.9 were different code, with
# SEVEN source commits since the bump — including #995, the quota
# misclassification that had seats probing a dead provider every 5s.
#
# Both were found by hand, months and hours late respectively. This makes the
# third one go red instead.
on:
pull_request:
branches: [ main ]
# `edited` catches base-branch retargeting — see the note in
# pr-base-freshness.yml. A stacked PR retargeted to main after its parent
# merges enters this guard's population without any event in the list
# below ever firing for it.
types: [opened, synchronize, reopened, ready_for_review, edited]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
version-guard:
name: Source changed ⇒ version bumped
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Every published package whose source moved must bump its version
shell: bash
run: |
set -euo pipefail
BASE="origin/${{ github.event.pull_request.base.ref }}"
# NO --depth here. `checkout` above already fetched full history
# (fetch-depth: 0); a --depth=1 fetch of the base UNDOES that by
# shallowing the ref, and once the base is one commit deep the fork
# point is unreachable. `git diff BASE...HEAD` then dies with
# "fatal: <base>...HEAD: no merge base" — which reads like a problem
# with the PR's diff and is entirely a problem with this fetch.
#
# It only fires once the base has advanced past the fork point, which
# is why the guard worked for weeks and then failed on #1113. Locally
# reproducible: full clone, advance base 12 commits past a feature
# branch, `git fetch --depth=1 origin <base>`, and the very next
# three-dot diff fails.
#
# Also dropped the `|| true`. A base we cannot fetch means every
# comparison below is meaningless, so it must stop rather than
# silently compare against whatever ref happens to be on disk — the
# failure mode this whole guard exists to prevent.
git fetch --no-tags origin "${{ github.event.pull_request.base.ref }}"
# Prove the base is usable before trusting any diff against it. A
# guard that cannot see its own baseline must say so, not pass.
if ! git merge-base "$BASE" HEAD >/dev/null 2>&1; then
echo "::error::No merge base between $BASE and HEAD — the checkout is too shallow for this guard to compare anything. Not passing on an unusable baseline."
exit 1
fi
fail=0
# pkg_dir : the published package root (holds package.json)
for pkg in cli commonly-mcp; do
# Did any SOURCE file move? Docs and tests do not require a release.
changed=$(git diff --name-only "$BASE"...HEAD -- "$pkg/src" | wc -l | tr -d ' ')
if [ "$changed" = "0" ]; then
echo "· $pkg: no src changes"
continue
fi
base_v=$(git show "$BASE:$pkg/package.json" 2>/dev/null | sed -n 's/.*"version": "\([^"]*\)".*/\1/p' | head -1)
head_v=$(sed -n 's/.*"version": "\([^"]*\)".*/\1/p' "$pkg/package.json" | head -1)
# `sort -V` is not semver. It orders 1.0.0 BEFORE 1.0.0-beta.1,
# reading a prerelease as NEWER than its own release, and it
# inverts in both directions: base=1.0.0 head=1.0.0-beta.1 would
# PASS — the exact backwards walk this check exists to stop — and
# the legitimate promotion 1.0.0-beta.1 → 1.0.0 would FAIL.
# No version ever committed to either package carries a
# prerelease, so this is latent today. It fails OPEN in the
# direction that matters, so the guard refuses to judge rather
# than guessing, on the same principle as the merge-base check
# above: a guard that cannot compare its inputs must say so.
case "$base_v$head_v" in
*-*)
echo "::error file=$pkg/package.json::$pkg version comparison involves a prerelease ($base_v → $head_v). \`sort -V\` orders a prerelease as NEWER than its release, so this guard reaches the wrong answer in BOTH directions. Teach it semver before landing a prerelease; do not merge on this check alone."
fail=1
continue
;;
esac
# An INCREASE, not merely a difference. A long-lived branch that
# bumped while the base moved further ahead leaves head_v BELOW
# base_v — different, so an equality test passes it, and merging
# then walks the version backwards on the base. Observed on two
# open PRs at once (base 0.1.21, heads 0.1.20 and 0.1.19), both
# showing this check green.
newest=$(printf '%s\n%s\n' "$base_v" "$head_v" | sort -V | tail -1)
if [ "$base_v" = "$head_v" ]; then
echo "::error file=$pkg/package.json::$pkg/src changed ($changed file(s)) but version is still $head_v. A published version that maps to two different artifacts defeats the only check available from outside this repo — bump it, or move the change out of $pkg/src."
fail=1
elif [ "$newest" != "$head_v" ]; then
echo "::error file=$pkg/package.json::$pkg/src changed ($changed file(s)) and $head_v is BELOW the base's $base_v. Merging would walk the published version backwards. Rebase and bump above $base_v."
fail=1
else
echo "✓ $pkg: src changed and version rose $base_v → $head_v"
fi
done
exit $fail