Add ascapi-release skill documenting the release runbook
Co-Authored-By: siGit Code v1.6.7-acp <noreply@sigit.si>
Seto Elkahfi committed
Oct 11, 2026 at 20:01 UTC
de82e1e16d5ffb9a31146893f841528bcba00a52
1 file changed
+182
.agents/skills/ascapi-release/SKILL.md
new
+182
@@ -0,0 +1,182 @@
1
+---
2
+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
8
+ release, cut a version, tag, bump the version, publish to crates.io, or publish
9
+ the MCP server for this repo. Covers the forge/remote quirks (GitHub, remote
10
+ named "setoelkahfi"), the signing identity, and the registry workflow's
11
+ hard invariants that will fail a release if violated.
12
+metadata:
13
+ author: smbcloudXYZ
14
+ version: "1.0"
15
+---
16
+
17
+# smbcloud-ascapi release runbook
18
+
19
+One Rust workspace, one version. The root `Cargo.toml` `[workspace.package]`
20
+`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.
23
+
24
+## Facts that are easy to get wrong
25
+
26
+- **Forge is GitHub** (`github.com/smbcloudXYZ`). The `mcp__sigit__*` tools do
27
+ NOT apply here. Use `git` / `gh` via the shell for any forge work.
28
+- **The remote is named `setoelkahfi`, not `origin`.** Push with
29
+ `git push setoelkahfi main` and `git push setoelkahfi --tags`. Confirm with
30
+ `git remote -v` before pushing.
31
+- **Signing identity** (confirmed, and already the repo's local git config):
32
+ - `user.email = 1797197+setoelkahfi@users.noreply.github.com`
33
+ - `user.signingkey = 2B76DF0F04014E36`
34
+ - A signed Release commit shows `sig=G key=2B76DF0F04014E36` under
35
+ `git log -1 --format='sig=%G? key=%GK'`. If it doesn't, stop and fix the
36
+ identity before tagging — do not release unsigned or under the wrong key.
37
+ (The `seto@smbcloud.xyz` / `B5690EEE…` key is a different, non-release
38
+ identity — don't use it here.)
39
+- **Release tool is `cargo-workspaces`** (binary `cargo workspaces`). Install
40
+ with `cargo install cargo-workspaces` if missing.
41
+
42
+## Step 1 — merge `development` into `main`
43
+
44
+```sh
45
+git checkout main
46
+git pull setoelkahfi main
47
+git merge development
48
+```
49
+
50
+The only conflict you're likely to hit is `Cargo.lock`, and it's almost always
51
+just package-entry **ordering** (new crates shift lines around), not a real
52
+dependency divergence. Resolve by taking development's lockfile, then prove it:
53
+
54
+```sh
55
+git checkout development -- Cargo.lock # if the conflict is only ordering
56
+git add Cargo.lock
57
+git commit # finish the merge
58
+cargo check --workspace --locked # MUST pass — this validates Cargo.lock
59
+```
60
+
61
+If `--locked` fails, the lock really did diverge; regenerate with
62
+`cargo check --workspace` and commit the result instead.
63
+
64
+## Step 2 — bump the version and tag
65
+
66
+Pick `patch` / `minor` / `major`. This repo keeps the Release commit body as the
67
+plain cargo-workspaces default (`Release X.Y.Z` subject, per-crate lines, and a
68
+`Generated by cargo-workspaces` footer) — don't hand-edit it.
69
+
70
+```sh
71
+cargo workspaces version minor \
72
+ --yes --allow-branch main --no-git-push --force '*'
73
+```
74
+
75
+- `--allow-branch main` because releases go out on `main`.
76
+- `--no-git-push` so you control the push (to the `setoelkahfi` remote).
77
+- `--force '*'` bumps every crate to the shared version even if cargo-workspaces
78
+ thinks some are unchanged (single-version workspace).
79
+
80
+This creates the `Release X.Y.Z` commit, the `vX.Y.Z` tag, and **8 per-crate
81
+tags** `smbcloud-ascapi-{aso,cli,core,frontend,mcp,pricing,reports,signing}@X.Y.Z`,
82
+all pointing at the Release commit.
83
+
84
+## Step 3 — sync `server.json`
85
+
86
+`server.json` is NOT touched by cargo-workspaces. The MCP Registry workflow
87
+rewrites it from the tag at publish time, but keep the repo honest by bumping it
88
+in the Release commit anyway. Two version fields:
89
+
90
+- top-level `.version`
91
+- `.packages[0].version`
92
+
93
+Edit both to the new version, then fold them into the Release commit and move
94
+every tag to the new commit:
95
+
96
+```sh
97
+git add server.json
98
+git commit --amend --no-edit
99
+NEW=$(git rev-parse HEAD)
100
+for t in \
101
+ smbcloud-ascapi-aso@X.Y.Z smbcloud-ascapi-cli@X.Y.Z \
102
+ smbcloud-ascapi-core@X.Y.Z smbcloud-ascapi-frontend@X.Y.Z \
103
+ smbcloud-ascapi-mcp@X.Y.Z smbcloud-ascapi-pricing@X.Y.Z \
104
+ smbcloud-ascapi-reports@X.Y.Z smbcloud-ascapi-signing@X.Y.Z \
105
+ vX.Y.Z; do
106
+ git tag -f "$t" "$NEW"
107
+done
108
+```
109
+
110
+Beware: any tooling that **amends a co-author trailer** after your commit
111
+changes the hash again. After all amends settle, re-point the tags to the final
112
+HEAD and verify they all land on one commit:
113
+
114
+```sh
115
+git tag --points-at HEAD # must list all 9 tags
116
+git log -1 --format='sig=%G? key=%GK' # must be sig=G key=2B76DF0F04014E36
117
+```
118
+
119
+## Step 4 — final consistency check, then push
120
+
121
+All three must read the same version, and the locked build must pass:
122
+
123
+```sh
124
+grep -n '"version"' server.json # both -> X.Y.Z
125
+sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1 # -> X.Y.Z
126
+git status --short # clean
127
+cargo check --workspace --locked
128
+git push setoelkahfi main
129
+git push setoelkahfi --tags --force # --force only if you moved tags in step 3
130
+```
131
+
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:
136
+
137
+```
138
+core → aso / pricing / signing / reports → frontend → mcp → cli
139
+```
140
+
141
+```sh
142
+cargo workspaces publish --from-git --allow-branch main --yes
143
+```
144
+
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.
148
+
149
+## Step 6 — publish the MCP server
150
+
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:
154
+
155
+1. **Version match:** root `Cargo.toml` workspace version == tag (minus the `v`).
156
+ Step 2 guarantees this.
157
+2. **Ownership marker in source:** `mcp-name: io.github.smbcloudXYZ/ascapi` must
158
+ be present in BOTH `README.md` and `crates/cli/README.md`. (The server name
159
+ comes from `server.json` `.name`.)
160
+3. **Ownership marker in the published crate:** the CLI crate downloaded from
161
+ crates.io for this exact version must contain the same marker in its README.
162
+ 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.
166
+
167
+The workflow rewrites `server.json`'s version from the tag, publishes with
168
+`mcp-publisher` (treating "duplicate version" as success), and verifies the
169
+server is listed in the registry.
170
+
171
+## Release-order summary
172
+
173
+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.
176
+
177
+## References
178
+
179
+- `../../../.github/workflows/release-mcp-registry.yml` — the registry workflow
180
+ and its invariant checks, in full.
181
+- Past release design notes live in `.agents/plans/` (upload / signing-identity
182
+ behaviour) — check there before reworking release mechanics.