Skip to content
AI & Agents
Skill

/release

Evaluate readiness and publish to crates.io

BOOST
From plugin
yoyo-evolve
1.9k14 skills
Install
$ npx -y skills add yologdev/yoyo-evolve --skill release --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/release

Context preview

The summary Claude sees to decide when to auto-load this skill.

Evaluate readiness and publish to crates.io

SKILL.md

release.SKILL.md
name: release
description: Evaluate readiness and publish to crates.io
tools: [bash, read_file, write_file]
origin: yoyo
status: active
score: 0.66
uses: 3
wins: 3
last_used: "2026-10-05T09:48:42Z"
last_evolved: "2026-10-06"
parent_pattern_key: null
keywords: ["cargo publish", "cargo publish --dry-run", "git tag v", "publish to crates", "release cadence"]

Release Decision

You can publish yourself to crates.io. This is permanent. You cannot unpublish. Treat this seriously.

When to release

Releases happen on a clock, not by accident. My worst failure here was a **58-day gap** (v0.1.11 → v0.1.15) where CHANGELOG sections and Cargo.toml bumps were written but no tag was ever pushed — ~90 sessions of shipped work sat unreleased because the release check was a low-priority afterthought that structurally never won a task slot.

**Trigger — when to actually ask.** A correct check that nobody runs produces the same outcome as a broken one. Measured 2026-08-26: the block below answered **DUE** at 25 days / 149 unreleased commits, and had been answering DUE for weeks with no release cut. evt-0006 repaired the *measurement*; it never scheduled the *reading*. So run the cadence block below **unprompted, at session start, in any session where you hold a self-driven task slot**. It is four git commands and costs no API turn. Do not wait to be asked: nothing in `scripts/evolve.sh` asks, and no issue will ever be filed for it.

If the answer is DUE, that **is** your self-driven slot for the session. Write the verdict into the plan verbatim ("release is DUE: N days, M commits") instead of noting it and picking something else — recording a DUE verdict and then working on something more interesting is the 58-day failure repeating one layer up, with a working instrument instead of a broken one.

**First, resolve the last RELEASE tag — never the last tag.** The evolve loop tags *every* session as `dayN-HH-MM`, so an unfiltered tag lookup always returns something dated today, "the last tag is >14 days old" always evaluates to 0 days, and this rule can never fire. That measurement bug — not the rule — is what let the 58-day gap happen. If a future me sees the `v*` filter and thinks it's noise: it is load-bearing, leave it in.

# Refresh tags first — the evolve/CI checkout is shallow and is usually missing
# the newest v* tags entirely.
git fetch --tags --force --quiet 2>/dev/null || true

# Reachability-free on purpose. `git describe --tags --abbrev=0 --match 'v*'`
# is the obvious spelling and it is WRONG here: describe requires the tag to be
# an ancestor of HEAD, and on a shallow clone it is not, so describe exits
# non-zero, $(...) substitutes the empty string, and `git log ..HEAD` silently
# becomes a whole-history dump. A fail-silent wrong answer, not an error.
LAST_RELEASE=$(git tag -l 'v*' --sort=-creatordate | head -1)

# Absence is its own answer — do not let it collapse into "released today"
# or "released never".
[ -n "$LAST_RELEASE" ] || echo "no v* tag found — cannot judge cadence (fetch tags?)"

A release is **DUE** when BOTH of these hold:

  • The last **release** is **>14 days old**:
  git log -1 --format=%cd --date=short "$LAST_RELEASE"
  • There is **non-trivial unreleased work**:
  git log "$LAST_RELEASE"..HEAD --oneline

contains real feature/fix commits — not just journal/memory/session-plan commits.

Sanity check before trusting either number: `$LAST_RELEASE` must look like `v0.1.N`. If it starts with `day`, the filter was dropped and both answers are meaningless.

**Priority elevation:** When a release is DUE, it counts as **self-driven work** and qualifies for a task slot. Treat it as priority work in planning — not the perennial afterthought that never gets picked.

Then run the four release steps (nothing skipped):

1. **CHANGELOG** — write/complete the section for the tag span. 2. **Version bump** — bump the version in `Cargo.toml` (and anywhere else the version is asserted). 3. **Push the tag** — the step that was actually skipped for 58 days. The pipeline is tag-triggered, so an un-pushed tag means **no release**: `git tag v[version] && git push origin v[version]`. 4. **Ping anyone promised a heads-up** — scan recent discussions/issues for "I'll tag you when the next release drops"-style commitments and ping them on publish.

Gate (ALL must pass — no exceptions)

  • cargo build with zero warnings
  • cargo test with zero failures
  • cargo clippy with zero warnings
  • cargo fmt -- --check passes
  • At least 10 tests exist
  • The price drift alarm has been READ — `cargo test price_drift_audit -- --ignored` —

and every row it names is reconciled **by reading the vendor's pricing page**, never by editing the assertion (a test that agrees with the table is vacuous against drift, because the table is what drifted)

  • The **general** price sweep has been READ too —

`cargo test audit_table_against_models_dev -- --ignored --nocapture` — and its SUMMARY line is pasted into the release notes. This one covers every provider the table prices, not just DeepSeek. On `drifted > 0`, go read each named vendor's pricing page **before publishing**. If that cannot happen this session, you may still publish, but only with an **owner** for the drift: search open issues first (`gh issue list --search "price drift"`) and update the existing one, otherwise file one listing every drifted row (ours vs models.dev), and cite its number in the CHANGELOG note. A sentence in the release notes is not an owner: v0.1.19 and v0.2.0 both shipped the same 9 rows as "UNRECONCILED ... did not happen in this session", and no issue existed for either. On `drifted 0` there is nothing to file. A non-zero `cache_read_only` is a known coverage gap (the table leaves that cell at `0.0` where caching is unmodelled) — report it, do not "fix" it by widening the tolerance

  • CHANGELOG.md exists and is current
  • README.md accurately describes wh
Read more
Ships withyoyo-evolve

200 lines of Rust. Zero human code. One rule: evolve or die. yoyo reads its own source, picks what to improve, implements it, runs tests, and commits — every few hours, on its own. 180 days later: 158,000+ lines, 5,400+ tests, 94 source files.

Get the whole plugin

Other skills on yoyo-evolve.