SKILL.md (13646B)
1 --- 2 name: "release-process" 3 description: "Step-by-step release checklist for Squad — prevents v0.8.22-style disasters" 4 domain: "release-management" 5 confidence: "high" 6 source: "team-decision" 7 --- 8 9 ## Context 10 11 This is the **definitive release runbook** for Squad. Born from the v0.8.22 release disaster (4-part semver mangled by npm, draft release never triggered publish, wrong NPM_TOKEN type, 6+ hours of broken `latest` dist-tag). 12 13 **Rule:** No agent releases Squad without following this checklist. No exceptions. No improvisation. 14 15 --- 16 17 ## Pre-Release Validation 18 19 Before starting ANY release work, validate the following: 20 21 ### 1. Version Number Validation 22 23 **Rule:** Only 3-part semver (major.minor.patch) or prerelease (major.minor.patch-tag.N) are valid. 4-part versions (0.8.21.4) are NOT valid semver and npm will mangle them. 24 25 ```bash 26 # Check version is valid semver 27 node -p "require('semver').valid('0.8.22')" 28 # Output: '0.8.22' = valid 29 # Output: null = INVALID, STOP 30 31 # For prerelease versions 32 node -p "require('semver').valid('0.8.23-preview.1')" 33 # Output: '0.8.23-preview.1' = valid 34 ``` 35 36 **If `semver.valid()` returns `null`:** STOP. Fix the version. Do NOT proceed. 37 38 ### 2. NPM_TOKEN Verification 39 40 **Rule:** NPM_TOKEN must be an **Automation token** (no 2FA required). User tokens with 2FA will fail in CI with EOTP errors. 41 42 ```bash 43 # Check token type (requires npm CLI authenticated) 44 npm token list 45 ``` 46 47 Look for: 48 - ✅ `read-write` tokens with NO 2FA requirement = Automation token (correct) 49 - ❌ Tokens requiring OTP = User token (WRONG, will fail in CI) 50 51 **How to create an Automation token:** 52 1. Go to npmjs.com → Settings → Access Tokens 53 2. Click "Generate New Token" 54 3. Select **"Automation"** (NOT "Publish") 55 4. Copy token and save as GitHub secret: `NPM_TOKEN` 56 57 **If using a User token:** STOP. Create an Automation token first. 58 59 ### 3. Branch and Tag State 60 61 **Rule:** Release from `main` branch. Ensure clean state, no uncommitted changes, latest from origin. 62 63 ```bash 64 # Ensure on main and clean 65 git checkout main 66 git pull origin main 67 git status # Should show: "nothing to commit, working tree clean" 68 69 # Check tag doesn't already exist 70 git tag -l "v0.8.22" 71 # Output should be EMPTY. If tag exists, release already done or collision. 72 ``` 73 74 **If tag exists:** STOP. Either release was already done, or there's a collision. Investigate before proceeding. 75 76 ### 4. Disable bump-build.mjs 77 78 **Rule:** `bump-build.mjs` is for dev builds ONLY. It must NOT run during release builds (it increments build numbers, creating 4-part versions). 79 80 ```bash 81 # Set env var to skip bump-build.mjs 82 export SKIP_BUILD_BUMP=1 83 84 # Verify it's set 85 echo $SKIP_BUILD_BUMP 86 # Output: 1 87 ``` 88 89 **For Windows PowerShell:** 90 ```powershell 91 $env:SKIP_BUILD_BUMP = "1" 92 ``` 93 94 **If not set:** `bump-build.mjs` will run and mutate versions. This causes disasters (see v0.8.22). 95 96 --- 97 98 ## Release Workflow 99 100 ### Step 1: Version Bump 101 102 Update version in all 3 package.json files (root + both workspaces) in lockstep. 103 104 ```bash 105 # Set target version (no 'v' prefix) 106 VERSION="0.8.22" 107 108 # Validate it's valid semver BEFORE proceeding 109 node -p "require('semver').valid('$VERSION')" 110 # Must output the version string, NOT null 111 112 # Update all 3 package.json files 113 npm version $VERSION --workspaces --include-workspace-root --no-git-tag-version 114 115 # Verify all 3 match 116 grep '"version"' package.json packages/squad-sdk/package.json packages/squad-cli/package.json 117 # All 3 should show: "version": "0.8.22" 118 ``` 119 120 **Checkpoint:** All 3 package.json files have identical versions. Run `semver.valid()` one more time to be sure. 121 122 ### Step 2: Commit and Tag 123 124 ```bash 125 # Commit version bump 126 git add package.json packages/squad-sdk/package.json packages/squad-cli/package.json 127 git commit -m "chore: bump version to $VERSION 128 129 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>" 130 131 # Create tag (with 'v' prefix) 132 git tag -a "v$VERSION" -m "Release v$VERSION" 133 134 # Push commit and tag 135 git push origin main 136 git push origin "v$VERSION" 137 ``` 138 139 **Checkpoint:** Tag created and pushed. Verify with `git tag -l "v$VERSION"`. 140 141 ### Step 3: Create GitHub Release 142 143 **CRITICAL:** Release must be **published**, NOT draft. Draft releases don't trigger `publish.yml` workflow. 144 145 ```bash 146 # Create GitHub Release (NOT draft) 147 gh release create "v$VERSION" \ 148 --title "v$VERSION" \ 149 --notes "Release notes go here" \ 150 --latest 151 152 # Verify release is PUBLISHED (not draft) 153 gh release view "v$VERSION" 154 # Output should NOT contain "(draft)" 155 ``` 156 157 **If output contains `(draft)`:** STOP. Delete the release and recreate without `--draft` flag. 158 159 ```bash 160 # If you accidentally created a draft, fix it: 161 gh release edit "v$VERSION" --draft=false 162 ``` 163 164 **Checkpoint:** Release is published (NOT draft). The `release: published` event fired and triggered `publish.yml`. 165 166 ### Step 4: Monitor Workflow 167 168 The `publish.yml` workflow should start automatically within 10 seconds of release creation. 169 170 ```bash 171 # Watch workflow runs 172 gh run list --workflow=publish.yml --limit 1 173 174 # Get detailed status 175 gh run view --log 176 ``` 177 178 **Expected flow:** 179 1. `publish-sdk` job runs → publishes `@bradygaster/squad-sdk` 180 2. Verify step runs with retry loop (up to 5 attempts, 15s interval) to confirm SDK on npm registry 181 3. `publish-cli` job runs → publishes `@bradygaster/squad-cli` 182 4. Verify step runs with retry loop to confirm CLI on npm registry 183 184 **If workflow fails:** Check the logs. Common issues: 185 - EOTP error = wrong NPM_TOKEN type (use Automation token) 186 - Verify step timeout = npm propagation delay (retry loop should handle this, but propagation can take up to 2 minutes in rare cases) 187 - Version mismatch = package.json version doesn't match tag 188 189 **Checkpoint:** Both jobs succeeded. Workflow shows green checkmarks. 190 191 ### Step 5: Verify npm Publication 192 193 Manually verify both packages are on npm with correct `latest` dist-tag. 194 195 ```bash 196 # Check SDK 197 npm view @bradygaster/squad-sdk version 198 # Output: 0.8.22 199 200 npm dist-tag ls @bradygaster/squad-sdk 201 # Output should show: latest: 0.8.22 202 203 # Check CLI 204 npm view @bradygaster/squad-cli version 205 # Output: 0.8.22 206 207 npm dist-tag ls @bradygaster/squad-cli 208 # Output should show: latest: 0.8.22 209 ``` 210 211 **If versions don't match:** Something went wrong. Check workflow logs. DO NOT proceed with GitHub Release announcement until npm is correct. 212 213 **Checkpoint:** Both packages show correct version. `latest` dist-tags point to the new version. 214 215 ### Step 6: Test Installation 216 217 Verify packages can be installed from npm (real-world smoke test). 218 219 ```bash 220 # Create temp directory 221 mkdir /tmp/squad-release-test && cd /tmp/squad-release-test 222 223 # Test SDK installation 224 npm init -y 225 npm install @bradygaster/squad-sdk 226 node -p "require('@bradygaster/squad-sdk/package.json').version" 227 # Output: 0.8.22 228 229 # Test CLI installation 230 npm install -g @bradygaster/squad-cli 231 squad --version 232 # Output: 0.8.22 233 234 # Cleanup 235 cd - 236 rm -rf /tmp/squad-release-test 237 ``` 238 239 **If installation fails:** npm registry issue or package metadata corruption. DO NOT announce release until this works. 240 241 **Checkpoint:** Both packages install cleanly. Versions match. 242 243 ### Step 7: Sync dev to Next Preview 244 245 After main release, sync dev to the next preview version. 246 247 ```bash 248 # Checkout dev 249 git checkout dev 250 git pull origin dev 251 252 # Bump to next preview version (e.g., 0.8.23-preview.1) 253 NEXT_VERSION="0.8.23-preview.1" 254 255 # Validate semver 256 node -p "require('semver').valid('$NEXT_VERSION')" 257 # Must output the version string, NOT null 258 259 # Update all 3 package.json files 260 npm version $NEXT_VERSION --workspaces --include-workspace-root --no-git-tag-version 261 262 # Commit 263 git add package.json packages/squad-sdk/package.json packages/squad-cli/package.json 264 git commit -m "chore: bump dev to $NEXT_VERSION 265 266 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>" 267 268 # Push 269 git push origin dev 270 ``` 271 272 **Checkpoint:** dev branch now shows next preview version. Future dev builds will publish to `@preview` dist-tag. 273 274 --- 275 276 ## Manual Publish (Fallback) 277 278 If `publish.yml` workflow fails or needs to be bypassed, use `workflow_dispatch` to manually trigger publish. 279 280 ```bash 281 # Trigger manual publish 282 gh workflow run publish.yml -f version="0.8.22" 283 284 # Monitor the run 285 gh run watch 286 ``` 287 288 **Rule:** Only use this if automated publish failed. Always investigate why automation failed and fix it for next release. 289 290 --- 291 292 ## Rollback Procedure 293 294 If a release is broken and needs to be rolled back: 295 296 ### 1. Unpublish from npm (Nuclear Option) 297 298 **WARNING:** npm unpublish is time-limited (24 hours) and leaves the version slot burned. Only use if version is critically broken. 299 300 ```bash 301 # Unpublish (requires npm owner privileges) 302 npm unpublish @bradygaster/squad-sdk@0.8.22 303 npm unpublish @bradygaster/squad-cli@0.8.22 304 ``` 305 306 ### 2. Deprecate on npm (Preferred) 307 308 **Preferred approach:** Mark version as deprecated, publish a hotfix. 309 310 ```bash 311 # Deprecate broken version 312 npm deprecate @bradygaster/squad-sdk@0.8.22 "Broken release, use 0.8.22.1 instead" 313 npm deprecate @bradygaster/squad-cli@0.8.22 "Broken release, use 0.8.22.1 instead" 314 315 # Publish hotfix version 316 # (Follow this runbook with version 0.8.22.1) 317 ``` 318 319 ### 3. Delete GitHub Release and Tag 320 321 ```bash 322 # Delete GitHub Release 323 gh release delete "v0.8.22" --yes 324 325 # Delete tag locally and remotely 326 git tag -d "v0.8.22" 327 git push origin --delete "v0.8.22" 328 ``` 329 330 ### 4. Revert Commit on main 331 332 ```bash 333 # Revert version bump commit 334 git checkout main 335 git revert HEAD 336 git push origin main 337 ``` 338 339 **Checkpoint:** Tag and release deleted. main branch reverted. npm packages deprecated or unpublished. 340 341 --- 342 343 ## Common Failure Modes 344 345 ### EOTP Error (npm OTP Required) 346 347 **Symptom:** Workflow fails with `EOTP` error. 348 **Root cause:** NPM_TOKEN is a User token with 2FA enabled. CI can't provide OTP. 349 **Fix:** Replace NPM_TOKEN with an Automation token (no 2FA). See "NPM_TOKEN Verification" above. 350 351 ### Verify Step 404 (npm Propagation Delay) 352 353 **Symptom:** Verify step fails with 404 even though publish succeeded. 354 **Root cause:** npm registry propagation delay (5-30 seconds). 355 **Fix:** Verify step now has retry loop (5 attempts, 15s interval). Should auto-resolve. If not, wait 2 minutes and re-run workflow. 356 357 ### Version Mismatch (package.json ≠ tag) 358 359 **Symptom:** Verify step fails with "Package version (X) does not match target version (Y)". 360 **Root cause:** package.json version doesn't match the tag version. 361 **Fix:** Ensure all 3 package.json files were updated in Step 1. Re-run `npm version` if needed. 362 363 ### 4-Part Version Mangled by npm 364 365 **Symptom:** Published version on npm doesn't match package.json (e.g., 0.8.21.4 became 0.8.2-1.4). 366 **Root cause:** 4-part versions are NOT valid semver. npm's parser misinterprets them. 367 **Fix:** NEVER use 4-part versions. Only 3-part (0.8.22) or prerelease (0.8.23-preview.1). Run `semver.valid()` before ANY commit. 368 369 ### Draft Release Didn't Trigger Workflow 370 371 **Symptom:** Release created but `publish.yml` never ran. 372 **Root cause:** Release was created as a draft. Draft releases don't emit `release: published` event. 373 **Fix:** Edit release and change to published: `gh release edit "v$VERSION" --draft=false`. Workflow should trigger immediately. 374 375 --- 376 377 ## Validation Checklist 378 379 Before starting ANY release, confirm: 380 381 - [ ] Version is valid semver: `node -p "require('semver').valid('VERSION')"` returns the version string (NOT null) 382 - [ ] NPM_TOKEN is an Automation token (no 2FA): `npm token list` shows `read-write` without OTP requirement 383 - [ ] Branch is clean: `git status` shows "nothing to commit, working tree clean" 384 - [ ] Tag doesn't exist: `git tag -l "vVERSION"` returns empty 385 - [ ] `SKIP_BUILD_BUMP=1` is set: `echo $SKIP_BUILD_BUMP` returns `1` 386 387 Before creating GitHub Release: 388 389 - [ ] All 3 package.json files have matching versions: `grep '"version"' package.json packages/*/package.json` 390 - [ ] Commit is pushed: `git log origin/main..main` returns empty 391 - [ ] Tag is pushed: `git ls-remote --tags origin vVERSION` returns the tag SHA 392 393 After GitHub Release: 394 395 - [ ] Release is published (NOT draft): `gh release view "vVERSION"` output doesn't contain "(draft)" 396 - [ ] Workflow is running: `gh run list --workflow=publish.yml --limit 1` shows "in_progress" 397 398 After workflow completes: 399 400 - [ ] Both jobs succeeded: Workflow shows green checkmarks 401 - [ ] SDK on npm: `npm view @bradygaster/squad-sdk version` returns correct version 402 - [ ] CLI on npm: `npm view @bradygaster/squad-cli version` returns correct version 403 - [ ] `latest` tags correct: `npm dist-tag ls @bradygaster/squad-sdk` shows `latest: VERSION` 404 - [ ] Packages install: `npm install @bradygaster/squad-cli` succeeds 405 406 After dev sync: 407 408 - [ ] dev branch has next preview version: `git show dev:package.json | grep version` shows next preview 409 410 --- 411 412 ## Post-Mortem Reference 413 414 This skill was created after the v0.8.22 release disaster. Full retrospective: `.squad/decisions/inbox/keaton-v0822-retrospective.md` 415 416 **Key learnings:** 417 1. No release without a runbook = improvisation = disaster 418 2. Semver validation is mandatory — 4-part versions break npm 419 3. NPM_TOKEN type matters — User tokens with 2FA fail in CI 420 4. Draft releases are a footgun — they don't trigger automation 421 5. Retry logic is essential — npm propagation takes time 422 423 **Never again.**