diff --git a/.agents/skills/changeset-release-pr/SKILL.md b/.agents/skills/changeset-release-pr/SKILL.md index 0bf6d617e..a6c700d6a 100644 --- a/.agents/skills/changeset-release-pr/SKILL.md +++ b/.agents/skills/changeset-release-pr/SKILL.md @@ -1,16 +1,17 @@ --- name: changeset-release-pr -description: 'Prepare the next Roomote release PR: review what merged to develop, fill missing release notes, ensure meaningful user-facing features are documented, confirm patch/minor/major when unspecified, run the product version script, and open the final release PR. Use when asked to prep, cut, or write notes for a release.' +description: 'Prepare Roomote releases: cut the normal release PR from develop, or ship an urgent patch directly from latest main and synchronize its version back to develop. Use when asked to prep, cut, hotfix, or write notes for a release.' --- # Changeset Release PR -Use this skill to prepare the single release PR that cuts the next Roomote -version. The deliverable is a normal PR against `develop` containing the root -version bump, final `CHANGELOG.md` entry, any public documentation updates needed -for the release's meaningful user-facing features, and deletion of every +By default, use this skill to prepare the single release PR that cuts the next +Roomote version. The deliverable is a normal PR against `develop` containing the +root version bump, final `CHANGELOG.md` entry, any public documentation updates +needed for the release's meaningful user-facing features, and deletion of every consumed pending changeset. Merging it makes CI open the frozen Promote PR to -`main`. +`main`. For an urgent patch that cannot wait for that path, use the clearly +separated direct-to-main hotfix workflow below. ## How releases work here @@ -271,6 +272,178 @@ Commit the generated release artifacts on a feature branch and open a PR against - a note that squash-merging the PR cuts the release and automatically opens the frozen **Promote vX.Y.Z to production** PR against `main` +## Emergency direct-to-main hotfix path + +Use this path only for an urgent production patch that cannot wait for the +normal `develop` release flow above. The normal workflow remains the default. +This exception produces two PRs: + +1. a complete patch and generated release artifacts targeting `main` +2. a companion PR targeting `develop` that changes only root `package.json` and + `CHANGELOG.md` + +The production PR must merge first. Do not merge the companion until the tag, +images, and GitHub Release have all shipped successfully. + +### A. Establish the release state + +Fetch both branches and tags, then branch from the exact latest `main` tip: + +```bash +git fetch --tags origin \ + refs/heads/main:refs/remotes/origin/main \ + refs/heads/develop:refs/remotes/origin/develop +git switch --create hotfix/ origin/main +git tag --sort=-version:refname | head -5 +node -p "require('./package.json').version" +head -20 CHANGELOG.md +``` + +Before continuing, verify: + +- `HEAD` is exactly `origin/main`. +- The newest shipped `vX.Y.Z` tag, root version, and top changelog section agree. +- `main` has no pending non-README changeset that `pnpm run version` would + accidentally consume with the hotfix. +- No newer unshipped Promote PR or release candidate conflicts with the patch + version. Resolve release ordering rather than guessing. +- The fix is a patch and every required prerequisite is already on `main` or is + explicitly included because it is safe to ship with the fix. +- The patch preserves the schema N-1 rollback guarantee and does not require an + incompatible migration, destructive schema change, or coordinated rollout + that makes direct release unsafe. + +### B. Apply the complete fix + +Identify the exact merged fix on `develop`, including tests, docs, and required +supporting changes. Read its source PR and compare its merge commit to `main`; +do not copy only the most obvious runtime file. Cherry-pick the smallest complete +commit set, usually the squash commit: + +```bash +git cherry-pick +git diff --stat origin/main...HEAD +git diff --name-status origin/main...HEAD +git diff ^.. -- +git diff origin/main...HEAD -- +``` + +Account for every intentional difference caused by prerequisites or conflict +resolution. Exclude unrelated features, pending `develop` changesets, and +release artifacts from another version. + +When the fix depends on an external service or production-like integration, +repeat the smallest safe live check that proves the original symptom is fixed. +Record the environment and observable result without exposing secrets or private +data. If a live check is irrelevant or unavailable, say why and rely on focused +deterministic tests. + +### C. Generate and validate the patch release + +Add one hotfix-specific patch changeset using the same format as step 8. Commit +the complete fix and changeset separately when useful for auditability, then +consume the changeset through the repository-owned command: + +```bash +pnpm run version +``` + +Never run `changeset version`, and never hand-edit the generated version or +changelog heading. Polish the generated top changelog summary and highlights as +described in step 9, then commit root `package.json` and `CHANGELOG.md` as the +version commit. + +Verify that: + +- the root version is exactly one patch above the shipped `main` version +- the top `CHANGELOG.md` section contains the complete hotfix note +- the hotfix changeset was consumed and no non-README `.changeset/*.md` remains +- no workspace package version changed +- `origin/main...HEAD` contains only the complete fix plus root `package.json` + and `CHANGELOG.md` + +Preserve the changeset commit in branch history even though the version commit +deletes its file. Run the release scripts, focused product tests and typechecks, +formatting check, and full pre-push gate: + +```bash +pnpm test:release-scripts +pnpm exec oxfmt --check . +node scripts/pre-push-checks.mjs +``` + +Repeat any relevant live check after final conflict resolution or release edits. +Failed or unavailable checks are release blockers unless a maintainer explicitly +accepts and records the risk. + +### D. Open the production hotfix PR + +Open the hotfix PR against `main`. Include the source fix PR/commit and +prerequisites, previous and next versions, exact patch scope and intentional +differences, all validation and live-check results, rollback instructions, and a +link to the companion PR once available. Require a **merge commit**; never squash +or rebase this PR. + +Rollback guidance must distinguish two states: + +- Before the tag is consumed externally, revert the hotfix merge commit if that + is still operationally safe. +- After publication, redeploy the previous immutable `vX.Y.Z`; never move, + delete, or reuse a published tag. Describe data or schema cleanup separately + and preserve the N-1 rollback contract. + +### E. Prepare the companion develop sync + +After the production branch is final, create a branch from latest +`origin/develop` and bring over only the generated product version and changelog: + +```bash +git switch --create chore/sync--on-develop origin/develop +git restore --source -- package.json CHANGELOG.md +git diff --name-only origin/develop +git diff --exit-code -- package.json CHANGELOG.md +git diff --exit-code origin/develop -- .changeset +``` + +Do not cherry-pick the runtime fix; it should already be on `develop`. Do not run +`pnpm run version` on this branch because that would consume unrelated pending +`develop` changesets. The first diff must list exactly `package.json` and +`CHANGELOG.md`; the other two must be empty. If `develop` independently changed +either release artifact, reconcile deliberately without touching pending +changesets and stop if the companion cannot remain a truthful two-file sync. + +Open the companion PR against `develop`. It must be squash-merged only after all +production gates below succeed. Merging it early can make +`.github/workflows/release.yml` freeze the moving `develop` tip as +`release/vX.Y.Z` and open an incorrect Promote PR. + +### F. Enforce merge and release order + +Do not collapse or reorder these gates: + +1. Merge the production hotfix PR into `main` with a merge commit. +2. Wait for **Tag Product Release** to succeed for that `main` merge. +3. Verify the remote annotated tag exists and resolves to the released tree with + `git ls-remote --tags origin refs/tags/vX.Y.Z` and, after fetching, + `git rev-parse 'vX.Y.Z^{}'`. +4. Wait for the tag-triggered **Publish GHCR Images** workflow to succeed, + including image publication and GitHub Release creation. Verify the GitHub + Release and immutable image tags; tag creation alone is not completion. +5. Only then squash-merge the companion PR into `develop`. + +After the tag exists, the `develop` Release workflow sees an already shipped +version and exits without creating a candidate. If a production gate fails, +leave the companion open while the release is repaired or rolled back. + +PRs [#1840](https://github.com/RooCodeInc/Roomote/pull/1840) and +[#1841](https://github.com/RooCodeInc/Roomote/pull/1841) demonstrate this +two-branch workflow. Their Notion-specific live checks are an optional example, +not a requirement for unrelated hotfixes. + +End the hotfix path by reporting both PR URLs, released version, source fix and +prerequisite commits, exact file scope, live-check result or reason omitted, +validation results, current merge-order gate, and rollback risks. + ## Guardrails - Never edit `CHANGELOG.md` or the root version by hand; generate both with