chore: formalize release workflow

This commit is contained in:
Dotta
2026-03-09 08:49:42 -05:00
parent ccd501ea02
commit a7cfd9f24b
9 changed files with 1431 additions and 1091 deletions

View File

@@ -1,363 +1,140 @@
---
name: release-changelog
description: >
Generate user-facing release changelogs for Paperclip. Reads git history,
merged PRs, and changeset files since the last release tag. Detects breaking
changes, categorizes changes, and outputs structured markdown to
releases/v{version}.md. Use when preparing a release or when asked to
generate a changelog.
Generate the stable Paperclip release changelog at releases/v{version}.md by
reading commits, changesets, and merged PR context since the last stable tag.
---
# Release Changelog Skill
Generate a user-facing changelog for a new Paperclip release. This skill reads
the commit history, changeset files, and merged PRs since the last release tag,
detects breaking changes, categorizes everything, and writes a structured
release notes file.
Generate the user-facing changelog for the **stable** Paperclip release.
**Output:** `releases/v{version}.md` in the repo root.
**Review required:** Always present the draft for human sign-off before
finalizing. Never auto-publish.
Output:
---
- `releases/v{version}.md`
Important rule:
- even if there are canary releases such as `1.2.3-canary.0`, the changelog file stays `releases/v1.2.3.md`
## Step 0 — Idempotency Check
Before generating anything, check if a changelog already exists for this version:
Before generating anything, check whether the file already exists:
```bash
ls releases/v{version}.md 2>/dev/null
```
**If the file already exists:**
If it exists:
1. Read the existing changelog and present it to the reviewer.
2. Ask: "A changelog for v{version} already exists. Do you want to (a) keep it
as-is, (b) regenerate from scratch, or (c) update specific sections?"
3. If the reviewer says keep it → **stop here**. Do not overwrite. This skill is
done.
4. If the reviewer says regenerate → back up the existing file to
`releases/v{version}.md.prev`, then proceed from Step 1.
5. If the reviewer says update → read the existing file, proceed through Steps
1-4 to gather fresh data, then merge changes into the existing file rather
than replacing it wholesale. Preserve any manual edits the reviewer previously
made.
1. read it first
2. present it to the reviewer
3. ask whether to keep it, regenerate it, or update specific sections
4. never overwrite it silently
**If the file does not exist:** Proceed normally from Step 1.
## Step 1 — Determine the Stable Range
**Critical rule:** This skill NEVER triggers a version bump. It only reads git
history and writes a markdown file. The `release.sh` script is the only thing
that bumps versions, and it is called separately by the `release` coordination
skill. Running this skill multiple times is always safe — worst case it
overwrites a draft changelog (with reviewer permission).
---
## Step 1 — Determine the Release Range
Find the last release tag and the planned version:
Find the last stable tag:
```bash
# Last release tag (most recent semver tag)
git tag --sort=-version:refname | head -1
# e.g. v0.2.7
# All commits since that tag
git log v0.2.7..HEAD --oneline --no-merges
git tag --list 'v*' --sort=-version:refname | head -1
git log v{last}..HEAD --oneline --no-merges
```
If no tag exists yet, use the initial commit as the base.
The planned stable version comes from one of:
The new version number comes from one of:
- An explicit argument (e.g. "generate changelog for v0.3.0")
- The bump type (patch/minor/major) applied to the last tag
- The version already set in `cli/package.json` if `scripts/release.sh` has been run
- an explicit maintainer request
- the chosen bump type applied to the last stable tag
- the release plan already agreed in `doc/RELEASING.md`
---
Do not derive the changelog version from a canary tag or prerelease suffix.
## Step 2 — Gather Raw Change Data
## Step 2 — Gather the Raw Inputs
Collect changes from three sources, in priority order:
Collect release data from:
### 2a. Git Commits
1. git commits since the last stable tag
2. `.changeset/*.md` files
3. merged PRs via `gh` when available
Useful commands:
```bash
git log v{last}..HEAD --oneline --no-merges
git log v{last}..HEAD --format="%H %s" --no-merges # full SHAs for file diffs
```
### 2b. Changeset Files
Look for unconsumed changesets in `.changeset/`:
```bash
git log v{last}..HEAD --format="%H %s" --no-merges
ls .changeset/*.md | grep -v README.md
```
Each changeset file has YAML frontmatter with package names and bump types
(`patch`, `minor`, `major`), followed by a description. Parse these — the bump
type is a strong categorization signal, and the description may contain
user-facing summaries.
### 2c. Merged PRs (when available)
If GitHub access is available via `gh`:
```bash
gh pr list --state merged --search "merged:>={last-tag-date}" --json number,title,body,labels
```
PR titles and bodies are often the best source of user-facing descriptions.
Prefer PR descriptions over raw commit messages when both are available.
---
## Step 3 — Detect Breaking Changes
Scan for breaking changes using these signals. **Any match flags the release as
containing breaking changes**, which affects version bump requirements and
changelog structure.
Look for:
### 3a. Migration Files
- destructive migrations
- removed or changed API fields/endpoints
- renamed or removed config keys
- `major` changesets
- `BREAKING:` or `BREAKING CHANGE:` commit signals
Check for new migration files since the last tag:
Key commands:
```bash
git diff --name-only v{last}..HEAD -- packages/db/src/migrations/
```
- **New migration files exist** = DB migration required in upgrade.
- Inspect migration content: look for `DROP`, `ALTER ... DROP`, `RENAME` to
distinguish destructive vs. additive migrations.
- Additive-only migrations (new tables, new nullable columns, new indexes) are
safe but should still be mentioned.
- Destructive migrations (column drops, type changes, table drops) = breaking.
### 3b. Schema Changes
```bash
git diff v{last}..HEAD -- packages/db/src/schema/
```
Look for:
- Removed or renamed columns/tables
- Changed column types
- Removed default values or nullable constraints
- These indicate breaking DB changes even if no explicit migration file exists
### 3c. API Route Changes
```bash
git diff v{last}..HEAD -- server/src/routes/ server/src/api/
git log v{last}..HEAD --format="%s" | rg -n 'BREAKING CHANGE|BREAKING:|^[a-z]+!:' || true
```
Look for:
- Removed endpoints
- Changed request/response shapes (removed fields, type changes)
- Changed authentication requirements
If the requested bump is lower than the minimum required bump, flag that before the release proceeds.
### 3d. Config Changes
## Step 4 — Categorize for Users
```bash
git diff v{last}..HEAD -- cli/src/config/ packages/*/src/*config*
```
Use these stable changelog sections:
Look for renamed, removed, or restructured configuration keys.
- `Breaking Changes`
- `Highlights`
- `Improvements`
- `Fixes`
- `Upgrade Guide` when needed
### 3e. Changeset Severity
Exclude purely internal refactors, CI changes, and docs-only work unless they materially affect users.
Any `.changeset/*.md` file with a `major` bump = explicitly flagged breaking.
Guidelines:
### 3f. Commit Conventions
- group related commits into one user-facing entry
- write from the user perspective
- keep highlights short and concrete
- spell out upgrade actions for breaking changes
Scan commit messages for:
- `BREAKING:` or `BREAKING CHANGE:` prefix
- `!` after the type in conventional commits (e.g. `feat!:`, `fix!:`)
## Step 5 — Write the File
### Version Bump Rules
| Condition | Minimum Bump |
|---|---|
| Destructive migration (DROP, RENAME) | `major` |
| Removed API endpoints or fields | `major` |
| Any `major` changeset or `BREAKING:` commit | `major` |
| New (additive) migration | `minor` |
| New features (`feat:` commits, `minor` changesets) | `minor` |
| Bug fixes only | `patch` |
If the planned bump is lower than the minimum required, **warn the reviewer**
and recommend the correct bump level.
---
## Step 4 — Categorize Changes
Assign every meaningful change to one of these categories:
| Category | What Goes Here | Shows in User Notes? |
|---|---|---|
| **Breaking Changes** | Anything requiring user action to upgrade | Yes (top, with warning) |
| **Highlights** | New user-visible features, major behavioral changes | Yes (with 1-2 sentence descriptions) |
| **Improvements** | Enhancements to existing features | Yes (bullet list) |
| **Fixes** | Bug fixes | Yes (bullet list) |
| **Internal** | Refactoring, deps, CI, tests, docs | No (dev changelog only) |
### Categorization Heuristics
Use these signals to auto-categorize. When signals conflict, prefer the
higher-visibility category and flag for human review.
| Signal | Category |
|---|---|
| Commit touches migration files, schema changes | Breaking Change (if destructive) |
| Changeset marked `major` | Breaking Change |
| Commit message has `BREAKING:` or `!:` | Breaking Change |
| New UI components, new routes, new API endpoints | Highlight |
| Commit message starts with `feat:` or `add:` | Highlight or Improvement |
| Changeset marked `minor` | Highlight |
| Commit message starts with `fix:` or `bug:` | Fix |
| Changeset marked `patch` | Fix or Improvement |
| Commit message starts with `chore:`, `refactor:`, `ci:`, `test:`, `docs:` | Internal |
| PR has detailed body with user-facing description | Use PR body as the description |
### Writing Good Descriptions
- **Highlights** get 1-2 sentence descriptions explaining the user benefit.
Write from the user's perspective ("You can now..." not "Added a component that...").
- **Improvements and Fixes** are concise bullet points.
- **Breaking Changes** get detailed descriptions including what changed,
why, and what the user needs to do.
- Group related commits into a single changelog entry. Five commits implementing
one feature = one Highlight entry, not five bullets.
- Omit purely internal changes from user-facing notes entirely.
---
## Step 5 — Write the Changelog
Output the changelog to `releases/v{version}.md` using this template:
Template:
```markdown
# v{version}
> Released: {YYYY-MM-DD}
{If breaking changes detected, include this section:}
## Breaking Changes
> **Action required before upgrading.** Read the Upgrade Guide below.
- **{Breaking change title}** — {What changed and why. What the user needs to do.}
## Highlights
- **{Feature name}** — {1-2 sentence description of what it does and why it matters.}
## Improvements
- {Concise description of improvement}
## Fixes
- {Concise description of fix}
---
{If breaking changes detected, include this section:}
## Upgrade Guide
### Before You Update
1. **Back up your database.**
- SQLite: `cp paperclip.db paperclip.db.backup`
- Postgres: `pg_dump -Fc paperclip > paperclip-pre-{version}.dump`
2. **Note your current version:** `paperclip --version`
### After Updating
{Specific steps: run migrations, update configs, etc.}
### Rolling Back
If something goes wrong:
1. Restore your database backup
2. `npm install @paperclipai/server@{previous-version}`
```
### Template Rules
Omit empty sections except `Highlights`, `Improvements`, and `Fixes`, which should usually exist.
- Omit any empty section entirely (don't show "## Fixes" with no bullets).
- The Breaking Changes section always comes first when present.
- The Upgrade Guide always comes last when present.
- Use `**bold**` for feature/change names, regular text for descriptions.
- Keep the entire changelog scannable — a busy user should get the gist from
headings and bold text alone.
## Step 6 — Review Before Release
---
Before handing it off:
## Step 6 — Present for Review
1. confirm the heading is the stable version only
2. confirm there is no `-canary` language in the title or filename
3. confirm any breaking changes have an upgrade path
4. present the draft for human sign-off
After generating the draft:
1. **Show the full changelog** to the reviewer (CTO or whoever triggered the release).
2. **Flag ambiguous items** — commits you weren't sure how to categorize, or
items that might be breaking but aren't clearly signaled.
3. **Flag version bump mismatches** — if the planned bump is lower than what
the changes warrant.
4. **Wait for approval** before considering the changelog final.
If the reviewer requests edits, update `releases/v{version}.md` accordingly.
Do not proceed to publishing, website updates, or social announcements. Those
are handled by the `release` coordination skill (separate from this one).
---
## Directory Convention
Release changelogs live in `releases/` at the repo root:
```
releases/
v0.2.7.md
v0.3.0.md
...
```
Each file is named `v{version}.md` matching the git tag. This directory is
committed to the repo and serves as the source of truth for release history.
The `releases/` directory should be created with a `.gitkeep` if it doesn't
exist yet.
---
## Quick Reference
```bash
# Full workflow summary:
# 1. Find last tag
LAST_TAG=$(git tag --sort=-version:refname | head -1)
# 2. Commits since last tag
git log $LAST_TAG..HEAD --oneline --no-merges
# 3. Files changed (for breaking change detection)
git diff --name-only $LAST_TAG..HEAD
# 4. Migration changes specifically
git diff --name-only $LAST_TAG..HEAD -- packages/db/src/migrations/
# 5. Schema changes
git diff $LAST_TAG..HEAD -- packages/db/src/schema/
# 6. Unconsumed changesets
ls .changeset/*.md | grep -v README.md
# 7. Merged PRs (if gh available)
gh pr list --state merged --search "merged:>=$(git log -1 --format=%aI $LAST_TAG)" \
--json number,title,body,labels
```
This skill never publishes anything. It only prepares the stable changelog artifact.

View File

@@ -1,402 +1,234 @@
---
name: release
description: >
Coordinate a full Paperclip release across engineering, website publishing,
and social announcement. Use when CTO/CEO requests "do a release" or
"release vX.Y.Z". Runs pre-flight checks, generates changelog via
release-changelog, executes npm release, creates cross-project follow-up
tasks, and posts a release wrap-up.
Coordinate a full Paperclip release across engineering verification, npm,
GitHub, website publishing, and announcement follow-up. Use when leadership
asks to ship a release, not merely to discuss version bumps.
---
# Release Coordination Skill
Run the full Paperclip release process as an organizational workflow, not just
an npm publish.
Run the full Paperclip release as a maintainer workflow, not just an npm publish.
This skill coordinates:
- User-facing changelog generation (`release-changelog` skill)
- Canary publish to npm (`scripts/release.sh --canary`)
- Docker smoke test of the canary (`scripts/docker-onboard-smoke.sh`)
- Promotion to `latest` after canary is verified
- Website publishing task creation
- CMO announcement task creation
- Final release summary with links
---
- stable changelog drafting via `release-changelog`
- prerelease canary publishing via `scripts/release.sh --canary`
- Docker smoke testing via `scripts/docker-onboard-smoke.sh`
- stable publishing via `scripts/release.sh`
- pushing the release commit and tag
- GitHub Release creation via `scripts/create-github-release.sh`
- website / announcement follow-up tasks
## Trigger
Use this skill when leadership asks for:
- "do a release"
- "release {patch|minor|major}"
- "release vX.Y.Z"
---
- "do a release"
- "ship the next patch/minor/major"
- "release vX.Y.Z"
## Preconditions
Before proceeding, verify all of the following:
1. `skills/release-changelog/SKILL.md` exists and is usable.
2. The `release-changelog` dependency work is complete/reviewed before running this flow.
3. App repo working tree is clean.
4. There are commits since the last release tag.
5. You have release permissions (`npm whoami` succeeds for real publish).
6. If running via Paperclip, you have issue context for posting status updates.
2. The repo working tree is clean, including untracked files.
3. There are commits since the last stable tag.
4. The release SHA has passed the verification gate or is about to.
5. npm publish rights are available locally, or the GitHub release workflow is being used with trusted publishing.
6. If running through Paperclip, you have issue context for status updates and follow-up task creation.
If any precondition fails, stop and report the blocker.
---
## Inputs
Collect these inputs up front:
- Release request source issue (if in Paperclip)
- Requested bump (`patch|minor|major`) or explicit version (`vX.Y.Z`)
- Whether this run is dry-run or live publish
- Company/project context for follow-up issue creation
- requested bump: `patch`, `minor`, or `major`
- whether this run is a dry run or live release
- whether the release is being run locally or from GitHub Actions
- release issue / company context for website and announcement follow-up
---
## Step 0 — Release Model
## Step 0 — Idempotency Guards
Paperclip now uses this release model:
Each step in this skill is designed to be safely re-runnable. Before executing
any step, check whether it has already been completed:
1. Draft the **stable** changelog as `releases/vX.Y.Z.md`
2. Publish one or more **prerelease canaries** such as `X.Y.Z-canary.0`
3. Smoke test the canary via Docker
4. Publish the stable version `X.Y.Z`
5. Push the release commit and tag
6. Create the GitHub Release
7. Complete website and announcement surfaces
| Step | How to Check | If Already Done |
|---|---|---|
| Changelog | `releases/v{version}.md` exists | Read it, ask reviewer to confirm or update. Do NOT regenerate without asking. |
| Canary publish | `npm view paperclipai@{version}` succeeds | Skip canary publish. Proceed to smoke test. |
| Smoke test | Manual or scripted verification | If canary already verified, proceed to promote. |
| Promote | `git tag v{version}` exists | Skip promotion entirely. A tag means the version is already promoted to latest. |
| Website task | Search Paperclip issues for "Publish release notes for v{version}" | Skip creation. Link the existing task. |
| CMO task | Search Paperclip issues for "release announcement tweet for v{version}" | Skip creation. Link the existing task. |
Critical consequence:
**The golden rule:** If a git tag `v{version}` already exists, the release is
fully promoted. Only post-publish tasks (website, CMO, wrap-up) should proceed.
If the version exists on npm but there's no git tag, the canary was published but
not yet promoted — resume from smoke test.
- Canaries do **not** use promote-by-dist-tag anymore.
- The changelog remains stable-only. Do not create `releases/vX.Y.Z-canary.N.md`.
**Iterating on changelogs:** You can re-run this skill with an existing changelog
to refine it _before_ the npm publish step. The `release-changelog` skill has
its own idempotency check and will ask the reviewer what to do with an existing
file. This is the expected workflow for iterating on release notes.
## Step 1 — Decide the Stable Version
---
## Step 1 - Pre-flight and Version Decision
Run pre-flight in the App repo root:
Use the last stable tag as the base:
```bash
LAST_TAG=$(git tag --sort=-version:refname | head -1)
git diff --quiet && git diff --cached --quiet
git log "${LAST_TAG}..HEAD" --oneline --no-merges | head -50
```
Then detect minimum required bump:
```bash
# migrations
LAST_TAG=$(git tag --list 'v*' --sort=-version:refname | head -1)
git log "${LAST_TAG}..HEAD" --oneline --no-merges
git diff --name-only "${LAST_TAG}..HEAD" -- packages/db/src/migrations/
# schema deltas
git diff "${LAST_TAG}..HEAD" -- packages/db/src/schema/
# breaking commit conventions
git log "${LAST_TAG}..HEAD" --format="%s" | rg -n 'BREAKING CHANGE|BREAKING:|^[a-z]+!:' || true
```
Bump policy:
- Destructive migration/API removal/major changeset/breaking commit -> `major`
- Additive migrations or clear new features -> at least `minor`
- Fixes-only -> `patch`
If requested bump is lower than required minimum, escalate bump and explain why.
- destructive migrations, removed APIs, breaking config changes -> `major`
- additive migrations or clearly user-visible features -> at least `minor`
- fixes only -> `patch`
---
If the requested bump is too low, escalate it and explain why.
## Step 2 - Generate Changelog Draft
## Step 2 — Draft the Stable Changelog
First, check if `releases/v{version}.md` already exists. If it does, the
`release-changelog` skill will detect this and ask the reviewer whether to keep,
regenerate, or update it. **Do not silently overwrite an existing changelog.**
Invoke `release-changelog` and generate:
Invoke the `release-changelog` skill and produce:
- `releases/v{version}.md`
- Sections ordered as: Breaking Changes (if any), Highlights, Improvements, Fixes, Upgrade Guide (if any)
- `releases/vX.Y.Z.md`
Required behavior:
- Present the draft for human review.
- Flag ambiguous categorization items.
- Flag bump mismatches before publish.
- Do not publish until reviewer confirms.
Rules:
---
- review the draft with a human before publish
- preserve manual edits if the file already exists
- keep the heading and filename stable-only, for example `v1.2.3`
- do not create a separate canary changelog file
## Step 3 — Publish Canary
## Step 3 — Verify the Release SHA
The canary is the gatekeeper: every release goes to npm as a canary first. The
`latest` tag is never touched until the canary passes smoke testing.
**Idempotency check:** Before publishing, check if this version already exists
on npm:
Run the standard gate:
```bash
# Check if canary is already published
npm view paperclipai@{version} version 2>/dev/null && echo "ALREADY_PUBLISHED" || echo "NOT_PUBLISHED"
# Also check git tag
git tag -l "v{version}"
pnpm -r typecheck
pnpm test:run
pnpm build
```
- If a git tag exists → the release is already fully promoted. Skip to Step 6.
- If the version exists on npm but no git tag → canary was published but not yet
promoted. Skip to Step 4 (smoke test).
- If neither exists → proceed with canary publish.
If the release will be run through GitHub Actions, the workflow can rerun this gate. Still report whether the local tree currently passes.
### Publishing the canary
## Step 4 — Publish a Canary
Use `release.sh` with the `--canary` flag (see script changes below):
Run:
```bash
# Dry run first
./scripts/release.sh {patch|minor|major} --canary --dry-run
# Publish canary (after dry-run review)
./scripts/release.sh {patch|minor|major} --canary
```
This publishes all packages to npm with the `canary` dist-tag. The `latest` tag
is **not** updated. Users running `npx paperclipai onboard` still get the
previous stable version.
What this means:
After publish, verify the canary is accessible:
- npm receives `X.Y.Z-canary.N` under dist-tag `canary`
- `latest` remains unchanged
- no git tag is created
- the script cleans the working tree afterward
After publish, verify:
```bash
npm view paperclipai@canary version
# Should show the new version
```
**How `--canary` works in release.sh:**
- Steps 1-5 are the same (preflight, changeset, version, build, CLI bundle)
- Step 6 uses `npx changeset publish --tag canary` instead of `npx changeset publish`
- Step 7 does NOT commit or tag — the commit and tag happen later in the promote
step, only after smoke testing passes
The user install path is:
**Script changes required:** Add `--canary` support to `scripts/release.sh`:
- Parse `--canary` flag alongside `--dry-run`
- When `--canary`: pass `--tag canary` to `changeset publish`
- When `--canary`: skip the git commit and tag step (Step 7)
- When NOT `--canary`: behavior is unchanged (backwards compatible)
```bash
npx paperclipai@canary onboard
```
---
## Step 5 — Smoke Test the Canary
## Step 4 — Smoke Test the Canary
Run the canary in a clean Docker environment to verify `npx paperclipai onboard`
works end-to-end.
### Automated smoke test
Use the existing Docker smoke test infrastructure with the canary version:
Run:
```bash
PAPERCLIPAI_VERSION=canary ./scripts/docker-onboard-smoke.sh
```
This builds a clean Ubuntu container, installs `paperclipai@canary` via npx, and
runs the onboarding flow. The UI is accessible at `http://localhost:3131`.
Confirm:
### What to verify
1. install succeeds
2. onboarding completes
3. server boots
4. UI loads
5. basic company/dashboard flow works
At minimum, confirm:
If smoke testing fails:
1. **Container starts** — no npm install errors, no missing dependencies
2. **Onboarding completes** — the wizard runs through without crashes
3. **Server boots** — UI is accessible at the expected port
4. **Basic operations** — can create a company, view the dashboard
- stop the stable release
- fix the issue
- publish another canary
- repeat the smoke test
For a more thorough check (stretch goal — can be automated later):
Each retry should create a higher canary ordinal, while the stable target version can stay the same.
5. **Browser automation** — script Playwright/Puppeteer to walk through onboard
in the Docker container's browser and verify key pages render
## Step 6 — Publish Stable
### If smoke test fails
- Do NOT promote the canary.
- Fix the issue, publish a new canary (re-run Step 3 — idempotency guards allow
this since there's no git tag yet).
- Re-run the smoke test.
### If smoke test passes
Proceed to Step 5 (promote).
---
## Step 5 — Promote Canary to Latest
Once the canary passes smoke testing, promote it to `latest` so that
`npx paperclipai onboard` picks up the new version.
### Promote on npm
Once the SHA is vetted, run:
```bash
# For each published package, move the dist-tag from canary to latest
npm dist-tag add paperclipai@{version} latest
npm dist-tag add @paperclipai/server@{version} latest
npm dist-tag add @paperclipai/cli@{version} latest
npm dist-tag add @paperclipai/shared@{version} latest
npm dist-tag add @paperclipai/db@{version} latest
npm dist-tag add @paperclipai/adapter-utils@{version} latest
npm dist-tag add @paperclipai/adapter-claude-local@{version} latest
npm dist-tag add @paperclipai/adapter-codex-local@{version} latest
npm dist-tag add @paperclipai/adapter-openclaw-gateway@{version} latest
./scripts/release.sh {patch|minor|major} --dry-run
./scripts/release.sh {patch|minor|major}
```
**Script option:** Add `./scripts/release.sh --promote {version}` to automate
the dist-tag promotion for all packages.
Stable publish does this:
### Commit and tag
- publishes `X.Y.Z` to npm under `latest`
- creates the local release commit
- creates the local git tag `vX.Y.Z`
After promotion, finalize in git (this is what `release.sh` Step 7 normally
does, but was deferred during canary publish):
Stable publish does **not** push the release for you.
## Step 7 — Push and Create GitHub Release
After stable publish succeeds:
```bash
git add .
git commit -m "chore: release v{version}"
git tag "v{version}"
git push origin HEAD:master --follow-tags
./scripts/create-github-release.sh X.Y.Z
```
### Verify promotion
Use the stable changelog file as the GitHub Release notes source.
```bash
npm view paperclipai@latest version
# Should now show the new version
## Step 8 — Finish the Other Surfaces
# Final sanity check
npx --yes paperclipai@latest --version
```
Create or verify follow-up work for:
---
- website changelog publishing
- launch post / social announcement
- any release summary in Paperclip issue context
## Step 6 - Create Cross-Project Follow-up Tasks
**Idempotency check:** Before creating tasks, search for existing ones:
```
GET /api/companies/{companyId}/issues?q=release+notes+v{version}
GET /api/companies/{companyId}/issues?q=announcement+tweet+v{version}
```
If matching tasks already exist (check title contains the version), skip
creation and link the existing tasks instead. Do not create duplicates.
Create at least two tasks in Paperclip (only if they don't already exist):
1. Website task: publish changelog for `v{version}`
2. CMO task: draft announcement tweet for `v{version}`
When creating tasks:
- Set `parentId` to the release issue id.
- Carry over `goalId` from the parent issue when present.
- Include `billingCode` for cross-team work when required by company policy.
- Mark website task `high` priority if release has breaking changes.
Suggested payloads:
```json
POST /api/companies/{companyId}/issues
{
"projectId": "{websiteProjectId}",
"parentId": "{releaseIssueId}",
"goalId": "{goalId-or-null}",
"billingCode": "{billingCode-or-null}",
"title": "Publish release notes for v{version}",
"priority": "medium",
"status": "todo",
"description": "Publish /changelog entry for v{version}. Include full markdown from releases/v{version}.md and prominent upgrade guide if breaking changes exist."
}
```
```json
POST /api/companies/{companyId}/issues
{
"projectId": "{workspaceProjectId}",
"parentId": "{releaseIssueId}",
"goalId": "{goalId-or-null}",
"billingCode": "{billingCode-or-null}",
"title": "Draft release announcement tweet for v{version}",
"priority": "medium",
"status": "todo",
"description": "Draft launch tweet with top 1-2 highlights, version number, and changelog URL. If breaking changes exist, include an explicit upgrade-guide callout."
}
```
---
## Step 7 - Wrap Up the Release Issue
Post a concise markdown update linking:
- Release issue
- Changelog file (`releases/v{version}.md`)
- npm package URL (both `@canary` and `@latest` after promotion)
- Canary smoke test result (pass/fail, what was tested)
- Website task
- CMO task
- Final changelog URL (once website publishes)
- Tweet URL (once published)
Completion rules:
- Keep issue `in_progress` until canary is promoted AND website + social tasks
are done.
- Mark `done` only when all required artifacts are published and linked.
- If waiting on another team, keep open with clear owner and next action.
---
## Release Flow Summary
The full release lifecycle is now:
```
1. Generate changelog → releases/v{version}.md (review + iterate)
2. Publish canary → npm @canary dist-tag (latest untouched)
3. Smoke test canary → Docker clean install verification
4. Promote to latest → npm @latest dist-tag + git tag + commit
5. Create follow-up tasks → website changelog + CMO tweet
6. Wrap up → link everything, close issue
```
At any point you can re-enter the flow — idempotency guards detect which steps
are already done and skip them. The changelog can be iterated before or after
canary publish. The canary can be re-published if the smoke test reveals issues
(just fix + re-run Step 3). Only after smoke testing passes does `latest` get
updated.
---
## Paperclip API Notes (When Running in Agent Context)
Use:
- `GET /api/companies/{companyId}/projects` to resolve website/workspace project IDs.
- `POST /api/companies/{companyId}/issues` to create follow-up tasks.
- `PATCH /api/issues/{issueId}` with comments for release progress.
For issue-modifying calls, include:
- `Authorization: Bearer $PAPERCLIP_API_KEY`
- `X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID`
---
These should reference the stable release, not the canary.
## Failure Handling
If blocked, update the release issue explicitly with:
- what failed
- exact blocker
- who must act next
- whether any release artifacts were partially published
If the canary is bad:
Never silently fail mid-release.
- publish another canary, do not ship stable
If stable npm publish succeeds but push or GitHub release creation fails:
- fix the git/GitHub issue immediately from the same checkout
- do not republish the same version
If `latest` is bad after stable publish:
```bash
./scripts/rollback-latest.sh <last-good-version>
```
Then fix forward with a new patch release.
## Output
When the skill completes, provide:
- stable version and, if relevant, the final canary version tested
- verification status
- npm status
- git tag / GitHub Release status
- website / announcement follow-up status
- rollback recommendation if anything is still partially complete