Stage 12 — Release-Blocking Issues

What this stage teaches

Stage 12 is not about a bigger patch. It is about judgment — the call a near-maintainer makes when an issue lands during a release cycle: does this block the release, or not? Get it wrong in one direction and you ship a data-loss bug; get it wrong in the other and you hold a release hostage to a cosmetic defect. The skills:

  • Recognise what makes an issue release-blocking: data loss, a BWC break, a security vulnerability, or a correctness regression versus the prior release.
  • Read the v3.x.0 milestone and understand how release managers use it to scope a release.
  • Know how to handle a revert — when reverting a merged change is the right call, and how to do it cleanly.
  • Understand the bar for a backport to a release branch (2.x, a 2.x.y patch line) and how the backport <branch> label and bot work.

This is the stage where you stop thinking like a contributor closing an issue and start thinking like a maintainer protecting a release. You do not need to be a maintainer to practise it — triaging blockers well is exactly how you earn the trust that leads there.

Prerequisite: everything above, especially Stage 11 (BWC is the most common blocker class), plus the release process and the maintainer mindset.


What makes an issue release-blocking

Not all bugs block. Use this decision table — it is the heart of the stage:

ClassBlocks the release?Why
Data loss / corruptionAlwaysIrreversible. No workaround restores lost data.
BWC break (rolling upgrade fails, index won't open)AlwaysBreaks the upgrade path for every user.
Security vulnerabilityAlways (often via embargo)Exploitable; handled through the security disclosure process, not a public issue.
Correctness regression vs prior releaseUsuallySomething that worked in N-1 now returns wrong results.
Crash / availability regressionUsuallyA node or cluster that was stable now falls over on a common path.
Performance regression (significant, common workload)SometimesBlocks if it is large and on a default path; otherwise note and fix in a patch.
New-feature bug (feature is new this release)SometimesOften "disable the feature" is the fix, not "block the release."
Cosmetic / docs / minor UXNoShip and fix in a patch (3.x.y).

The pivotal word is regression: a bug that exists in N-1 and N equally is not a release blocker for N (it shipped before). A bug introduced in N against N-1 behaviour is a regression and a candidate blocker. The first question on any "is this a blocker" triage is therefore: did this work in the last release?

flowchart TD
  I[incoming issue during release cycle] --> Q1{data loss / BWC break / security?}
  Q1 -->|yes| BLOCK[release-blocker: must fix or revert before GA]
  Q1 -->|no| Q2{regression vs previous release?}
  Q2 -->|no, pre-existing| PATCH[not a blocker; fix in a future patch]
  Q2 -->|yes| Q3{common default path & severe?}
  Q3 -->|yes| BLOCK
  Q3 -->|no| Q4{can the new feature be disabled?}
  Q4 -->|yes| FLAG[ship with feature off / experimental; fix later]
  Q4 -->|no| BLOCK

How release managers triage with the milestone

OpenSearch scopes a release with a GitHub milestone (v3.2.0, v3.3.0, …) — not a label. Release managers move issues into and out of the milestone as the date approaches.

is:issue is:open milestone:"v3.2.0" label:bug sort:updated-desc
is:issue is:open milestone:"v3.2.0" label:"backport 2.x"
is:open milestone:"v3.2.0" -label:"good first issue"   # what's actually gating the release

As code-freeze nears, the release manager's job is to drive the blocker count in the milestone to zero. Anything that is genuinely a blocker stays in the milestone; everything else is bumped to the next milestone with a comment. Practising Stage 12 means being able to look at the milestone and correctly argue, issue by issue, "this stays / this can move."

A blocker argument in an issue comment looks like:

This is a release-blocker for v3.2.0: it is a correctness regression vs 3.1 (the same
query returned 5 hits in 3.1, returns 4 in 3.2 — repro below). It is on the default search
path with no workaround. Recommend either a fix in #PR or reverting #PR-that-introduced-it
before GA.

Three elements: class (regression), evidence (repro + the N-1 comparison), and a recommendation (fix or revert). A blocker claim without all three gets discounted.


Handling a revert

Sometimes the right call for a blocker is not "fix forward" but "revert the change that caused it." Reverting is a first-class, low-drama action in a healthy project — it buys time without holding the release. When to revert rather than fix forward:

  • The fix is not obvious and the release date is close.
  • The change that introduced the regression is recent and isolated (a clean revert).
  • The feature can ship later without disruption.

How to do it cleanly:

git fetch upstream
git checkout -b revert/some-change upstream/main
git revert <commit-sha>            # creates a revert commit with the original message
# resolve any conflicts, keep the revert minimal
git commit -s                       # DCO still required on the revert
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ ### Changed
+- Revert "<original change>" due to a search correctness regression in v3.2 ([#NNNNN](...), reverts [#MMMMM](...))

In the revert PR, link both the regression issue and the original PR, and state plainly: "we are reverting to unblock v3.2; the feature will be re-introduced with a fix in #follow-up." Reverting is not a failure — shipping a known data-loss or correctness regression is. The maintainer instinct is to protect the release first and restore the feature second.


The bar for a backport to a release branch

A change merged to main (the 3.x line) does not automatically reach the maintenance line (2.x) or a patch line. Backporting has a higher bar than merging to main:

Backport candidateBackport to 2.x?
Security fixYes — backport as far as the support window allows
Data-loss / corruption fixYes
Correctness/availability regression fixUsually
Bug fix with low risk and clear valueOften
New featureRarely (features generally go forward-only)
Refactor / cleanupNo

The mechanics: label the merged PR backport 2.x (or the relevant branch) and the backport bot opens a cherry-pick PR against that branch. If the cherry-pick conflicts, the bot asks you to do it manually:

git fetch upstream
git checkout -b backport/2.x-of-NNNN upstream/2.x
git cherry-pick -x <merge-or-squash-sha>    # -x records the source commit
# resolve conflicts, keep the change minimal and faithful
git commit -s
git push && gh pr create --repo opensearch-project/OpenSearch --base 2.x --fill

Warning: A backport must be faithful — it carries the same change, not a new one. If the fix needs to be different on 2.x (e.g. a BWC constraint differs), that is a separate, reviewed PR, not a backport. Quietly altering behaviour in a backport is how maintenance branches drift from main.

The -x flag annotates the cherry-pick with the original SHA so anyone can trace the maintenance commit back to its main origin — auditability the release process depends on.


Finding Stage 12 issues to triage

You practise this stage by triaging, not (only) by coding:

is:issue is:open milestone:"v3.2.0" sort:created-asc
is:issue is:open label:bug "regression" in:title,body sort:updated-desc
is:issue is:open label:"backport 2.x" no:assignee

For each, write the blocker argument (class / evidence / recommendation) in a comment, even if you are not the one to decide. Maintainers weigh well-argued triage heavily; it is the most visible near-maintainer skill.

Security note: never triage a suspected vulnerability in a public issue. OpenSearch has a security disclosure process (SECURITY.md, a private report channel). Suspected security blockers go there, under embargo, not into a milestone comment. Mis-handling a disclosure publicly is itself a serious mistake.


Pitfalls

  • Confusing severity with blocker status. A severe bug that existed in N-1 is not a release blocker for N — it shipped already. The blocker test is regression + class, not raw severity.
  • Blocking on a fixable feature. If a new feature is broken, "disable / mark experimental" is often the correct, faster path than holding the whole release.
  • Fix-forward when revert is right. Near a freeze, a clean revert beats a rushed fix. Protect the release; restore the feature next cycle.
  • Unfaithful backports. A backport carries the same change. Different code for 2.x needs its own review.
  • Backporting features or refactors. Maintenance branches take fixes, not features.
  • Triaging security in public. Use the disclosure process under embargo. Always.
  • Blocker claims without evidence. "This feels bad" is not triage. Class + repro + N-1 comparison + recommendation, or it is noise.

Exit criteria — this is the end of the roadmap

You have completed the roadmap when:

  • You have helped triage at least one release-blocking issue, writing the class / evidence / recommendation argument, and your call matched the maintainers'.
  • You can apply the blocker decision table from memory and explain why a pre-existing bug is not a release blocker while a regression of the same severity is.
  • You have either executed or correctly proposed a revert to protect a release, and you know when fix-forward is preferable.
  • You understand the backport bar, the backport <branch> bot, the faithful-cherry-pick rule, and that security goes through the disclosure process, never a public milestone.

Where to go from here

The roadmap ends at the judgment a maintainer applies; becoming a maintainer is a separate path the TSC and each repo's maintainers steward — see the maintainer mindset and release process. Landing PRs across these twelve stages is necessary for that trajectory, not sufficient: the rest is consistency, good review citizenship, and showing the judgment this stage describes, over time.

If you have worked even three or four rungs of this ladder for real, attempt the capstone: a single end-to-end contribution that exercises issue selection, root-cause, a fix with tests, BWC reasoning, and a release-aware writeup — the whole roadmap compressed into one PR.