git:20260812.7b95cc6 to git:20261016.9e7b69e

2 added, 2 removed. Audit A to A.

---
name: clud-tag-release
description: Tag a release and let the auto-release workflow build it. Validates version match, clean main, no duplicate tag — then pushes the tag and surfaces the workflow URL.
triggers:
- When the user types "/clud-tag-release" with or without a version arg
- When the user says "cut a release", "tag a release", "ship a version"
- When the user wants to publish a new version of a Rust/Python crate
---
<!-- managed-by: clud -->
# /clud-tag-release
Tag a release the way `zackees/zccache` does it: the tag is the trigger, the workflow does the rest. Five hard rules:
1. **Tag must equal workspace version.** Read the version from `Cargo.toml [workspace.package].version` (workspace) or `Cargo.toml [package].version` (single crate) or `pyproject.toml [project].version`. The tag pushed must match — `1.2.3` and `v1.2.3` are both fine, but `1.2.3` ≠ `1.2.4`. If they don't match, stop and tell the user to bump first; do not silently retag.
2. **Tag from clean default branch only.** Working tree clean, on `main`/`master`, in sync with `origin`. Never tag from a feature branch or a dirty tree.
3. **No duplicates.** Tag must not exist locally or on origin; no GitHub release record for that tag yet. If it does, stop — re-tagging a published release is destructive.
4. **Push the tag — don't `gh release create`.** `gh release create` creates the release record directly and bypasses the auto-release workflow's preflight (version validation, idempotency check, registry skip-existing). The deliverable is an annotated tag pushed to origin; the workflow does everything else.
5. **Verify the workflow kicked off.** After push, find the run, surface its URL, and confirm it transitioned to `in_progress` (or queued). A push that doesn't trigger a run means the workflow isn't wired up — surface that.
## Code Change Rule
This skill should not implement fixes or features. If the release is blocked by a code bug or missing feature, stop and send the user to `/goal`; that work must use RED -> GREEN before this tag-release flow resumes.
## Workflow
1. **Detect the version source.** In repo root, in this order:
- `Cargo.toml` with `[workspace.package].version` → that's the version.
- `Cargo.toml` with `[package].version` (no workspace) → that's the version.
- `pyproject.toml` with `[project].version` → that's the version.
- None of the above → stop with a message; this skill is for Rust/Python projects with a tag-driven release flow.
2. **Detect the auto-release workflow.** Look for `.github/workflows/release-auto.yml` or any workflow whose `on.push.tags` pattern matches version-style tags (`v*`, `[0-9]*`). If none exists, stop and tell the user — point them at `zackees/zccache` (`.github/workflows/release-auto.yml`) for the canonical pattern. Don't try to push a tag with no workflow waiting.
3. **Resolve the tag to push.**
- No arg → use the detected version verbatim. Tell the user: "I'll tag `<version>` (or `v<version>` if your existing tags use that prefix)." Pick the prefix that matches the most recent existing release tag; default to bare (no `v`) if there are no prior releases.
- Arg `<version>` or `v<version>` → use as given, but validate it matches the workspace version. On mismatch: stop and tell the user to bump (`crates/.../Cargo.toml` or `pyproject.toml`) first.
4. **Set the goal.** With the resolved tag known, invoke `/goal Tag <tag> and confirm the auto-release workflow transitioned to in_progress or queued; report the run URL.` so the harness Stop hook blocks until the workflow run is located and surfaced. If any pre-flight gate (step 5) fails the skill aborts before pushing — clear the goal then.
5. **Pre-flight gates.** Every gate is blocking. Run them in parallel where possible (`git fetch` and `gh api` calls). Report the first failure and stop:
- `git rev-parse --abbrev-ref HEAD` is `main` (or the repo's default — `gh api repos/<owner>/<repo> --jq .default_branch`).
- `git status --porcelain` is empty.
- `git fetch origin && git rev-list HEAD...origin/<default>` is empty (in sync, no diverging commits).
- `git tag -l <tag>` and `git tag -l v<tag>` are both empty (no local tag).
- `git ls-remote --tags origin <tag>` and same with `v<tag>` are empty (no remote tag).
- `gh api repos/<owner>/<repo>/releases/tags/<tag>` returns 404 (no published release).
6. **Confirm with the user.** Show:
- Tag to push: `<tag>`
- HEAD SHA: `<sha>` (and one-line commit subject)
- Workflow that will fire: `<release-workflow-path>`
Wait for explicit confirmation. Don't proceed on silence.
7. **Tag and push.**
- `git tag -a <tag> -m "Release <version>"` — **annotated**, not lightweight, so `git describe` works downstream.
- `git push origin <tag>` — push *just the tag*, not `--tags` (which would push every local tag).
- 8. **Locate the triggered run.** Poll `gh run list --workflow=<release-workflow-file> --event=push --limit 5 --json databaseId,headSha,status,url` for up to 30 seconds, looking for a run whose `headSha` matches the tagged commit's SHA. The workflow can take a few seconds to register. If none appears after 30s, surface that — the workflow may not be configured to trigger on this tag pattern.
+ 8. **Locate the triggered run.** Read `gh run list --workflow=<release-workflow-file> --event=push --limit 5 --json databaseId,headSha,status,url` **once**, looking for a run whose `headSha` matches the tagged commit's SHA. Never poll for it: a loop, a `sleep` before the read, `timeout … bash -c`, a Monitor call, a written script or Python are all hand-written waits that clud's hook refuses (zackees/clud#1972). The workflow can take a few seconds to register, so if the run is not listed yet, report the tag and the workflow's Actions page (`https://github.com/<owner>/<repo>/actions/workflows/<release-workflow-file>`) instead of a run URL, and say the run had not registered at the time of the read. If no run ever appears, the workflow may not be configured to trigger on this tag pattern.
9. **Report.** Output exactly:
- Tag pushed: `<tag>` → `<sha>`
- Workflow run: `<url>` (status: `<queued|in_progress>`)
- One line on what the workflow will do (build, publish to PyPI/crates.io, attach release artifacts) — pulled from the workflow file's job names, not invented.
- - Nothing else. Don't `gh run watch` unless the user asked.
+ - Nothing else. Don't follow the run. If the user asked for its result, read it with one `gh run view <id>` in a later turn; never `gh run watch` or a polling loop (for a PR, `"$CLUD_EXE" tool run github/pr_merge_watch.py <PR>` with `run_in_background` is the waiter).
## Failure modes to avoid
- **Mismatched tag and workspace version.** Re-tagging to a version that doesn't exist in `Cargo.toml`/`pyproject.toml` will fail in the workflow's preflight anyway — better to catch it locally and tell the user to bump.
- **`gh release create v1.2.3`.** This creates the release record directly; the auto-release workflow won't fire (or will see the release already exists and skip steps). Always use `git tag` + `git push origin <tag>`.
- **`git push --tags`.** Pushes every local tag, including stale ones from old branches. Push exactly the one you just created.
- **Lightweight tag.** `git tag <tag>` (no `-a`/`-m`) creates a lightweight ref with no metadata. Use annotated tags so `git describe` and release notes have something to anchor on.
- **Tagging from a feature branch.** Even if the commit is identical to main's HEAD, the branch context muddies the audit trail. Switch to main first.
- **Re-pushing a deleted tag.** If a tag was previously published and then deleted, the GitHub release may still exist (just untagged). The skill checks `releases/tags/<tag>`; respect the result.
- **Skipping the workflow detection step.** Pushing a tag with no auto-release workflow is a silent no-op — the user will think they shipped, nothing happens. Always confirm a workflow is wired up to the tag pattern.
## When NOT to use this
- The repo has no auto-release workflow → set one up first (model on `zackees/zccache` `.github/workflows/release-auto.yml`).
- The user wants to publish a hotfix from a branch other than main → that's a different flow; do a backport PR or use the workflow's `workflow_dispatch` input directly.
- The user wants to bump the version → that's `/clud-bump` (or a manual edit + commit + this skill). This skill assumes the version is already bumped and committed.
- The release publishes via a non-tag-driven path (e.g. `cargo publish` from CI on every main push) — the skill's pre-flight checks don't apply.