doc/RELEASING.md
Maintainer runbook for shipping Paperclip across npm, GitHub, and the website-facing changelog surface.
The release model is now commit-driven:
master publishes a canary automatically.releases/vYYYY.MDD.P.md.Paperclip uses calendar versions that still fit semver syntax:
YYYY.MDD.PYYYY.MDD.P-canary.NExamples:
2026.318.02026.318.12026.318.1 line: 2026.318.1-canary.3Important constraints:
MDD, where M is the UTC month and DD is the zero-padded UTC day2026.303.0 for March 3, not 2026.33.02026.0318.02026.3.18.12026.318.0-canary.1Every stable release has four separate surfaces:
paperclipai and public workspace packages are publishedA stable release is done only when all four surfaces are handled.
Canaries only cover the first two surfaces plus an internal traceability tag.
masterreleases/vYYYY.MDD.P.mdEvery push to master runs the canary path inside .github/workflows/release.yml.
It:
canarycanary/vYYYY.MDD.P-canary.NUsers install canaries with:
npx paperclipai@canary onboard
# or
npx paperclipai@canary onboard --data-dir "$(mktemp -d /tmp/paperclip-canary.XXXXXX)"
Use .github/workflows/release.yml from the Actions tab with the manual workflow_dispatch inputs.
Inputs:
source_ref
stable_date
YYYY-MM-DD2026-03-18, not a version like 2026.318.0dry_run
Before running stable:
./scripts/release.sh stable --date "$(date +%F)" --print-versionreleases/vYYYY.MDD.P.md on that source refExample:
source_ref: masterstable_date: 2026-03-182026.318.0The workflow:
YYYY.MDD.P under npm dist-tag latestvYYYY.MDD.Preleases/vYYYY.MDD.P.md./scripts/release.sh canary --dry-run
./scripts/release.sh stable --dry-run
This is mainly for emergency/manual use. The normal path is the GitHub workflow.
./scripts/release.sh stable
git push public-gh refs/tags/vYYYY.MDD.P
PUBLISH_REMOTE=public-gh ./scripts/create-github-release.sh YYYY.MDD.P
Stable changelog files live at:
releases/vYYYY.MDD.P.mdCanaries do not get changelog files.
Recommended local generation flow:
VERSION="$(./scripts/release.sh stable --date 2026-03-18 --print-version)"
claude --print --output-format stream-json --verbose --dangerously-skip-permissions --model claude-opus-4-6 "Use the release-changelog skill to draft or update releases/v${VERSION}.md for Paperclip. Read doc/RELEASING.md and .agents/skills/release-changelog/SKILL.md, then generate the stable changelog for v${VERSION} from commits since the last stable tag. Do not create a canary changelog."
The repo intentionally does not run this through GitHub Actions because:
For a canary:
PAPERCLIPAI_VERSION=canary ./scripts/docker-onboard-smoke.sh
For the current stable:
PAPERCLIPAI_VERSION=latest ./scripts/docker-onboard-smoke.sh
Useful isolated variants:
HOST_PORT=3232 DATA_DIR=./data/release-smoke-canary PAPERCLIPAI_VERSION=canary ./scripts/docker-onboard-smoke.sh
HOST_PORT=3233 DATA_DIR=./data/release-smoke-stable PAPERCLIPAI_VERSION=latest ./scripts/docker-onboard-smoke.sh
Automated browser smoke is also available:
gh workflow run release-smoke.yml -f paperclip_version=canary
gh workflow run release-smoke.yml -f paperclip_version=latest
Minimum checks:
npx paperclipai@canary onboard installsRollback does not unpublish versions.
It only moves the latest dist-tag back to a previous stable:
./scripts/rollback-latest.sh 2026.318.0 --dry-run
./scripts/rollback-latest.sh 2026.318.0
Then fix forward with a new stable patch slot or release date.
Do not run stable.
Instead:
masterThis is a partial release. npm is already live.
Do this immediately:
PUBLISH_REMOTE=public-gh ./scripts/create-github-release.sh YYYY.MDD.Preleases/vYYYY.MDD.P.mdDo not republish the same version.
latest is broken after stable publishRoll back the dist-tag:
./scripts/rollback-latest.sh YYYY.MDD.P
Then fix forward with a new stable release.