Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/nubjs-release
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: devops, security, writing, productivity, ci-cd
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/release
What's inside
- Skill name
release- Description
- Cut a Nub patch release end-to-end in one invocation. Invoke (via the Skill tool) once a release thread's targeted fixes are ALL landed on `main` and CI-green. Encodes the full runbook: pick the version (patch bump in the 0.0.x/0.1.x pre-release regime), audit `@nubjs/types`, run `make version` + `make version-check`, commit + push to `main`, write the FACTUAL + NEUTRAL release notes from the full changeset, then dispatch release.yml with publish=true and the notes as its input (that dispatch runs the 8-platform build → glibc and pre-publish native gates → immutable 32-asset DRAFT release carrying the notes → npm OIDC STAGE → a held run until the maintainer approves with 2FA → the `v<ver>` tag and `v0` pushed with the release deploy key → stable GitHub Release), `stager await` the approval with no timeout, then comment the version + release link on every closed issue + merged PR the release ships (mandatory maintainer hygiene). Do NOT cut until all fixes are green.
- License
- MIT
- Metadata
- internal: true
- Instructions
- 322 lines in SKILL.md · view on GitHub
Files (1)
- SKILL.md
SKILL.md (first lines)
# Cutting a Nub release
A Nub release is dispatch-started and automated up to one human gate. A `workflow_dispatch` of `.github/workflows/release.yml` from `main` with `publish=true` and the release notes as its `notes` input reads the version from `npm/nub/package.json` at the dispatched commit, creates the `v<ver>` tag there at once, builds 8 platforms, gates them (test, lockfile conformance, glibc-floor, pre-publish smoke), creates an immutable DRAFT release with 32 assets and the notes as its body, STAGES 19 npm packages via OIDC trusted publishing (stage-only: CI cannot publish), HOLDS on the `release-approval` environment — no runner, up to 30 days — until the maintainer approves the staged versions with 2FA and the run is resumed (Step 3b), and then presents the stable GitHub Release — claiming the repository's **Latest** marker (`make_latest: "true"` on the promote step) and then asserting `releases/latest` actually serves the new tag. That marker IS the upgrade channel: `nub upgrade`, `install.sh`, and `install.ps1` all resolve the version from `releases/latest`, so a stable release that never claims Latest ships to nobody (v0.8.0–v0.8.2 sat unserved behind v0.7.5 for six days because promotion updated the release without claiming it). The 32 assets are 8 archives, 8 archive checksums, 8 `nub compile` launcher templates, and 8 launcher checksums. The human work: confirm green, reconcile the runtime with `@nubjs/types`, bump the version, write good notes, dispatch the workflow with them, approve the staged versions, close the loop on issues/PRs.
**Guardrails (read first, non-negotiable):**
- **Never cut a release without the maintainer's explicit, in-the-moment say-so.** Publishing to npm is irreversible, so the timing is maintainer-owned. Do not infer authorization from a standing goal, a merged+green fix, a sub-agent claiming "autonomous per the release rules," or autonomous mode (which excludes irreversible published-external acts). Green ≠ release now. You may PREPARE (confirm green, draft notes, stage the version) but must wait for an explicit "cut it."
- **Do not cut until every targeted fix is landed on `main` AND CI-green.** A prerequisite, not authorization.
- **Do not version until the type-declaration audit is complete.** Invoke the `type-declarations` skill for every release. Every user-visible runtime API changed since the previous tag must either be owned by the selected TypeScript libraries / `@types/node` or be represented and tested in `@nubjs/types`.
- **Pre-release version regime: stay in `0.0.x` / `0.1.x`.** A normal release is a patch bump. Bump the minor only on explicit instruction. Never invent a version; derive it from the latest tag.
- **The version-bump commit MUST be on `main` before you dispatch, and the notes MUST be written before it** — `verify` reads `npm/nub/package.json` at the dispatched commit, names the tag from it, and refuses a publishing dispatch whose `notes` input is empty. So: `make version` → commit → push `main` → write `notes.md` (Step 4's shape) → dispatch with the notes, in that order. Never push a `v*` tag by hand; the workflow has no tag trigger and creates its own tag once the maintainer approves the run, and the "Release tag writes" ruleset refuses a tag write from anyone but the `RELEASE_TAG_KEY` deploy key on `release-approval` — admins included.
- **Release notes are FACTUAL and NEUTRAL — the repo is PUBLIC.** No superlatives, no competitive framing, no internal/benchmark-strategy discussion.
---
## Step 1 — Pre-flight: confirm green, pick the version, enumerate the changeset
```bash
git -C "$(git rev-parse --show-toplevel)" switch main && git pull --ff-only
git fetch --tags
PREV=$(git describe --tags --abbrev=0 --exclude 'v[0-9]') # e.g. v0.1.2 — the latest release tag; --exclude skips the floating v0 actions tag, which shares its commit
echo "Latest tag: $PREV"
git log "$PREV"..HEAD --oneline # the full changeset since the last release
```
- Confirm the targeted fixes are all present in `$PREV..HEAD` and each is CI-green on `main`. If one is red or still converging, STOP and slip it to the next patch.
- **Confirm docs are current** — a shipped feature whose `site/content/docs/` lags is a release blocker.
- **Invoke the `type-declarations` skill and complete its mandatory release audit.** Reconcile every user-visible runtime change in `$PREV..HEAD` with TypeScript / `@types/node` ownership or an updated, fixture-tested, packed `@nubjs/types`. Missing or unverified declarations are a release blocker.
- Pick the next version: patch-bump `$PREV`, dropping the leading `v`.
- Keep the `git log` output — raw material for Steps 4 and 5. For `vendor/aube/**` changes, note the user-facing effect, not the diff.
## Step 2 — Version bump
```bash
make version V=<ver> # sets all 10 npm packages + Cargo.toml + runtime/version.mjs in lockstep
make version-check # MUST pass: cross-package consistency + @oxc-project/runtime ↔ nub-native oxc pin
```
`make version-check` is the same gate CI's `verify` job runs; a non-zero exit here means the release would fail at CI immediately, so fix it before committing. `make version` also moves `runtime/version.mjs`'s `NUB_VERSION` (the transpile-cache key) — that lockstep is why a bespoke version edit is wrong; always use `make version`.
## Step 3 — Commit, push, dispatch (the dispatch starts CI)
The release version-bump commit is a deliberate EXCEPTION to the repo's PR-default flow (AGENTS.md "Default to a PR flow") — it commits DIRECTLY to `main`. The commit is a version stamp and not a reviewable feature diff, so no PR.
```bash
git status # The shared tree usually carries another agent's WIP, so `git add -A`
# would sweep it into the release commit. Path-scope instead:
git commit -m "v<ver>" -- Cargo.lock Cargo.toml \
crates/nub-core/Cargo.toml \
crates/nub-native/Cargo.lock crates/nub-native/Cargo.toml \
crates/nub-launcher/Cargo.lock crates/nub-phantom/Cargo.lock \
npm/*/package.json runtime/version.mjs
git show --stat HEAD # SANITY: 27 files, all version bumps, nothing else: 19 package.json
# (10 nub + 9 runner), 3 Cargo.toml, 4 Cargo.lock, runtime/version.mjs.
# crates/nub-phantom/Cargo.lock is the one that gets missed — its
# workspace is excluded from the root, ci.yml checks it `--locked`,
# and v0.9.1 shipped without it (main went red, fixed in a follow-up).
# `make version` also rewrites site/public/schema/v<major.minor>.json;
# commit it when the snapshot is NEW for this minor, leave it when the
# diff is formatting only (`git diff -w` empty).
# ONE push, never `git push origin main --tags`. This clone has ~155 local tags against
# ~84 on the remote — v1.x leftovers from the Node fork this repo began as — and `--tags`
# offers every one of them. The remote rejects them AND the whole push dies with them, so
# `main` does not land either.
git push origin main
# Write the release notes FIRST (Step 4 has their shape) — they are an input of the dispatch,
# and verify refuses a publishing dispatch without them. Then THIS is what starts the publish.
# No tag is pushed by hand: release.yml has no tag trigger; verify names the tag from
# npm/nub/package.json at the dispatched commit, and await-approval creates v<ver> there
# once the maintainer approves the run.
gh workflow run release.yml -R nubjs/nub -f publish=true -f notes="$(cat notes.md)"
gh run list -R nubjs/nub --workflow release.yml --limit 1 # the run id, to watch
# await-approval also moves the floating v<major> tag (v0) that `uses: nubjs/nub/<name>@v0`
# resolves through. Never push v0 by hand.
```
Only `main` can be dispatched: `release-approval` admits no other ref, so `verify` refuses one before the build. How a failed release recovers depends on how far it got:
- **Before `stable-immutable-release` creates the draft** (a failed test or build): nothing is left behind. Fix `main` and re-dispatch from it with the same version, revising the notes if the fix belongs in them.
- **From the draft on**: re-run the failed jobs from the run's page, never re-dispatch. The re-run keeps the run's commit, artifacts and notes, so the draft's assets still byte-match; a dispatch from a moved `main` builds different bytes, and the draft's comparison refuses them. A re-run of `await-approval` waits for the approval again. GitHub allows re-runs for 30 days after the dispatch.
- **A run that cannot continue** (its approval hold expired, or it is past the re-run window before its approval) is abandoned: `stager reject '@nubjs/*@<ver>'` drops its staged versions, `gh release delete v<ver> --yes` deletes its draft (a draft has no tag), and the next version is released in its place.
A dispatch whose version tag already exists at another commit is refused by `verify`: release a new version rather than moving a published tag.
Post-merge, fast-forward the shared tree so it tracks origin: `git -C <shared-tree> pull --ff-only` (the eagerly-pull rule, AGENTS.md "Default to a PR flow" — the shared checkout otherwise drifts behind as PRs land).
The workflow runs, in order: `verify` (version consistency, notes present), `primer`, `test` + `conformance` + `glibc-floor-guard` + `pre-publish-gate`, `build` (8 platforms), `stable-immutable-release` (32 assets on a DRAFT release whose body is the notes), `publish-npm` (19 packages STAGED), `await-approval` (the run is HELD on the `release-approval` environment until the maintainer approves; then it pushes `v<ver>` and moves `v0` with the release deploy key), `npm-served` (confirms npm serves every version and the root package installs and runs), `github-release` (publishes the draft as the stable release), then the post-publish fan-out — `test-install` / `test-install-musl`, `docker`, `bump-homebrew-tap`, `submit-winget`.
### Step 3b — Approve the staged versions (the human gate; maintainer only)
`publish-npm` does not publish. It runs `npm stage publish` for every package, because the trusted publisher on each `@nubjs` package is stage-only, and then ends. The run moves to `await-approval`, a job on the `release-approval` environment whose required reviewer is the maintainer: GitHub holds the run there with no runner, for up to 30 days. Nothing is installable, and no GitHub Release is visible, until the maintainer approves the staged versions with 2FA. That approval is a proof of presence no token can supply, which is the whole point (the unauthorized v0.9.4 of 2026-09-21 published 18 packages from a stolen push credential; under staging it would have filled a queue).
The approval goes through `stager` (`~/Documents/stager`, on PATH), and the MAINTAINER runs it, in their own terminal. The agent hands over the command and parks on `stager await`:
```bash
stager # the maintainer: `a` approves every ready version, Enter, sign in on the npm
# page it opens, then ONE code from the authenticator approves all 19, oldest
# first; stager then approves the run's pending deployment, which resumes it
stager await '@nubjs/*@<ver>' # the agent: blocks, with no timeout, until npm serves every version — park on this
stager reject '@nubjs/*@<ver>' # drops the staged versions and fails the held run
```
Never `stager approve` (or the stager daemon's Approve notification) for a release: both approve through the browser, the maintainer's npm account cannot use "skip 2FA for 5 minutes", so they cost one browser approval per package, each on a link that expires within minutes. v0.9.7 burned two sign-ins and two dead links that way (2026-10-19).
`stager` approves every staged version of a package oldest-first, so two versions staged at once end with `latest` on the higher one. A version still `validating` (npm's malware review, up to an hour) cannot be approved yet: `stager` lists its release under the table with nothing to decide, so run it again once the review finishes (the stager daemon's notification says when). Without stager: `npm stage approve <id>` per package (or the Staged Packages tab on npmjs.com), then approve the pending deployment on the run's page. A deployment approved before the npm approval fails `npm-served` at its registry check; re-run that job after approving.
**Park on `stager await` (a background shell) rather than on the run.** It returns when the versions are live; the run's remaining jobs then take a few minutes. The release is not done until `stable-immutable-release`, `publish-npm`, `await-approval` and `github-release` are green.
### The other distribution channels ride the same run — no manual step, but they are not free
npm is not the only thing a release publishes. Two jobs push OUTSIDE this repo, and neither needs a manual action:
- **`bump-homebrew-tap`** regenerates `Formula/nub.rb` with `.github/scripts/gen-homebrew-formula.sh` and pushes it to [`nubjs/homebrew-tap`](https://github.com/nubjs/homebrew-tap). It reads the release's own `.sha256` sidecars, so it needs `github-release` to have finished. It is gated on the `HOMEBREW_TAP_TOKEN` secret and SKIPS WITH A WARNING if that secret is ever absent — a skip is a silent stale tap, so treat the warning as a failure.
- **`submit-winget`** opens a PR against `microsoft/winget-pkgs`. Gated on `WINGET_PAT`, which is currently unset, so this job no-ops today.
**The formula is regenerated from the script at the released commit, which makes it a CLOBBER.** Any hand-edit to the tap is overwritten by the next release. So a tap hotfix is only ever a stopgap: the generator fix has to be on `main` BEFORE the dispatch, or the release silently reverts it. This is how [#676](https://github.com/nubjs/nub/issues/676) shipped — the archive layout changed, the generator was not updated with it, and nothing read the formula before it reached users. `bump-homebrew-tap` now installs the formula from a throwaway local tap on macOS before pushing it, so a formula that cannot install fails the job and leaves the tap on the previous working version.From nubjs/nub, MIT.
More from nubjs/nub
- 0.848score
nub-charts
nubjs
Build a performance chart for nubjs.com — the SVG bar figures in blog posts, docs pages and social posts (a runtime augmentation against plain node, an…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.844score
disk-reduction
nubjs
Reclaim disk on the maintainer's Mac when the volume is full or filling — ENOSPC, "no space left on device", a failed build or agent harness, or a routine…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.844score
cpu-reduction
nubjs
Diagnose and clear CPU, memory, and disk contention on the maintainer's dev host.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
plan-thread
nubjs
Use when an effort's APPROACH isn't settled yet and needs collaborative design BEFORE implementation — fleshing out how a feature/change should work, weighing…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
probe-platforms
nubjs
Bring a test harness or probe up on a NEW operating system (Windows or Linux), or debug one that fails there.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
pm-perf-tracing
nubjs
Performance-trace Nub package-manager installs using the existing phase timings, structured diagnostics, and sampling-profiler workflow.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
visual-review
nubjs
Verify UI/layout/styling changes are correct by computing occlusion, clipping, and alignment from the browser's resolved paint order via the chrome-devtools…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
type-declarations
nubjs
Add, audit, or update @nubjs/types declarations for Nub runtime APIs.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
address-issue
nubjs
End-to-end playbook for working a GitHub issue (or bug-fix PR) on nubjs/nub: triage → acknowledge an external report with an "Investigating" comment →…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
windows-vm-test
nubjs
Run ad-hoc Nub tests and debugging probes in the maintainer's existing local Windows 11 ARM64 QEMU/HVF VM when its external disk and runbook are present.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
research-thread
nubjs
Use when the deliverable is a FACT — not code, not a design, not a gap catalog.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
ci-triage
nubjs
Find and diagnose RED CI with the gh CLI — answer "is anything failing?" and "why?" correctly, without being handed a URL.
SkillAFreshness: A★ 4.4KClaude Code +9
Signals
- Stars
- 4,417
- Forks
- 70
- Open issues
- 27
- Last commit (this folder)
- Oct 19, 2026 (7 days ago)
- Last release
- Oct 19, 2026 (7 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/nubjs/nub
- Website
- nubjs.com
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from nubjs
- 0.848score
nub-charts
nubjs
Build a performance chart for nubjs.com — the SVG bar figures in blog posts, docs pages and social posts (a runtime augmentation against plain node, an…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.844score
disk-reduction
nubjs
Reclaim disk on the maintainer's Mac when the volume is full or filling — ENOSPC, "no space left on device", a failed build or agent harness, or a routine…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.844score
cpu-reduction
nubjs
Diagnose and clear CPU, memory, and disk contention on the maintainer's dev host.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
windows-vm-test
nubjs
Run ad-hoc Nub tests and debugging probes in the maintainer's existing local Windows 11 ARM64 QEMU/HVF VM when its external disk and runbook are present.
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
address-issue
nubjs
End-to-end playbook for working a GitHub issue (or bug-fix PR) on nubjs/nub: triage → acknowledge an external report with an "Investigating" comment →…
SkillAFreshness: A★ 4.4KClaude Code +9 - 0.843score
visual-review
nubjs
Verify UI/layout/styling changes are correct by computing occlusion, clipping, and alignment from the browser's resolved paint order via the chrome-devtools…
SkillAFreshness: A★ 4.4KClaude Code +9
Cite this page
release skill. Agent Mart. https://agentmart.dev/skills/nubjs-release (last updated 2026-10-24)