Skip to content

Commit d0dd71e

Browse files
Merge pull request #287 from TheAngryRaven/BETA
Release v2.9.2
2 parents bcb3062 + 64d5ee8 commit d0dd71e

143 files changed

Lines changed: 4770 additions & 803 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
---
2+
name: beta-release-prep
3+
description: Prepare the BETA branch for release into main. Use when the user wants to "prep beta", "cut a release", "get beta ready for main", or "do release prep". Researches every commit between the last release tag and BETA to verify the CHANGELOG is complete and to build release notes, creates a release branch off BETA, flips the eye-in-the-sky coach plugin from the BETA git dependency to the published production npm package, sets the version in package.json to match the topmost unreleased CHANGELOG heading, dates that heading to today, runs the green-before-merge checks, opens a PR into BETA, ensures a BETA→main PR exists whose body is copy-paste-ready release notes, and opens a follow-up "flip coach back to BETA" PR to merge after release. Documents the manual post-release tag + back-merge of main into BETA. Never creates tags.
4+
---
5+
6+
# Beta → Main Release Prep
7+
8+
Automates the release-prep checklist for Dove's DataViewer / LapWing: get the
9+
`BETA` branch ready to merge into `main`. The headline gotcha this skill exists
10+
to catch: **the coach plugin (`eye-in-the-sky`) must be on the published
11+
production npm package on `main`, but during BETA development it points at the
12+
coach repo's git branch — and people forget to flip it.**
13+
14+
> Read `CLAUDE.md` → the "⚠️ SUPER IMPORTANT — coach source differs by branch"
15+
> block before running this. This skill operationalizes that block.
16+
17+
## The two coach-source states (the only lines that differ between BETA and main)
18+
19+
| | `package.json` dependency | `vite.config.ts` `DEFAULT_PLUGIN_PACKAGES` |
20+
|---|---|---|
21+
| **Production (main)** | `"@perchwerks/eye-in-the-sky": "~<x.y.z>"` (tilde-pinned published release) | `"@perchwerks/eye-in-the-sky"` |
22+
| **BETA (dev)** | `"@theangryraven/eye-in-the-sky": "github:TheAngryRaven/DataViewer_coach#BETA"` | `"@theangryraven/eye-in-the-sky"` |
23+
24+
These two lines — `package.json` and `vite.config.ts` line ~156 — are the
25+
**only** ones that change between the states. Nothing else about the coach moves.
26+
27+
## Hard rules
28+
29+
- **NEVER create or push a git tag.** Tagging from Claude errors out. The
30+
maintainer tags the release manually after merge. Do not run `git tag` or
31+
`git push --tags`.
32+
- **NEVER push to `BETA` or `main` directly.** All changes go through PRs.
33+
- Work happens on a fresh branch cut from the current `origin/BETA`.
34+
- The skill is **idempotent**: if the coach is already on production, leave it;
35+
if the CHANGELOG is already dated, don't re-date it. Report what was already
36+
correct rather than forcing a no-op change.
37+
38+
## Procedure
39+
40+
### 0. Sync and branch
41+
42+
```bash
43+
git fetch --all --prune --tags
44+
```
45+
46+
Cut the release branch from `origin/BETA` (not from your current HEAD):
47+
48+
```bash
49+
git switch -c release/v<X.Y.Z> origin/BETA # name decided in step 2
50+
```
51+
52+
If you must name the branch before knowing the version, use a placeholder like
53+
`release/beta-prep` and rename once the version is known, or just keep the
54+
descriptive name you were given. The important part is that it is based on
55+
`origin/BETA` and contains a clean tree.
56+
57+
### 0.5. Research the commits since the last release
58+
59+
Releases are tagged on `main` (e.g. `v2.9.1`). The maintainer tags manually
60+
after merge, so the **latest tag is the last shipped version**. Diff every commit
61+
between it and `BETA` — this drives both a CHANGELOG-completeness check and the
62+
release notes in step 6.
63+
64+
```bash
65+
LAST_TAG=$(git tag --sort=-v:refname | head -1)
66+
echo "Last released tag: $LAST_TAG"
67+
git log "$LAST_TAG"..origin/BETA --oneline # full commit list
68+
git log "$LAST_TAG"..origin/BETA --oneline --merges # merged PRs (often the cleanest summary)
69+
git diff "$LAST_TAG"..origin/BETA --stat # what changed, by file
70+
```
71+
72+
> The tag lives on `main` and (due to the back-merge model — see "Post-release
73+
> back-merge" below) may **not** be an ancestor of `BETA`. That's fine: the
74+
> tagged commit still has `BETA`'s previously-released history as ancestors, so
75+
> `"$LAST_TAG"..origin/BETA` correctly lists only the *new* commits since release.
76+
77+
**Use this to verify the CHANGELOG is complete.** Walk the merged-PR / commit
78+
list and confirm every user-facing change is reflected under the topmost
79+
`## [X.Y.Z] - unreleased` heading. If something user-facing is missing, add it to
80+
that section before continuing (the changelog is part of the change — Golden Rule
81+
4). Internal-only commits (CI, tests, refactors with no user impact) don't need a
82+
changelog line. Keep the raw `git log` output around — step 6 reuses it.
83+
84+
### 1. Flip the coach plugin to production
85+
86+
Inspect the current state first:
87+
88+
```bash
89+
grep -n "eye-in-the-sky" package.json
90+
grep -n "DEFAULT_PLUGIN_PACKAGES" vite.config.ts
91+
```
92+
93+
If it already reads `@perchwerks/eye-in-the-sky` in **both** files, it's already
94+
production — note that and skip the edits. Otherwise:
95+
96+
1. In `package.json`, replace the BETA git dependency line
97+
```json
98+
"@theangryraven/eye-in-the-sky": "github:TheAngryRaven/DataViewer_coach#BETA"
99+
```
100+
with the tilde-pinned published release
101+
```json
102+
"@perchwerks/eye-in-the-sky": "~<x.y.z>"
103+
```
104+
Determine `<x.y.z>` — the published npm version to ship. If the user gave a
105+
version, use it. Otherwise find the latest published version and **confirm it
106+
with the user** before pinning (don't guess a coach version silently):
107+
```bash
108+
npm view @perchwerks/eye-in-the-sky version
109+
```
110+
2. In `vite.config.ts`, set
111+
```ts
112+
const DEFAULT_PLUGIN_PACKAGES = "@perchwerks/eye-in-the-sky";
113+
```
114+
3. Re-resolve the lockfile and install:
115+
```bash
116+
bun install
117+
```
118+
Commit the updated `bun.lock` alongside the two source edits.
119+
120+
### 2. Set the version
121+
122+
The CHANGELOG drives the version. Find the topmost unreleased heading:
123+
124+
```bash
125+
grep -n "unreleased" CHANGELOG.md | head -1
126+
```
127+
128+
That heading is `## [<X.Y.Z>] - unreleased`. Set `package.json` `"version"` to
129+
exactly `<X.Y.Z>`:
130+
131+
```bash
132+
grep -n '"version"' package.json
133+
```
134+
135+
The footer/build version stamp is baked from `package.json` (`vite.config.ts`),
136+
so `package.json` is the only place to edit — there is no second version string
137+
to keep in sync. (`CHANGELOG.md` is the only other place the version appears.)
138+
139+
### 3. Date the changelog
140+
141+
Replace `- unreleased` with today's date on that same topmost heading only:
142+
143+
```
144+
## [<X.Y.Z>] - <YYYY-MM-DD>
145+
```
146+
147+
Use the real current date (the harness provides it; don't hardcode). Leave every
148+
older heading untouched. Do **not** create a new `[Unreleased]` block — per the
149+
changelog rule the next version's block is started after this release ships.
150+
151+
### 4. Green before merge
152+
153+
All four must pass before opening any PR (CI runs them as separate workflows):
154+
155+
```bash
156+
bun run lint
157+
bun run typecheck
158+
bun run test:run
159+
bun run build
160+
```
161+
162+
If any fail, fix forward on this branch before proceeding. The `build` step in
163+
particular validates that the production coach package resolves and bundles.
164+
165+
### 5. Commit and push
166+
167+
```bash
168+
git add package.json vite.config.ts bun.lock CHANGELOG.md
169+
git commit -m "chore(release): prep v<X.Y.Z> — production coach + dated changelog"
170+
git push -u origin release/v<X.Y.Z>
171+
```
172+
173+
(Retry push up to 4× with exponential backoff on network errors only.)
174+
**Do not tag.**
175+
176+
### 6. Open the PRs
177+
178+
Use the available GitHub mechanism — `gh` CLI locally, or the `mcp__github__*`
179+
tools in the remote/web environment. Owner/repo: `TheAngryRaven/DovesDataViewer`.
180+
181+
**PR A — release branch → BETA** (the actual prep change):
182+
- base: `BETA`, head: `release/v<X.Y.Z>`
183+
- title: `Release prep v<X.Y.Z>`
184+
- body: summarize the coach flip (→ production), the version bump, and the dated
185+
changelog.
186+
187+
**PR B — BETA → main** (the release itself, in preparation):
188+
- First check whether one already exists:
189+
```
190+
list_pull_requests(owner, repo, base="main", head="BETA", state="open")
191+
```
192+
- **The body must BE the release notes**, written so the maintainer can
193+
copy-paste it verbatim into the GitHub Release notes when they tag. Build it
194+
from the dated `## [X.Y.Z]` CHANGELOG section (the one you just finalized),
195+
cross-checked against the step 0.5 commit research so nothing user-facing is
196+
missing. Format:
197+
- Start with a one-line `## v<X.Y.Z> — <YYYY-MM-DD>` header.
198+
- Reproduce the CHANGELOG section's `### Added / Changed / Fixed / …`
199+
subsections and bullets verbatim — this is the human-readable changelog,
200+
not a commit dump.
201+
- End with a `**Full changelog:** <LAST_TAG>...v<X.Y.Z>` compare link, e.g.
202+
`https://github.com/TheAngryRaven/DovesDataViewer/compare/<LAST_TAG>...main`.
203+
- Do **not** append the Claude/Co-Authored-By footer to this body — it's
204+
release-notes copy, not a normal PR description.
205+
- **If the PR already exists**, don't open a duplicate — instead update its body
206+
to the freshly-generated release notes (`update_pull_request`) so it stays
207+
copy-paste ready, and note its number/URL.
208+
- If it doesn't exist, create it: base `main`, head `BETA`,
209+
title `Release v<X.Y.Z>`, body = the release notes above. It may stay a draft
210+
until PR A lands.
211+
212+
**PR C — flip the coach back to BETA** (post-release cleanup, opened now):
213+
- Cut another branch from `origin/BETA`:
214+
```bash
215+
git switch -c chore/coach-back-to-beta origin/BETA
216+
```
217+
- Apply the **reverse** of step 1: `package.json`
218+
`"@theangryraven/eye-in-the-sky": "github:TheAngryRaven/DataViewer_coach#BETA"`,
219+
`vite.config.ts``DEFAULT_PLUGIN_PACKAGES = "@theangryraven/eye-in-the-sky"`,
220+
then `bun install`. Commit and push.
221+
- Open PR: base `BETA`, head `chore/coach-back-to-beta`.
222+
- **Title must signal ordering**, e.g.
223+
`[DO NOT MERGE YET] Flip coach back to BETA — merge AFTER beta is merged into main`
224+
- body: explain this restores BETA's git-dependency coach source and must only be
225+
merged once PR B (BETA → main) has merged, so the release isn't undone.
226+
227+
### 7. Report
228+
229+
Summarize for the user: the release version, whether the coach needed flipping or
230+
was already production, the three PR URLs (noting if PR B already existed /
231+
was updated), and an explicit reminder that **no tag was created** — they tag
232+
manually after merge. Then remind them of the two manual post-release steps below.
233+
234+
## Post-release: tag, then back-merge main into BETA (manual, after merge)
235+
236+
This skill stops at *prep* — it can't tag or back-merge, because at prep time the
237+
release isn't merged into `main` yet. Once the maintainer merges **PR B
238+
(BETA → main)**, two things happen in order:
239+
240+
1. **Tag the release on `main`** (maintainer does this by hand — Claude tagging
241+
errors out). The tag (`v<X.Y.Z>`) lands on the `main` merge commit.
242+
2. **Back-merge `main` into `BETA`.** This is the part that's easy to forget and
243+
the reason `git describe` / "commits since last tag" goes wrong over time.
244+
245+
**Why the back-merge is required.** Merging `BETA → main` creates a merge commit
246+
on `main`; the tag sits on `main` only and is **never** copied to `BETA`. So
247+
`BETA` and `main` diverge — the tag isn't an ancestor of `BETA`, `git describe`
248+
from `BETA` can't find the latest release, and the gap compounds with every
249+
release (you can confirm with `git merge-base --is-ancestor v<X.Y.Z> origin/BETA`
250+
returning non-zero). Merging `main` back into `BETA` reconverges the branches so
251+
the tag is reachable from `BETA` again. This is the existing `Main2beta-*` branch
252+
pattern in the repo history.
253+
254+
Do it via a normal PR (don't push to `BETA` directly):
255+
256+
```bash
257+
git fetch --all --tags
258+
git switch -c chore/main-back-to-beta origin/main
259+
git push -u origin chore/main-back-to-beta
260+
# open PR: base BETA, head chore/main-back-to-beta, title "Back-merge main → BETA after v<X.Y.Z>"
261+
```
262+
263+
> Order matters: merge **PR C (flip coach back to BETA)** and this back-merge into
264+
> `BETA` *after* the release is on `main`. If PR C's coach revert lands on `BETA`
265+
> before `BETA → main` merges, the release ships with the BETA git-dependency
266+
> coach — the exact bug this whole skill exists to prevent.
267+
268+
## Notes & edge cases
269+
270+
- If the working tree is dirty at the start, stop and ask the user — don't stash
271+
or discard their work.
272+
- If there is no `unreleased` heading in the CHANGELOG, the release version is
273+
ambiguous — ask the user which version is being cut.
274+
- A git dependency records the resolved coach commit in `bun.lock`; flipping to
275+
the npm package changes the lockfile, which is expected and must be committed.
276+
- If `bun install` can't reach the registry in a restricted network, surface the
277+
error — don't hand-edit `bun.lock`.

0 commit comments

Comments
 (0)