--- name: qv-sdk-pr-create description: Generate PR descriptions for SDK pod packages following template and format rules. Use when creating an SDK pod PR or invoking /qv-sdk-pr-create. --- # SDK Pod PR Creation Generate PR titles and descriptions for SDK pod packages, following the team's template and format rules. ## When to use this skill **Applies to SDK pod packages** whose paths are owned by `.github/teams/sdk.json`. **Use when:** - Creating a PR for any SDK pod package - User asks to generate PR description - User invokes `/qv-sdk-pr-create` ## Branch / remote preference **Preferred path for internal SDK work:** push the head branch to the org repo (`tetherto/qvac`) and open a same-repo PR. Org-branch ready PRs get baseline CI without any fork trust gate. Heavy tiers still opt in via labels (`test-e2e-smoke` / `test-e2e-full`, addon stage labels, etc.). Prefer **Ready for review** over Draft when you want baseline checks to run (drafts run nothing until ready). **Fallback:** personal-fork → org PRs still work, but a personal fork counts as external. Privileged CI needs a merge/release-team member to approve the `fork-ci` environment on each workflow run for the current head SHA; each new push re-prompts. Do not ask the PR author to self-approve `fork-ci`. Resolve remotes from `git remote -v`: - **Org remote** — URL contains `tetherto/qvac` (often named `upstream`, sometimes `origin`) - **Fork remote** — contributor fork, if present (often named `origin` when org is `upstream`) In command examples below, `ORG_REMOTE` / `FORK_REMOTE` / `BRANCH` are placeholders — substitute the resolved remote and branch names. Do not run those tokens literally. ## Release PR branch naming (org-branch path) `publish-sdk.yml` (and sibling publish workflows) run **Release Merge Guard** on `push` to any `release-*` ref. The guard validates `github.ref_name` — the branch that was **pushed** — not the PR base. It requires `release--x.y.z` (three-part semver). Consequences for release changelog / metadata PRs: 1. **Base (target)** must be exactly `release--` (e.g. `release-sdk-0.17.0`). Short cuts like `release-sdk-0.17` fail the guard on merge/publish. If the cut is short-named, STOP and ask a repo admin to rename it to the three-part form before relying on publish (protected-branch rename needs admin). 2. **Head (working branch)** must **not** start with `release-` when pushed to the org remote. Prefer `chore/--changelog` (e.g. `chore/sdk-0.17.0-changelog`). Names like `release-sdk-0.17.0-changelog` trip Release Merge Guard on the helper push and can leave a failing check on that commit SHA. 3. GitHub cannot retarget a PR's **head**. To rename a bad head: push the same commits under the new name, open a new PR, close the old one, delete the old remote head. If the new PR still shows a stale Release Merge Guard fail on the same SHA, push an empty `[skiplog]` commit so checks reattach to a fresh SHA. `backmerge/release--` heads are fine — they do not match the `release-*` push trigger. **Release changelog PRs** (metadata + notes onto `release--x.y.z`): - Title is `chore:` (optional `[mod]` when the cut is catalog-only). Do not put `[bc]` on the release title; breaking lives in `breaking.md`. - Body API / Models / Breaking sections are copied from `changelog//`, not from an earlier hop or the generator summary. Delete a section only when that file is absent. ## Workflow 1. Identify base and current branch — note whether the base is `main` or a `release--` branch. For release PRs, apply **Release PR branch naming** above (base three-part; head not `release-*`) 2. Resolve the head remote (prefer org remote; see above). Collect commits/diff from `.../` (or local `HEAD` if not yet pushed) 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. **If the diff exposes or changes a user-facing SDK capability**, apply first-class product parity (see below) before outputting the description 9. **If diff touches the `version` of `packages/sdk` or its `@qvac/inference` range, or sdk's dep blocks**, chain into the `qv-sdk-inference-version` skill (see "SDK @qvac/inference Version Trigger" below) 10. **If the diff touches user-facing paths under `packages/sdk`, `packages/cli`, or `packages/sdk-python` and `docs/website/content/docs` is not already in the diff**, apply the docs website trigger (see "Docs Website Trigger" below) before outputting the description 11. Output complete PR description 12. If base is a release branch, chain into the dual-PR flow (see "Release Target Dual-PR Flow" below) ## Inference Strategy Infer first, ask only if uncertain: **Ticket number:** - Extract from branch name pattern: `QVAC-\d+`, `SDK-\d+` - Extract from commit messages if referenced - ASK only if no ticket found **Prefix (feat/fix/doc/test/chore/infra):** - Extract from branch name prefix: `feat/`, `fix/`, `infra/`, etc. - Use majority prefix from commit messages - If no conventional commits, infer from diff: - New files/exports → `feat` - Bug-related changes → `fix` - Only .md files → `doc` - Only test files → `test` - ASK only if mixed signals or unclear **Tags ([api]/[bc]/[mod]):** - `[api]`: new exported functions/types in public API - `[bc]`: removed/changed existing public API signatures - `[mod]`: changes to model constant definitions - ASK only if change scope is ambiguous - **Release changelog PRs** (base `release-*`, diff is notes / NOTICE / version / generated docs): title is `chore:` (optional `[mod]`). Do not copy `[bc]` / `[api]` from the notes into the title. Body API / Models / Breaking still copy `changelog//`. **Testing section:** - If test files modified → "Unit tests added/updated for X" - If no tests → ASK what manual testing was done ## Format References - **PR title format**: See `docs/gitflow.md` and the validation rules below - **PR body template**: See `.github/PULL_REQUEST_TEMPLATE/sdk-pod.md` Fill template sections based on the diff analysis. Delete sections that don't apply. ## First-class product parity (CLI) **Trigger:** the diff touches `packages/sdk/server/bare/plugins/**`, `packages/sdk/client/api/**`, a plugin `*-config.ts` schema, or a request/sampling schema used by serve, **and** the change is a user-facing inference capability (new modality, new request field, new load-time `modelConfig` field). Native addon-only diffs skip this. See `packages/sdk/AGENTS.md`. If triggered and `packages/cli` is unchanged: 1. ASK: "CLI not updated. Add CLI in this PR, mark library-only in the body, or skip?" 2. Add CLI: stop until the CLI diff is present. Library-only: one-line why in the body. Skip: reminder at the end. ## Output Format ALWAYS output the PR in this copy-ready format, even when making corrections: ~~~ ## PR Title ``` TICKET prefix[tags]: subject ``` ## PR Body ```markdown ## 🎯 What problem does this PR solve? ... ``` ~~~ ## gh CLI Integration After generating the PR description, check for `gh` CLI: 1. Check if `gh` is installed: `which gh` 2. Check remotes: `git remote -v` — identify the **org** remote (`tetherto/qvac`) and any fork remote 3. If available, ask user: "Create PR now with gh CLI?" [Yes / No / Preview first] 4. If yes, ensure changes are committed, then push the head branch to the **org remote** when the user has write access. Only push to a personal fork when org push is unavailable or the user explicitly chooses the fork path 5. Create the PR: ```bash # Preferred — org-branch (same-repo) PR. # ORG_REMOTE = remote whose URL is tetherto/qvac (often `upstream` or `origin`). git push -u ORG_REMOTE BRANCH gh pr create \ --repo tetherto/qvac \ --base main \ --head BRANCH \ --title "TICKET prefix: subject" \ --body "..." # Fallback — personal fork -> org PR (external CI path; needs merge/release fork-ci approval per run): git push -u FORK_REMOTE BRANCH gh pr create \ --repo tetherto/qvac \ --base main \ --head FORK_OWNER:BRANCH \ --title "TICKET prefix: subject" \ --body "..." # Then open in browser: gh pr view --repo tetherto/qvac BRANCH --web ``` **Important:** - `--web` alone only opens browser for manual creation, does NOT create the PR - For fork PRs, must specify `--repo`, `--base`, and `--head FORK_OWNER:BRANCH` explicitly - For org-branch PRs, `--head BRANCH` (no `owner:`) is enough when `--repo tetherto/qvac` - Do not add fork trust gates on org-branch PRs; baseline CI runs without them. For fork PRs, tell the user a merge/release reviewer must approve the pending `fork-ci` deployment after reviewing the current head - Commit and push before creating PR 6. If gh not available, output the copy-ready markdown format above 7. As part of the output, provide a clickable hyperlink (not plain text) to the PR on GitHub. ## SDK @qvac/inference Version Trigger **Trigger:** the PR diff (`.../` or local `HEAD`) modifies the `version` of `packages/sdk/package.json` or its `@qvac/inference` range, or sdk's `dependencies` / `optionalDependencies` / `peerDependencies`. When triggered, prompt the user to run `qv-sdk-inference-version` so the versions and the generated Python client are right in the same commit/PR: `@qvac/sdk`'s version and its `@qvac/inference` range sharing a major.minor, and `tetherto-qvac-sdk` (generated `SDK_VERSION` / `_generated/`) at the SDK's version. A range on a different major.minor fails the SDK's `lint` on every PR. A change to `packages/inference/package.json` alone is not a trigger — the engine has its own version and its own release. ### Steps (after Step 8 of Workflow above) 1. Detect the trigger condition by inspecting the diff: - `git diff .../ -- packages/sdk/package.json` (or vs local `HEAD`) shows changes - Changes touch sdk's `version` line, its `@qvac/inference` range, OR sdk's `dependencies` / `optionalDependencies` / `peerDependencies` block 2. If triggered, ask user: "PR touches sdk's deps/version. Run `qv-sdk-inference-version` (version + sdk-python)?" [Yes / No (skip)] 3. If yes, read `.agents/skills/qv-sdk-inference-version/SKILL.md` and follow it inline. 4. Verify: `bun run enforce-inference-versions` in `packages/sdk` and `packages/sdk-python` `generate.py --check` must both pass. 5. Stage and commit the version changes onto the same branch BEFORE proceeding to Output step. ### Opt-out To skip this sync for a single run, the user can invoke `/qv-sdk-pr-create --no-sync`. The skill proceeds normally and emits a reminder at the end: "Reminder: sdk deps/version changed but the `@qvac/inference` version was not synced. Run `/qv-sdk-inference-version` before merge." ## Docs Website Trigger **Trigger:** diff touches user-facing paths under `packages/sdk`, `packages/cli`, or `packages/sdk-python`, and `docs/website/content/docs` is not already in the diff. ### Steps (after Step 9 of Workflow above) 1. When triggered, ask: "user-facing SDK/CLI change with no docs-website diff. run `/qv-docs-update` before opening the PR?" [Yes / No (library-only)] 2. If yes, read `.agents/skills/qv-docs-update/SKILL.md` and follow it. Don't open the PR until it reports `DONE`, `NO_DOCS_IMPACT`, or `GENERATED_DOCS_ONLY`. 3. If no (library-only), put the library-only note in the PR body — one line on why the change needs no docs. This is not the generated API summary — that still ships via `qv-sdk-changelog` on release. ## Docs Artifacts (SDK Releases) **Context:** for `@qvac/sdk` releases, the `qv-sdk-changelog` skill (Step 8) now generates the documentation-site API reference + release notes locally and ships them in this same release PR. There is **no longer a separate auto-generated docs PR** (the old `docs-release.yml` workflow was removed). Staging works the same as for the rest of the release commit — no special handling is needed. Step 8's committable surfaces — the API summary (minor only) and the release notes of the SDK's current documentation line, `docs/website/content/docs/sdk/(v)/reference/{api,release-notes}.mdx` — show up in `git status` alongside the changelog, while every generation/build byproduct (`api-data.json`, `.next/`, `.source/`, `out/`, `dist/`, `next-env.d.ts`, `packages/sdk/dist/`) is gitignored and therefore never appears. Review `git status` and commit the shown files as usual. Reviewers should expect the `reference/api` + `reference/release-notes` diff in the release PR alongside the changelog. ## Release Target Dual-PR Flow **Trigger:** the just-created PR's base is `release--` for any SDK pod package. When triggered, automatically chain into the `sdk-backmerge` skill so a follow-up PR is also opened against `main` with the same version-bump + changelog metadata. This applies the gitflow.md "Keep main aligned" rule at PR-creation time so nobody has to remember a follow-up step after the release PR merges. **Preflight before opening the release PR:** confirm base matches `^release--\d+\.\d+\.\d+$` and the org head is **not** `release-*` (see **Release PR branch naming**). Do not open / chain the dual-PR flow against a short-named cut if publish is expected on merge. ### Steps (after Step 5 of gh CLI Integration above) 1. Capture context for the backmerge: - Just-created release PR number and URL - Release branch name (`release--`) and parsed `` / `` - Source head branch, org-remote-qualified when possible (e.g. `ORG_REMOTE/`, or the release PR `headRefOid` if the remote tip is missing) - Ticket number from the title 2. Invoke the `sdk-backmerge` workflow inline with these inputs (read `.agents/skills/qv-sdk-backmerge/SKILL.md` and follow it). 3. **Fail-stop policy** — if the backmerge cherry-pick produces a conflict outside `sdk-backmerge`'s auto-resolve list, STOP. Print: - The release PR URL (success — PR #1 is open) - The current `git status -sb` from the conflicted cherry-pick - Resume instructions: `git add && git cherry-pick --continue`, then run `/qv-sdk-backmerge --resume` 4. On success, print **both** PR URLs as clickable hyperlinks, ordered: - Release PR (target: `release--`) - Backmerge PR (target: `main`) ### Opt-out To skip the backmerge for a single run, the user can invoke `/qv-sdk-pr-create --no-backmerge`. The skill still creates PR #1 normally and prints a reminder pointing to `/qv-sdk-backmerge` for later. ## Quality Checklist Before outputting the PR description, verify: - [ ] Title follows format: `TICKET prefix[tags]: subject` - [ ] "What problem" describes user impact, not implementation - [ ] "How it solves" is high-level approach, not line-by-line - [ ] Unused sections are deleted - [ ] `[bc]` tag has BEFORE/AFTER code examples (feature PRs). Release changelog PRs: no `[bc]` on the title; Breaking section copies `breaking.md` when that file exists - [ ] `[api]` tag has usage example - [ ] `[mod]` tag has Added/Removed models list - [ ] Description is concise - bullet points, no fluff - [ ] Generated helper notes, template instructions, and tool footers are removed from the PR body - [ ] If the diff is a user-facing SDK capability, CLI was updated in this PR, the body says library-only, or the skip reminder was emitted - [ ] If diff touches sdk's `version` / `@qvac/inference` range (or sdk's deps), `qv-sdk-inference-version` ran (or `--no-sync` was set with a reminder emitted), and the version checks plus sdk-python checks pass - [ ] For sdk releases with generated docs, `git status` shows only the current line's `reference/api.mdx` (minor) and `reference/release-notes.mdx` as committable docs changes, never `src/lib/versions.ts` or `public/_redirects` — disposable byproducts (`api-data.json`, `out/`, `.next/`, `dist/`, etc.) are gitignored - [ ] If base is `release--`, the dual-PR flow ran (or `--no-backmerge` was set), and both PR URLs are reported - [ ] Release PRs: base is three-part `release--x.y.z`; org head is `chore/--changelog` (or other non-`release-*` name) - [ ] Release changelog PRs: title is `chore:` (no `[bc]`); body API / Models / Breaking match `changelog//` - [ ] Head was pushed to the org remote when write access allows; fork path only used as fallback (with `fork-ci` re-approval called out) - [ ] PR is Ready for review when baseline CI is expected (not left as Draft unintentionally) ## References - SDK pod ownership: `.github/teams/sdk.json` - Shared SDK and first-class product guidance: `packages/sdk/AGENTS.md` - PR template: `.github/PULL_REQUEST_TEMPLATE/sdk-pod.md` - Format rules: `docs/gitflow.md` and this skill's validation section - Backmerge skill: `.agents/skills/qv-sdk-backmerge/SKILL.md` - sdk @qvac/inference version: `.agents/skills/qv-sdk-inference-version/SKILL.md` - GitFlow: `docs/gitflow.md` — still documents fork-first contribution; for internal SDK PRs, prefer the org-branch path in this skill until DevOps updates gitflow - Fork CI trust model: `docs/ci/LABELS.md` (fork-ci environment + `fork-approval`)