Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 38 additions & 6 deletions .claude/skills/release-manager/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,22 +269,46 @@ Pass criteria: the upgrade lands on the `0.0.0-commit.<sha>` build, the install

Across the full catalog most failures are not regressions, and reporting them as "N failed" without triage is useless to the release manager. Sort every failure into one of these before drawing any conclusion:

- **Preview-build artifacts.** These are caused by the `0.0.0-commit.<sha>` version string itself and cannot happen for a real npm release, so they are never blockers. The recurring ones: pnpm `ERR_PNPM_TRUST_DOWNGRADE` ("possible package takeover"), npm `ETARGET` from a `before`/min-release-age policy, bun `minimum release age`, `ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION` when a project declares `vite` as a peer (migrate writes the `npm:@voidzero-dev/vite-plus-core@...` alias there), and Docker builds whose context does not carry the bridge `.npmrc`.
- **Registry-bridge fetch flakes.** Check these first, because they are common and they masquerade as something far worse. The bridge drops tarball requests under load: pnpm logs `error (23). Will retry`, or the install dies with `ECONNRESET aborted`. The dangerous case is a platform binding, because `@voidzero-dev/vite-plus-<platform>` is an **optional** dependency: when its download exhausts the retries, the installer skips it and still reports success, and the job then fails much later at the first command that loads the binding, with `Cannot find native binding` / `Cannot find module '@voidzero-dev/vite-plus-linux-x64-gnu'`. That is a local `node_modules` resolution failure and says nothing about the registry, but NAPI's loader appends generic "npm has a bug related to optional dependencies" boilerplate that reads like a publishing problem. Do not conclude the addon was unpublished; the bridge publishes every platform package for every commit build. Confirm, then re-run:

```bash
curl -s "https://registry-bridge.viteplus.dev/@voidzero-dev%2fvite-plus-linux-x64-gnu" \
| python3 -c "import json,sys; print('0.0.0-commit.<sha>' in json.load(sys.stdin)['versions'])"
gh run rerun <run-id> --failed --repo <owner>/<repo>
```

Grep every failing log for `error (23)` and `ECONNRESET` before classifying it as anything else. In one release this single cause accounted for 8 fork failures, all of which passed on re-run.

- **Preview-build artifacts.** These are caused by the `0.0.0-commit.<sha>` version string itself and cannot happen for a real npm release, so they are never blockers. The recurring ones: pnpm `ERR_PNPM_TRUST_DOWNGRADE` ("possible package takeover"), npm `ETARGET` from a `before`/min-release-age policy, bun `minimum release age`, `ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION` when a project declares `vite` as a peer (migrate writes the `npm:@voidzero-dev/vite-plus-core@...` alias there), `ERR_PNPM_TARBALL_URL_MISMATCH` or a failed supply-chain policy check against the bridge tarball URLs, and Docker builds whose context does not carry the bridge `.npmrc`.
- **Pre-existing failures.** Prove it rather than asserting it, with whichever control is cheaper: install the previous release into an isolated home and re-run the same command, or check whether the fork's base branch CI already fails. The isolated-home control is the highest-value technique in this step, since it converts a scary-looking failure into a one-line fact:

```bash
VP_HOME=$HOME/.cache/vp-control-<prev> VP_VERSION=<prev> VP_NODE_MANAGER=no bash packages/cli/install.sh
VP_HOME=$HOME/.cache/vp-control-<prev> PATH="$HOME/.cache/vp-control-<prev>/bin:$PATH" vp migrate <project> --no-interactive
cd <project> && VP_HOME=$HOME/.cache/vp-control-<prev> VP_NODE_MANAGER=no \
PATH="$HOME/.cache/vp-control-<prev>/bin:$PATH" vp migrate <project> --no-interactive
```

**Run the control from inside the project directory.** Launching it from the vite-plus checkout makes `vp` delegate to that checkout's `packages/cli/dist` instead of the pinned release, which silently invalidates the comparison (it fails with an unrelated error such as `Fail to parse yaml as RuleConfig`).

