| name | opsmill-dev-backporting-fixes |
| description | Ports an existing fix from one long-lived branch to another (e.g. a stable fix landed on the development branch), adapting it to the target branch's current code rather than cherry-picking blindly. TRIGGER when: phrasings like "backport <fix/PR/commit> to <branch>", "port this stable fix to develop", "land <ticket> on <branch> too", or a fix that already exists on one branch has to land on another. DO NOT TRIGGER when: syncing one whole branch into another wholesale → opsmill-dev-merging-branches; replaying local work onto its base → opsmill-dev-rebase; fixing a brand-new bug that has no existing fix → opsmill-dev-analyzing-bugs / opsmill-dev-fixing-bugs. |
| argument-hint | A fix reference on the source branch (commit SHA, merged PR, or ticket) and the target branch to land it on |
| compatibility | Requires a Git working tree. Locating the source fix and opening the PR need GitHub access (gh CLI authenticated, GitHub MCP server, or equivalent). Composes the `opsmill-dev-commit`, `opsmill-dev-pr`, `opsmill-dev-monitoring-pull-requests`, and `opsmill-dev-creating-issues` skills. |
| metadata | {"version":"0.1.0","author":"OpsMill"} |
Backport a Fix
Port a single fix that already exists on one branch onto another long-lived branch, adapting it to the target branch's current code instead of copying it verbatim. Used when a fix merged on a release/stable line has to also land on the development line (or the reverse), and a plain cherry-pick is wrong because the branches have diverged.
This is selective and adaptive: one fix, reshaped to fit the target. That is the opposite of merging-branches, which carries a whole branch across wholesale. If the request is "sync all of <branch> into <branch>", use merging-branches; if it is "make this fix also work on <branch>", use this skill.
This skill is repo-agnostic. The core workflow below applies to any OpsMill repo; the Per-Project Specifics block near the end is where a repo's concrete base-branch, changelog, and validation conventions live — read the target repo's AGENTS.md and dev/ docs to fill it in.
The core insight
A clean cherry-pick is not proof of a correct backport. The source fix encodes assumptions about the code as it was on the source branch. If the target branch has since diverged in the area the fix touches — a refactor, a new abstraction, a different event or scoping model — copying the fix can compile, merge cleanly, and still be wrong (a no-op, a double-apply, or a violation of an invariant the target added). The work is to understand what the fix achieves, then re-express that on the target's terms.
Inputs
- A fix reference on the source branch — a commit SHA (or range), a merged PR, or a ticket that names the fix. Resolve it to the actual commits and, critically, the tests that came with it (the reproduction test and any test the fix changed).
- The target branch the fix must land on.
If either is ambiguous (direction unclear, or only a ticket given), confirm the source commits and the target branch with the user before proceeding.
Steps to Follow
-
Locate the source fix and its tests. From the commit/PR/ticket, identify every commit that makes up the fix, and read them in full:
git log --oneline <source-branch> | grep -i <keyword>
git show <sha>
Note separately: the behavior change (the product code), the reproduction test, and any existing test the fix modified (often a test that asserted the now-fixed-wrong behavior). You will need all three on the target.
-
Determine the target base branch. This is frequently not the repo's default branch. A stable→develop backport targets develop; the repo default may be stable. Confirm against the project's branch model (AGENTS.md, dev/guidelines/git-workflow.md) rather than assuming origin/HEAD.
-
Set up an isolated worktree off the target base — the same fresh-worktree-off-<base> flow that merging-branches (its integration-branch step) and rebase already perform, so the backport never disturbs the current checkout. Reuse that canonical flow rather than re-deriving it here; in particular it covers the case this worktree-based workflow actually hits — a branch or worktree pre-created on the wrong base — by verifying with git merge-base --is-ancestor HEAD origin/<target-base> and resetting an unpushed branch with git reset --hard origin/<target-base>. Initialise submodules and install dependencies in the new worktree before running anything.
This "fresh worktree off <base>" setup is shared with merging-branches and rebase. Extracting it into one canonical reference the three skills share, so they cannot drift, is worth a follow-up.
-
Divergence check — the heart of this skill. Read the target branch's current version of the subsystem the fix touches and compare it against what the source fix assumed:
digraph backport_decision {
rankdir=LR;
"Does the target's code in this area\nmatch the source's assumptions?" [shape=diamond];
"Cherry-pick / apply as-is\n(then still re-validate on target)" [shape=box];
"ADAPT: re-express the fix on the\ntarget's mechanism, re-derive via tests" [shape=box];
"Does the target's code in this area\nmatch the source's assumptions?" -> "Cherry-pick / apply as-is\n(then still re-validate on target)" [label="identical"];
"Does the target's code in this area\nmatch the source's assumptions?" -> "ADAPT: re-express the fix on the\ntarget's mechanism, re-derive via tests" [label="diverged"];
}
Important Rules
- Selective and adaptive, not wholesale. One fix, reshaped for the target. Wholesale branch sync is
merging-branches.
- Adapt to the target's code. A clean cherry-pick that compiles is not proof; when the target diverged, re-express the fix on its terms and re-derive with the reproduction test.
- Re-validate on the target. "It passed on the source branch" is not evidence for the target.
- Never ship a no-op. If part of the fix is architecturally blocked on the target, descope it into a linked follow-up — do not ship code that looks like a fix but is ignored downstream.
- Target the right base branch — usually not the repo default for a backport.
- Don't duplicate a changelog fragment that the normal branch sync will carry over.
- The skill's only outputs are the backport branch and its PR; it never pushes to the source, target, or any long-lived branch directly (that is
/opsmill-dev-commit's discipline).
Quality gates
Gates for this skill follow ../quality-gates/gates/gate-model.md. backporting-fixes is Tier 2 — it lands an adapted fix and opens a PR.
| Gate | Step / trigger | Tier | Primitives | Pass criteria | On-fail |
|---|
| Validated-on-target | before opening the PR | T2-verify | P1 | Reproduction + reconciled tests pass on the target worktree (paste output); runtime fixes exercised for real. | STOP |
| Adapted-not-copied | before gh pr create | T2-ship | P2 + P3 | A fresh judge confirms the fix was adapted to the target's current code (not blindly cherry-picked), re-validated on the target, and any target-blocked sub-case was descoped into a follow-up rather than shipped as a silent no-op. | STOP; adapt/descope; do not open the PR |
Per-Project Specifics
The core above is generic. Before backporting, read the target repo's AGENTS.md, dev/guidelines/, and branch-model docs, then record (or append) a block like the template below.
Template
- Branch model: which branch is the backport target for which source (e.g.
stable → develop), and whether the target differs from the repo default.
- Changelog: the fragment system and directory, and the rule for backports (usually: omit, the source fragment syncs over).
- Worktree + setup: where worktrees live, and the submodule-init / dependency-install commands needed before tests run.
- Validation: the local checks and the targeted test command for the touched area (and what is left for CI, e.g. a heavy integration/e2e job).
- Divergence hot-spots: subsystems known to differ between the branches, where adaptation (not cherry-pick) is the norm.
Keep exact paths, tool-version pins, and known gotchas in the target repo's own AGENTS.md / dev/ docs — reference them rather than duplicating (and stale-ing) them here.