From 0cc8b58f909be1718e675c906557a6f6b5a30def Mon Sep 17 00:00:00 2001 From: Simon Iribarren Date: Fri, 1 May 2026 12:57:18 +0200 Subject: [PATCH 1/2] infra[skiplog]: document release & backmerge PR conventions in sdk-pr-create skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sdk-pr-create skill covered normal feature/fix PRs but said nothing about the two other PR shapes that show up in the SDK release flow: - Release PRs (fork -> release--) — version bump + per-version changelog folder + aggregated CHANGELOG.md prepend. Merging triggers GPR publish. - Backmerge PRs (release-- -> main) — bring the release artifacts back into main after publish, usually hand-crafted (not a git merge) so main's deps don't get regressed. Both shapes have established title conventions in this repo (precedent: #1726, #1645, #1552, #1301, #1781, #1782, #1857) but the rules and skill never spelled them out, leading to mis-tagged titles like "chore[notask|skiplog]: backmerge ..." with a ticket present (notask is for omitting the ticket, not a generic backmerge marker). Adds an explicit "Release & Backmerge PRs" section to the skill with: - Title format for each shape, with and without a ticket - Tag rules ([skiplog] required for backmerge, [notask] only when no ticket, [notask|skiplog] is a legitimate combination of the two metadata tags) - Body content guidance (link to release PR, explain hand-crafted merges, reference precedent backmerge for previous version) - A decision-rule table mapping PR shape -> base -> title format Also tightens the quality checklist to catch the two most common mistakes: ticket + [notask] in the same title, and missing [skiplog] on a backmerge. Workspace consumers (tether) already symlink this file from .cursor/skills/sdk-pr-create/SKILL.md, so the change reaches them as soon as it lands on main. --- .cursor/skills/sdk-pr-create/SKILL.md | 79 ++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/.cursor/skills/sdk-pr-create/SKILL.md b/.cursor/skills/sdk-pr-create/SKILL.md index 264ea204ae..5f7da171cb 100644 --- a/.cursor/skills/sdk-pr-create/SKILL.md +++ b/.cursor/skills/sdk-pr-create/SKILL.md @@ -18,14 +18,18 @@ Generate PR titles and descriptions for SDK pod packages, following the team's t ## Workflow -1. Identify base (main) and current branch -2. Collect commits/diff from `main...origin/` -3. Infer ticket, prefix, and tags from changes (see Inference Strategy) -4. Only ask user for input when inference confidence is low -5. Generate title: `TICKET prefix[tags]: subject` -6. Fill template sections based on changes -7. Validate tag requirements ([bc]/[api]/[mod]) -8. Output complete PR description +1. Identify base and current branch: + - **Normal PR** → base is `main` + - **Release PR** → base is `release--` (PR is the release commit on the release branch) + - **Backmerge PR** → base is `main`, head is `release--` (or a hand-crafted branch with the release-branch artifacts) +2. Collect commits/diff from `...origin/` +3. Determine PR shape (normal / release / backmerge — see "Release & Backmerge PRs" below) +4. Infer ticket, prefix, and tags from changes (see Inference Strategy) +5. Only ask user for input when inference confidence is low +6. Generate title using the right format for the PR shape +7. Fill template sections based on changes +8. Validate tag requirements ([bc]/[api]/[mod]) +9. Output complete PR description ## Inference Strategy @@ -63,6 +67,61 @@ Infer first, ask only if uncertain: Fill template sections based on the diff analysis. Delete sections that don't apply. +## Release & Backmerge PRs + +The standard format `TICKET prefix[tags]: subject` covers normal feature/fix PRs. The release flow has two additional PR shapes that follow established conventions in this repo. **Pick the shape first**, then fill in the template. + +### Release PR (fork → release branch) + +Cuts a new package version onto a `release--` branch on `tetherto/qvac`. Bumps `package.json` version, adds the per-version changelog folder, and prepends an entry to the aggregated `CHANGELOG.md`. Merging this PR triggers GPR publish. + +**Title format:** + +- With ticket: `TICKET chore: release ` + - Example: `QVAC-18184 chore: release sdk 0.9.2` +- Without ticket: `chore[notask|skiplog]: release ` + - Example: `chore[notask|skiplog]: release @qvac/infer-base v0.4.1` (#1781) + +**Notes:** +- `[skiplog]` is **not** used when the release PR itself is what generates the changelog (it would be self-contradictory). +- Use `[notask]` only when there is no ticket; combine with `[skiplog]` via `|` if both are needed. +- Body should describe what's in the release at a high level, link to the per-version `CHANGELOG.md`, and call out any post-merge actions (npm publish via backmerge, etc.). + +### Backmerge PR (release branch → main) + +Brings the release artifacts (changelog folder, aggregated `CHANGELOG.md` entry, version bump in `package.json`) from the release branch back into `main` after the package is published. **Should usually be hand-crafted, not a literal `git merge`** — `main` often has progressed past the release branch on dependencies and other files, and a blind merge regresses them. + +**Title format:** + +- With ticket: `TICKET chore[skiplog]: backmerge release ` + - Examples: + - `QVAC-18184 chore[skiplog]: backmerge release sdk 0.9.2` (#1857) + - `QVAC-16776 chore[skiplog]: backmerge release-sdk-0.9.0 — changelog, NOTICE, model registry, and tooling fixes` (#1645) + - `QVAC-16495 chore[skiplog]: backmerge sdk v0.8.1 release, changelog & NOTICE` (#1301) +- Without ticket: `chore[notask|skiplog]: backmerge release ` + - Examples: + - `chore[notask|skiplog]: backmerge release sdk v0.8.3` (#1552) + - `chore[notask]: backmerge release @qvac/cli v0.2.2` (#1076) + +**Tag rules:** +- **`[skiplog]` is required** for backmerges — the changelog has already been written on the release branch; main shouldn't generate another entry from this PR. +- `[notask]` is **only** used when there is no ticket. Do **not** combine `[notask]` with a ticket in the title (`QVAC-XXX chore[notask|skiplog]: ...` is wrong). +- The two metadata tags `[notask]` and `[skiplog]` may be combined via `|` (e.g. `[notask|skiplog]`); this is distinct from the rule that content tags `[api]/[bc]/[mod]` cannot be combined. + +**Body content for backmerges:** +- Brief summary linking to the corresponding release PR and noting the publish has already happened. +- Explicit list of which files are touched (changelog folder, aggregated CHANGELOG.md, package.json version field). +- If the PR is hand-crafted rather than a `git merge`, **explain why** — usually a table of dependencies that have moved ahead on `main` and would be regressed by a blind merge. +- Reference the precedent backmerge PR for the previous version so reviewers can compare shape. + +### Decision rule (quick reference) + +| You are opening… | Base | Title format | +|---|---|---| +| A normal feature/fix/doc PR | `main` | `TICKET prefix[tags]: subject` | +| A release PR for a new version | `release--` | `TICKET chore: release ` (or `chore[notask|skiplog]: release ...` if no ticket) | +| A backmerge PR after publish | `main` | `TICKET chore[skiplog]: backmerge release ` (or `chore[notask|skiplog]: backmerge ...` if no ticket) | + ## Output Format ALWAYS output the PR in this copy-ready format, even when making corrections: @@ -117,7 +176,9 @@ gh pr view --repo UPSTREAM_ORG/REPO BRANCH --web Before outputting the PR description, verify: -- [ ] Title follows format: `TICKET prefix[tags]: subject` +- [ ] Title follows format: `TICKET prefix[tags]: subject` (or the release / backmerge variants above) +- [ ] If the PR has a ticket, `[notask]` is **NOT** in the title +- [ ] If the PR is a backmerge, `[skiplog]` is in the title - [ ] "What problem" describes user impact, not implementation - [ ] "How it solves" is high-level approach, not line-by-line - [ ] Unused sections are deleted From 35cbde560a36b90f8491b56c40656beea00abfd7 Mon Sep 17 00:00:00 2001 From: Simon Iribarren Date: Fri, 1 May 2026 12:59:55 +0200 Subject: [PATCH 2/2] infra[skiplog]: require standard sdk-pod headings for release & backmerge PR bodies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the earlier commit on this branch that added release & backmerge title conventions. Reviewer noted that the body of release / backmerge PRs should also use the standard sdk-pod template headings (🎯 What problem / 📝 How does it solve / 🧪 How was it tested) — not custom shapes like ## Summary / ## Changes / ## Test plan that I had been writing. Adds three things to the skill: - An explicit "Body always uses the same headings" callout in the Release & Backmerge PRs section, listing the standard headings and flagging custom alternatives as wrong. - A "Mapping release/backmerge content onto the template" subsection that shows how to fit each PR shape's content into the standard sections (e.g. for a backmerge: justify a hand-crafted merge in ## 🧪 How was it tested?, including the dependency-divergence table). - A new quality-checklist line forbidding custom headings. Also updates the decision-rule table to add a "Body" column making it explicit that all three PR shapes use the same template. --- .cursor/skills/sdk-pr-create/SKILL.md | 40 ++++++++++++++++++++------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/.cursor/skills/sdk-pr-create/SKILL.md b/.cursor/skills/sdk-pr-create/SKILL.md index 5f7da171cb..317c5c68e7 100644 --- a/.cursor/skills/sdk-pr-create/SKILL.md +++ b/.cursor/skills/sdk-pr-create/SKILL.md @@ -71,6 +71,17 @@ Fill template sections based on the diff analysis. Delete sections that don't ap The standard format `TICKET prefix[tags]: subject` covers normal feature/fix PRs. The release flow has two additional PR shapes that follow established conventions in this repo. **Pick the shape first**, then fill in the template. +**Body always uses the same headings.** Regardless of PR shape (normal / release / backmerge), the body MUST use the standard section headings from `.github/PULL_REQUEST_TEMPLATE/sdk-pod.md`: + +- `## 🎯 What problem does this PR solve?` (always) +- `## 📝 How does it solve it?` (always) +- `## 🧪 How was it tested?` (delete if not applicable) +- `## 💥 Breaking Changes` (only if `[bc]`) +- `## 🔌 API Changes` (only if `[api]`) +- `## 📦 Models` (only if `[mod]`) + +Do NOT invent custom headings like `## Summary`, `## Changes`, `## Why`, or `## Test plan` — they break tooling and reviewer expectations. Map the content of every PR shape onto these standard sections (see "Mapping release/backmerge content onto the template" below). + ### Release PR (fork → release branch) Cuts a new package version onto a `release--` branch on `tetherto/qvac`. Bumps `package.json` version, adds the per-version changelog folder, and prepends an entry to the aggregated `CHANGELOG.md`. Merging this PR triggers GPR publish. @@ -108,19 +119,27 @@ Brings the release artifacts (changelog folder, aggregated `CHANGELOG.md` entry, - `[notask]` is **only** used when there is no ticket. Do **not** combine `[notask]` with a ticket in the title (`QVAC-XXX chore[notask|skiplog]: ...` is wrong). - The two metadata tags `[notask]` and `[skiplog]` may be combined via `|` (e.g. `[notask|skiplog]`); this is distinct from the rule that content tags `[api]/[bc]/[mod]` cannot be combined. -**Body content for backmerges:** -- Brief summary linking to the corresponding release PR and noting the publish has already happened. -- Explicit list of which files are touched (changelog folder, aggregated CHANGELOG.md, package.json version field). -- If the PR is hand-crafted rather than a `git merge`, **explain why** — usually a table of dependencies that have moved ahead on `main` and would be regressed by a blind merge. -- Reference the precedent backmerge PR for the previous version so reviewers can compare shape. +### Mapping release/backmerge content onto the template + +Use the standard sdk-pod headings for every shape. Suggested mapping: + +**Release PR:** +- `## 🎯 What problem does this PR solve?` — what's in the release at a high level (single hotfix? feature batch? security patch?), and which downstream consumer has been waiting on it. +- `## 📝 How does it solve it?` — what files change (`packages//package.json` version bump, the new `changelog//` folder, the prepended aggregated `CHANGELOG.md` entry), what the merge triggers (GPR publish), and the post-merge follow-up (open the backmerge PR to publish to npm). +- `## 🧪 How was it tested?` — `bun lint`, `bun run build`, `bun test` results; any release-scripts that were run (`generate-changelog-sdk-pod.cjs`, `generate-notice.cjs` for SDK). + +**Backmerge PR:** +- `## 🎯 What problem does this PR solve?` — name the published version, link the release PR, state that `main` is currently behind and would otherwise have a hole in its changelog history. +- `## 📝 How does it solve it?` — list the artifacts brought back (changelog folder, aggregated `CHANGELOG.md` entry, `package.json` version), reference the precedent backmerge PR for the previous version so reviewers can compare shape. +- `## 🧪 How was it tested?` — **if hand-crafted, this is where you justify it.** Explain why a literal `git merge` would regress something on `main` (usually a table of `package.json` dependencies that have advanced past the release branch). Confirm the diff touches only the four expected files. Note any post-merge automation (NOTICE regeneration, etc.) that's expected to follow. ### Decision rule (quick reference) -| You are opening… | Base | Title format | -|---|---|---| -| A normal feature/fix/doc PR | `main` | `TICKET prefix[tags]: subject` | -| A release PR for a new version | `release--` | `TICKET chore: release ` (or `chore[notask|skiplog]: release ...` if no ticket) | -| A backmerge PR after publish | `main` | `TICKET chore[skiplog]: backmerge release ` (or `chore[notask|skiplog]: backmerge ...` if no ticket) | +| You are opening… | Base | Title format | Body | +|---|---|---|---| +| A normal feature/fix/doc PR | `main` | `TICKET prefix[tags]: subject` | sdk-pod template | +| A release PR for a new version | `release--` | `TICKET chore: release ` (or `chore[notask|skiplog]: release ...` if no ticket) | sdk-pod template | +| A backmerge PR after publish | `main` | `TICKET chore[skiplog]: backmerge release ` (or `chore[notask|skiplog]: backmerge ...` if no ticket) | sdk-pod template | ## Output Format @@ -179,6 +198,7 @@ Before outputting the PR description, verify: - [ ] Title follows format: `TICKET prefix[tags]: subject` (or the release / backmerge variants above) - [ ] If the PR has a ticket, `[notask]` is **NOT** in the title - [ ] If the PR is a backmerge, `[skiplog]` is in the title +- [ ] Body uses the standard sdk-pod headings (`## 🎯 What problem...`, `## 📝 How does it solve...`, `## 🧪 How was it tested?`) — no custom headings like `## Summary` / `## Changes` / `## Test plan` - [ ] "What problem" describes user impact, not implementation - [ ] "How it solves" is high-level approach, not line-by-line - [ ] Unused sections are deleted