Reset the project (`git reset --hard`, `git clean -fd`, delete lockfile and `node_modules`) before the control run so it starts from the same state as the real run. When the control reproduces the failure, say so with the evidence (same exit code and same error class) rather than just "pre-existing".

- **Stale pins: weight the forks that were actually on the previous release.** A fork pinned several releases back does not test the release under review at all; `vp migrate` performs a multi-release jump, and any resulting type errors are evidence about that jump. Before drawing a conclusion, work out what each fork was on and judge the release primarily on the forks upgrading from the immediately previous release. Derive the pin from the upgrade commit itself, not from `HEAD`, since later commits on the test branch hide it:

```bash
sha=$(git -C <dir> log --format=%H --grep='^test: upgrade vite-plus to prerelease' -n 1)
git -C <dir> show "$sha" | grep -E '^-.*vite-plus'
```

Reset the project (`git reset --hard`, `git clean -fd`, delete lockfile and `node_modules`) before the control run so it starts from the same state as the real run.
Report that subset separately; "2 of the 7 forks on the previous release pass, the other 5 fail on fork infrastructure" is a far stronger statement than a headline pass rate over the whole catalog.

- **Project-side and infra failures.** Dependency conflicts between the project's own packages, missing fork secrets, third-party GitHub Apps not installed on the fork, network timeouts. Retry once before classifying anything as a network failure; they pass on retry.
- **Project-side and infra failures.** Dependency conflicts between the project's own packages, missing fork secrets, third-party GitHub Apps not installed on the fork, network timeouts. Retry once before classifying anything as a network failure; they pass on retry. Two recurring shapes worth naming: a package that imports a dependency it never declared and only ever resolved through hoisting (`Cannot find package 'oxfmt'`) breaks as soon as the harness regenerates the lockfile; and a project whose own dependency has no `main`/`module`/`exports` cannot load its config under any vite-plus version.
- **Harness artifacts.** Failures your own test setup caused, such as a lockfile the harness deleted and the install never regenerated. Fix these and re-run rather than reporting them.

Report the tally by cause, not just pass/fail, and state plainly which failures you controlled for and which you classified from the error text alone. Only a failure that reproduces on the candidate but not on the previous release is a regression.

Two long-run mechanics worth knowing: `vp migrate` installs Vite+ git hooks in the project, so any later `git commit`/`git push` there needs `--no-verify`; and macOS has no GNU `timeout`, so a driver script that time-boxes runs needs its own watchdog.
Two long-run mechanics worth knowing: `vp migrate` installs Vite+ git hooks in the project, so any later `git commit`/`git push` there needs `--no-verify`; and macOS has no GNU `timeout`, so a driver script that time-boxes runs needs its own watchdog. If that driver runs projects in parallel, kill the whole process tree on timeout, not just the wrapper: an orphaned `pnpm install` holds the store lock and the next project then hangs at 0% CPU with no output, which reads like a vite-plus hang and is not one.

Two fork-CI blockers are worth fixing rather than reporting, both on the **test branch only** so the tracked branch stays clean against upstream. A fork whose workflows never trigger on `pull_request` reports "no checks" and proves nothing: add a minimal workflow that runs `vp run build` through whatever setup the project already uses. A fork whose workflows target third-party runners (self-hosted labels such as `blacksmith-*`) queues every job forever, because those labels only resolve for the upstream org: map them to GitHub-hosted equivalents, replacing the longest label first so an `-arm` suffix is not left half-rewritten. Runner-specific _actions_ need more than a label swap and are usually not worth fixing.

## 5. Release-branch CI

