From cfa7bd04457b5ef67d8c81c46d58079b55d7defe Mon Sep 17 00:00:00 2001 From: leodido <120051+leodido@users.noreply.github.com> Date: Mon, 4 May 2026 10:22:34 +0000 Subject: [PATCH] docs(skills): add kfeatures-release-flow skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Captures the release-cut workflow distilled in the v0.5.0 cycle: the staged CHANGELOG → commit → push → tag → push sequence with explicit-approval checkpoints, GH007 / noreply identity recovery, PR-label-driven categorized release notes (labeler workflow ↔ .github/release.yml coupling, additive-labeler retro-labeling recipe), and cosign keyless verification of published artifacts. Co-authored-by: Ona --- .skills/kfeatures-release-flow/SKILL.md | 79 ++++++++++++++ .../references/release-cut.md | 100 ++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 .skills/kfeatures-release-flow/SKILL.md create mode 100644 .skills/kfeatures-release-flow/references/release-cut.md diff --git a/.skills/kfeatures-release-flow/SKILL.md b/.skills/kfeatures-release-flow/SKILL.md new file mode 100644 index 0000000..ba59fcb --- /dev/null +++ b/.skills/kfeatures-release-flow/SKILL.md @@ -0,0 +1,79 @@ +--- +name: kfeatures-release-flow +description: Cut a kfeatures release. Covers the staged CHANGELOG/commit/tag/push workflow with explicit-approval checkpoints, GH007 / noreply identity recovery, PR-label-driven categorized release notes, and cosign keyless verification. Use when the user says "cut a kfeatures release", "tag vX.Y.Z", "release notes are uncategorized", "regenerate release notes", "verify a kfeatures download". Triggers on "kfeatures release", "cut release", "tag vX", "labeler", "release.yml categories", "cosign verify-blob", "GH007". +--- + +# kfeatures release flow + +For per-PR conventions read `AGENTS.md` and `CONTRIBUTING.md`. This skill covers the release cut. + +## Hard rules + +1. Never commit, push, tag, or merge without explicit user approval per step. A previous "yes" does not authorize the next operation. Ask again. +2. CHANGELOG `[Unreleased]` is the staging area. Every user-visible change lands there in the PR that introduces it. The release-cut PR only renames the section. +3. Tag pushes create a GitHub Release via GoReleaser (`push: tags: v*`). Confirm `gh release view vX.Y.Z` shows the right body before treating the release as done. +4. The release workflow reads the tag's tree, not HEAD. Do not push fixes to `main` after tagging and expect them in the release. + +## Workflow + +Read `references/release-cut.md` for the command sequence. Steps: + +1. Cut commit: rename `[Unreleased]` to `[X.Y.Z] - YYYY-MM-DD`, update compare links at bottom of `CHANGELOG.md`. Subject: `docs(changelog): cut vX.Y.Z release`. Ask before committing. +2. Push the cut commit to `main`. Ask before pushing. +3. Wait for `main` CI green before tagging. +4. Annotated tag with noreply identity (see below). Ask before tagging. +5. Push the tag. Ask before pushing. +6. Verify `gh release view vX.Y.Z` shows the categorized body, all per-platform tarballs, `checksums.txt`, and a `.sigstore.json` sibling for each. + +## GH007: noreply identity + +The release commit and tag must be authored with the maintainer's GitHub-noreply email (`+@users.noreply.github.com`), not a personal email. The devcontainer's `postCreateCommand` sets `user.email` and `user.name` accordingly; the canonical values for this repo live in `.devcontainer/devcontainer.json`. Verify before tagging: + +```bash +git config user.email # must end in @users.noreply.github.com +git config user.name +``` + +If the identity is wrong (e.g. running outside the devcontainer), use the inline form for each git operation: + +```bash +git -c user.email= -c user.name= commit -m "..." +git -c user.email= -c user.name= tag -a vX.Y.Z -m "vX.Y.Z" +``` + +Symptom of a wrong identity on push: `remote: error: GH007: Your push would publish a private email address.` Recover by amending the commit or re-creating the tag with the noreply identity, then re-push. + +## Categorized release notes + +GitHub generates the release body from PR labels read at the tag, via `.github/release.yml`. Two files must stay in sync: + +- `.github/workflows/labeler.yml`: auto-labels new PRs from their Conventional Commit prefix. The `prefixMap` and the `*(deps)` / `*(dependencies)` scope override govern what counts as `dependencies` vs `chore`. +- `.github/release.yml`: the category list (Breaking / Features / Bug Fixes / Documentation / Dependencies / Other) and the `exclude.labels` list (`no-releasenotes`, `chore`). + +Add a new prefix or category in both files in the same PR. + +### Re-categorizing an already-shipped release + +The labeler is additive and only fires on `pull_request: [opened, edited]`. Old PRs need manual label fixes: + +1. List PRs in the release range: search for `merged:>=YYYY-MM-DD base:main`. +2. For each, set the right label via `gh pr edit --add-label