docs(agents): document the release workflow and link .claude/skills
Update the ascapi-release runbook for release-crates.yml: pushing the vX.Y.Z tag verifies the workspace, publishes the eight crates through crates.io trusted publishing, and dispatches the GitHub and MCP Registry releases. The manual publish steps become a fallback. Point .claude/skills at .agents/skills so Claude Code and the other agents read the same skills. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Seto Elkahfi committed
Oct 12, 2026 at 00:59 UTC
3bc688b9fb33393d58e2d984ae377050c7bbd910
2 files changed
+88
-25
.agents/skills/ascapi-release/SKILL.md
+87
-25
@@ -3,23 +3,26 @@ name: ascapi-release
3
description: >
4
Cut a new release of the smbcloud-ascapi Rust workspace: merge development into
5
main, bump the single workspace version with cargo-workspaces, create the
6
- Release commit and matching tags, keep server.json in sync, publish the crates
7
- to crates.io, and run the MCP Registry workflow. Use whenever the user asks to
6
+ Release commit and matching tags, keep server.json in sync, then push the tag
7
+ so release-crates.yml publishes the crates to crates.io and dispatches the
8
+ GitHub and MCP Registry releases. Use whenever the user asks to
9
release, cut a version, tag, bump the version, publish to crates.io, or publish
10
the MCP server for this repo. Covers the forge/remote quirks (GitHub, remote
11
named "setoelkahfi"), the signing identity, and the registry workflow's
12
hard invariants that will fail a release if violated.
13
metadata:
14
author: smbcloudXYZ
14
- version: "1.0"
15
+ version: "1.1"
16
---
17
18
# smbcloud-ascapi release runbook
19
20
One Rust workspace, one version. The root `Cargo.toml` `[workspace.package]`
21
`version` (around line 15) is the single source of truth — every crate inherits
21
-it with `version.workspace = true`. A release bumps that one number, tags it,
22
-publishes the crates, and republishes the MCP server.
22
+it with `version.workspace = true`. A release bumps that one number and tags
23
+it. Pushing the `vX.Y.Z` tag starts `.github/workflows/release-crates.yml`,
24
+which publishes the crates and then dispatches the GitHub release and the MCP
25
+Registry release.
26
27
## Facts that are easy to get wrong
28
@@ -125,32 +128,66 @@ grep -n '"version"' server.json # both -> X.Y.Z
128
sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1 # -> X.Y.Z
129
git status --short # clean
130
cargo check --workspace --locked
131
+```
132
+
133
+Push the branch first, then the tags. The tag push is what starts the
134
+publish, and the workflow checks out the tag, so `main` has to be on the
135
+remote before it runs:
136
+
137
+```sh
138
git push setoelkahfi main
139
git push setoelkahfi --tags --force # --force only if you moved tags in step 3
140
```
141
132
-## Step 5 — publish crates to crates.io
133
-
134
-Publish in dependency order (lower crates first), because the MCP Registry
135
-workflow verifies the **published** CLI crate, and the CLI depends on the rest:
142
+## Step 5 — `release-crates.yml` publishes the crates
143
+
144
+Pushing a `vX.Y.Z` tag starts `.github/workflows/release-crates.yml`. The
145
+per-crate `smbcloud-ascapi-*@X.Y.Z` tags don't match its `v*.*.*` filter, so
146
+they don't start extra runs. Three jobs run in order:
147
+
148
+1. **`verify`** fails the run if the tag differs from the workspace version in
149
+ `Cargo.toml`, then runs `cargo fmt --check`, clippy with `-D warnings`,
150
+ `cargo test --workspace --locked`, and `cargo check` on `core`, `aso`,
151
+ `pricing`, `signing` and `reports` on their own.
152
+2. **`publish`** (environment `crates-io`) publishes `core`, `aso`, `pricing`,
153
+ `signing`, `reports`, `frontend`, `mcp`, `cli`, in that order, with
154
+ `cargo publish -p <crate> --locked`. Authentication is crates.io trusted
155
+ publishing: the job exchanges its GitHub OIDC token through
156
+ `rust-lang/crates-io-auth-action`, so there is no registry token secret.
157
+ A crate version that is already on crates.io is skipped, so re-running the
158
+ workflow after a partial failure resumes where it stopped.
159
+3. **`dispatch-distributions`** dispatches `release-github.yml` (six binaries
160
+ attached to a GitHub release) and `release-mcp-registry.yml`, both with the
161
+ same tag. It only runs when `publish` succeeded.
162
+
163
+Watch it with:
164
165
+```sh
166
+gh run list --workflow release-crates.yml --limit 3
167
+gh run watch <run-id>
168
```
138
-core → aso / pricing / signing / reports → frontend → mcp → cli
139
-```
169
+
170
+To run it by hand for an existing tag (for example after fixing a trusted
171
+publisher config), dispatch it against that tag:
172
173
```sh
142
-cargo workspaces publish --from-git --allow-branch main --yes
174
+gh workflow run release-crates.yml --ref vX.Y.Z -f tag=vX.Y.Z
175
```
176
145
-`--from-git` publishes exactly what the tags describe and skips versions already
146
-on crates.io (safe to re-run). If you publish manually with `cargo publish -p`,
147
-respect the order above and wait for each to index before the next.
177
+### One-time setup it depends on
178
149
-## Step 6 — publish the MCP server
179
+- Each of the eight crates has a trusted publisher on crates.io for repository
180
+ `smbcloudXYZ/smbcloud-ascapi` and workflow `release-crates.yml`. If a crate
181
+ is missing one, `cargo publish` fails for that crate and the later crates are
182
+ not published. Add the publisher and re-run.
183
+- If a trusted publisher names an environment, it must be `crates-io`, which
184
+ is the environment the `publish` job runs in.
185
151
-Dispatch `.github/workflows/release-mcp-registry.yml` with the tag (e.g.
152
-`vX.Y.Z`). It authenticates to the MCP Registry via GitHub OIDC (no secret) and
153
-enforces these invariants — a release fails if any is violated:
186
+## Step 6 — MCP Registry release
187
+
188
+`release-mcp-registry.yml` is dispatched by step 5, so there is normally
189
+nothing to do here. It authenticates to the MCP Registry via GitHub OIDC (no
190
+secret) and enforces these invariants. A release fails if any is violated:
191
192
1. **Version match:** root `Cargo.toml` workspace version == tag (minus the `v`).
193
Step 2 guarantees this.
@@ -160,22 +197,47 @@ enforces these invariants — a release fails if any is violated:
197
3. **Ownership marker in the published crate:** the CLI crate downloaded from
198
crates.io for this exact version must contain the same marker in its README.
199
crates.io versions are immutable, so **the marker must be in the README
163
- BEFORE you publish the crate in step 5** — if a published version lacks it,
164
- you cannot fix that version; you must cut a new release. This is why step 5
165
- precedes step 6.
200
+ BEFORE you tag**. If a published version lacks it, you cannot fix that
201
+ version; you must cut a new release.
202
203
The workflow rewrites `server.json`'s version from the tag, publishes with
204
`mcp-publisher` (treating "duplicate version" as success), and verifies the
169
-server is listed in the registry.
205
+server is listed in the registry. It waits up to ten minutes for crates.io to
206
+index the CLI crate, so it is safe that it starts while step 5 is still
207
+publishing.
208
+
209
+To publish the server again for a tag, dispatch it yourself:
210
+
211
+```sh
212
+gh workflow run release-mcp-registry.yml --ref vX.Y.Z -f tag=vX.Y.Z
213
+```
214
+
215
+## Manual fallback
216
+
217
+Only if the workflow can't run (Actions outage, trusted publishing not set up
218
+yet). Publish in dependency order, because the CLI depends on the rest:
219
+
220
+```sh
221
+cargo workspaces publish --from-git --allow-branch main --yes
222
+```
223
+
224
+`--from-git` publishes exactly what the tags describe and skips versions
225
+already on crates.io. Then dispatch `release-github.yml` and
226
+`release-mcp-registry.yml` yourself with the tag.
227
228
## Release-order summary
229
230
merge → bump+tag → sync server.json into Release commit → verify sig & versions
174
-→ push main+tags → **publish crates (step 5)** → **then** run MCP Registry
175
-workflow (step 6). Steps 5 and 6 are order-dependent; don't swap them.
231
+→ push `main`, then tags → `release-crates.yml` verifies and publishes → it
232
+dispatches the GitHub release and the MCP Registry release. Check the run in
233
+Actions; nothing after the tag push is manual.
234
235
## References
236
237
+- `../../../.github/workflows/release-crates.yml` — verify, publish and
238
+ dispatch, in full.
239
+- `../../../.github/workflows/release-github.yml` — the binary build and GitHub
240
+ release.
241
- `../../../.github/workflows/release-mcp-registry.yml` — the registry workflow
242
and its invariant checks, in full.
243
- Past release design notes live in `.agents/plans/` (upload / signing-identity
.claude/skills
new
+1
@@ -0,0 +1 @@
1
+../.agents/skills
\ No newline at end of file