Expand Down Expand Up @@ -359,7 +383,15 @@ Merging the release PR is the release trigger. Before merging confirm: CI green,
docker run --rm ghcr.io/voidzero-dev/vite-plus:X.Y.Z vp --version
```

`vp upgrade` reporting `Already up to date (X.Y.Z)` also passes. Caveat: `vp upgrade` exists only on standalone-installer (`~/.vite-plus`) installs; if the release manager's `vp` is managed another way (e.g. mise), `vp upgrade` is missing and `vp update` is not a substitute (it runs `pnpm update` on the current project, so never run it inside the vite-plus checkout). In that case rely on the npm/GHCR checks plus the in-container `vp --version`. Do not trust `vp upgrade`'s success message on its own: on a machine where `~/.vite-plus/current` is a symlink to a `local-dev-*` directory (a `pnpm bootstrap-cli` build), it can print `Updated vite-plus from A -> B` while creating no `~/.vite-plus/X.Y.Z` directory and leaving `current` untouched. Confirm with `readlink ~/.vite-plus/current` and `ls ~/.vite-plus`, and if that is the case say the check was inconclusive rather than reporting it as passing. The Docker check must run `vp --version` inside the image, not just pull it: the output must report `vp vX.Y.Z`. Outside a project that output lists no bundled tools, so inspect the installed image package tree under `~/.vite-plus/X.Y.Z/node_modules/.pnpm` and confirm the bundled tool packages and versions match the changelog's Bundled Versions table. `tsdown` will be absent from that tree because it is bundled into `@voidzero-dev/vite-plus-core`; verify it with `npm view @voidzero-dev/vite-plus-core@X.Y.Z bundledVersions --json` instead. If no local Docker daemon is running, confirm the `publish-docker` job succeeded and the GHCR manifest exists, then still run the in-container check once a daemon is available:
`vp upgrade` reporting `Already up to date (X.Y.Z)` also passes. Caveat: `vp upgrade` exists only on standalone-installer (`~/.vite-plus`) installs; if the release manager's `vp` is managed another way (e.g. mise), `vp upgrade` is missing and `vp update` is not a substitute (it runs `pnpm update` on the current project, so never run it inside the vite-plus checkout). In that case rely on the npm/GHCR checks plus the in-container `vp --version`. Do not trust `vp upgrade`'s success message on its own: on a machine where `~/.vite-plus/current` is a symlink to a `local-dev-*` directory (a `pnpm bootstrap-cli` build), it can print `Updated vite-plus from A -> B` while creating no `~/.vite-plus/X.Y.Z` directory and leaving `current` untouched. The same false pass happens when the shell still carries a `VP_HOME` export or an isolated control home on `PATH` from an earlier smoke-test control run: `vp upgrade` then upgrades that throwaway home and reports success while the real install is untouched (the giveaway is an upgrade "from" a `0.0.0-commit.<sha>` version). Run the check in a clean environment and against the real binary, then confirm the result:

```bash
env -u VP_HOME -u VP_NODE_MANAGER PATH="$HOME/.vite-plus/bin:/usr/bin:/bin" ~/.vite-plus/bin/vp upgrade
readlink ~/.vite-plus/current # must be X.Y.Z
ls ~/.vite-plus # must contain X.Y.Z
```

If `current` did not move, say the check was inconclusive rather than reporting it as passing. The Docker check must run `vp --version` inside the image, not just pull it: the output must report `vp vX.Y.Z`. Outside a project that output lists no bundled tools, so inspect the installed image package tree under `~/.vite-plus/X.Y.Z/node_modules/.pnpm` and confirm the bundled tool packages and versions match the changelog's Bundled Versions table. `tsdown` will be absent from that tree because it is bundled into `@voidzero-dev/vite-plus-core`; verify it with `npm view @voidzero-dev/vite-plus-core@X.Y.Z bundledVersions --json` instead. If no local Docker daemon is running, confirm the `publish-docker` job succeeded and the GHCR manifest exists, then still run the in-container check once a daemon is available:

```bash
TOKEN=$(curl -s "https://ghcr.io/token?scope=repository:voidzero-dev/vite-plus:pull" \
Expand Down
Loading