docs(platform.ci-cd): back-merge main into develop before cutting a release

Hotfixes merged straight to `main` are not in `develop`. Nothing warns you
they have diverged; the bill arrives at the next release, as a conflict on
the prod branch.

hiveops-remote hit this on 2026-07-20: main had 5 commits develop had never
seen (OWASP suppressions, jackson-bom fix, jackson bump, cd-main rework,
sonar-pr removal) against develop's 9 including the hiveops-bom 1.0.2 -> 1.0.8
bumps. Both sides had fixed the SAME jackson/spring/tomcat CVEs by different
routes — main suppressed, develop bumped — so develop -> main conflicted in
pom.xml with main's hotfixes at risk of being clobbered.

Documents the two read-only probes (rev-list count, merge-tree exit code),
the back-merge-first ordering, and why resolving a divergence inside the
release PR is the wrong place for it.

Also records the second-order effect: hotfixes that bypass the develop PR
flow lose their issue paperwork. agent#62, bom#6 and devices#55/#60 all sat
open with a shipped fix, no Verify Fix label and no milestone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 10:00:47 -04:00
parent b5de1a1107
commit e4b894a848
@@ -196,6 +196,43 @@ curl -X POST "$GITEA/api/v1/repos/<org>/<repo>/branch_protections" \
# verify: GET /repos/<org>/<repo>/branches/develop → "protected": true
```
## ⚠️ Back-merge `main` into `develop` before cutting a release
A hotfix merged straight to `main` is **not** in `develop`. Do that a few times and the two branches
diverge silently — nothing warns you, and the bill arrives when you try to release.
**`hiveops-remote`, 2026-07-20.** `main` had 5 commits `develop` had never seen (OWASP suppressions,
a jackson-bom import fix, a jackson bump, the cd-main rework, a sonar-pr removal), while `develop`
had 9 of its own including the `hiveops-bom` 1.0.2 → 1.0.8 bumps. Both branches had fixed **the same
jackson/spring/tomcat CVEs by different routes** — `main` *suppressed* them, `develop` *bumped the
BOM*. The `develop` → `main` release PR conflicted in `pom.xml`, on the prod branch, with `main`'s
hotfixes at risk of being clobbered by an older `develop` state.
**Probe before you assume a release is clean** — this writes nothing:
```bash
git fetch origin
git rev-list --count origin/develop..origin/main # >0 = unmerged hotfixes on main
git merge-tree --write-tree origin/main origin/develop >/dev/null; echo $? # 1 = CONFLICTS
```
**The fix is ordering, not cleverness:**
| Step | Where | Why |
|---|---|---|
| 1. Back-merge `main` → `develop` on a branch | targets `develop` | CI validates the resolution; nothing is prod-bound |
| 2. Resolve the conflict there, PR it, merge | `develop` | one resolution, reviewed, reversible |
| 3. *Then* cut `develop` → `main` | prod branch | now a clean merge |
⛔ **Never resolve a divergence conflict inside the release PR itself.** That puts an unreviewed
merge resolution directly onto the branch production builds from, and a wrong `--ours`/`--theirs`
silently reverts a shipped hotfix.
**The paperwork rots too.** Hotfixes that bypass the `develop` PR flow tend to lose their issue
links: the fix merges, but the issue keeps no `Verify Fix` label and no milestone, so it reads as
untouched forever. `hiveops-agent#62`, `hiveops-bom#6` and `hiveops-devices#55`/`#60` all sat open
with a shipped fix for this reason. After any hotfix to `main`, go back and label the issue.
## Root fragility
The runners are VMs on the cluster they depend on, with a single non-HA resolver — so cluster