chore(release-skill): time label-gated PR merges around the release (#3654)
Teaches the `/release` skill about the `merge-immediately-before-release` and `merge-immediately-after-release` labels. ## Why Both labels exist on the repo and are in active use — #3651 is open right now carrying `merge-immediately-after-release`, with `[DO NOT MERGE UNTIL RELEASE]` in the title. The title prefix is the tell: the skill has no idea these labels exist, so maintainers have been hand-signalling the timing instead. I checked what an agent actually does today. Given four open PRs and no hint about the labels, it produces a complete nine-step release plan and never runs `gh pr list` even once. The labeled PR is simply invisible to it and gets stranded. ## What the labels mean These mark PRs — typically ones touching `docs/` or `install.sh` — that deploy on merge to `main` (via `trigger-docs-deploy.yml`) rather than shipping inside the release artifact. So their merge has to be timed around the release rather than left to land whenever. | Label | Merged at | In the tag & artifact? | |-------|-----------|------------------------| | `merge-immediately-before-release` | Step 7.5 — after the prep PR merges, before the tag | Yes | | `merge-immediately-after-release` | Step 8.5 — after the release workflow uploads assets | No | An `after-release` PR being absent from the tag and artifact is the intended outcome, not a problem to fix. An author who needed the change in the artifact would have used the before-release label. ## Changes - **Step 2.5** surveys both labels up front and asks which of the PRs found should in fact be merged at their labeled time. Declined PRs are dropped for the rest of the run. - **Step 7.5** merges before-release PRs after the prep PR lands and before the tag. They're absent from the changelog, which was cut earlier — that's expected, and the step says so to stop anyone regenerating it. - **Step 8.5** merges after-release PRs only once the release workflow has finished and the assets exist. `install.sh` fetches `releases/latest/download/...`, so merging on tag-push alone would put an installer live against a release that's still building. Aborts and merges nothing if the workflow failed or assets are missing. Each merge takes a second confirmation at the point of merging: the survey approves the *timing*, the checkpoint approves merging *now*. ## Drive-by fix Step 7's `gh pr create` was missing a line continuation after `--repo atuinsh/atuin`, so `--draft` parsed as a separate command — the PR got created **non-draft** and the only symptom was a `--draft: command not found` that's easy to miss in scrollback. It's valid bash syntax (`--draft` is just a command name that doesn't exist), so nothing catches it at parse time. Scanned every other shell block in the file for the same defect class; this was the only instance. ## Testing Behaviour was verified with paper dry-runs against the skill text — no real release was run. - **Baseline, labels hidden:** never checked the labels at all. Confirms the gap is real. - **Baseline, labels handed over:** still diverged — merged the before-release PR before the clone, invented its own asset-waiting rule, and said it was "inferring policy from label names." - **With the new steps:** placement matches spec exactly, both confirmations fire, unlabeled PRs aren't surfaced as candidates, changelog left alone. - **Pressure test** (flight deadline + "that label was probably a mistake" + a plausible technical argument for merging early): held the line, and did it by reasoning rather than rule-citing — it worked out that `install.sh` never ships inside the artifact anyway, so merging early wouldn't achieve what was asked and *would* open the broken-installer window. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>