@ondeinference / ed / commits / e69f8a8

chore: add release workflow skill

Co-Authored-By: OndeCode v1.0.1-acp <noreply@ondeinference.com>

Seto Elkahfi committed Oct 10, 2026 at 15:01 UTC e69f8a8abab7d798c1c64009cd64991ccc5b8615
1 file changed +173
.agents/skills/release/SKILL.md new
+173
@@ -0,0 +1,173 @@
1 +---
2 +name: release
3 +description: Cut a release of the onde-ed Rust workspace. Covers version bumping, the release branch/PR flow, tagging, and the two tag-triggered GitHub Actions (crates.io publish and Swift XCFramework). Use when asked to create, cut, or publish a release.
4 +allowed-tools: Read, Write, Edit, Glob, Grep, Bash
5 +user-invocable: true
6 +---
7 +
8 +# Skill: Release
9 +
10 +## What This Skill Covers
11 +
12 +How to cut a release of the `onde-ed` Cargo workspace: bumping the version,
13 +landing it through the branch/PR flow, tagging, and what the two tag-triggered
14 +workflows do automatically afterwards.
15 +
16 +## The one-number rule
17 +
18 +The whole workspace releases on **one version**. Every crate declares
19 +`version.workspace = true`, so a release changes exactly one source of truth:
20 +the root `Cargo.toml`.
21 +
22 +```toml
23 +[workspace.package]
24 +version = "X.Y.Z"
25 +```
26 +
27 +Plus the internal path dependencies in the same file, which pin the version
28 +explicitly:
29 +
30 +```toml
31 +ed-agent = { version = "X.Y.Z", path = "crates/ed-agent" }
32 +ed-mcp = { version = "X.Y.Z", path = "crates/ed-mcp" }
33 +ed-acp = { version = "X.Y.Z", path = "crates/ed-acp" }
34 +ed-acp-tui = { version = "X.Y.Z", path = "crates/ed-acp-tui" }
35 +ed-acp-testkit = { version = "X.Y.Z", path = "crates/ed-acp-testkit" }
36 +ed-onde-account = { version = "X.Y.Z", path = "crates/ed-onde-account" }
37 +```
38 +
39 +Bump all of these together. The crate manifests under `crates/*/Cargo.toml`
40 +need no edits.
41 +
42 +## Versioning
43 +
44 +Semver. Given the current `X.Y.Z`:
45 +- **patch** (`Z+1`) — bug fixes, internal changes, no API additions.
46 +- **minor** (`Y+1`, `Z=0`) — backwards-compatible API additions.
47 +- **major** (`X+1`, `Y=Z=0`) — breaking changes.
48 +
49 +## Step-by-step
50 +
51 +The base branch is **`development`**; `main` is the promoted/stable branch.
52 +Releases land on `development` first, get tagged, then `development` is promoted
53 +to `main`.
54 +
55 +### 1. Branch off `development`
56 +
57 +```sh
58 +git checkout development && git pull
59 +git checkout -b release/X.Y.Z
60 +```
61 +
62 +### 2. Bump the version
63 +
64 +Edit the root `Cargo.toml` (`[workspace.package] version` and the internal
65 +`ed-*` path-dependency versions). A quick way, run from the repo root:
66 +
67 +```sh
68 +sed -i '' 's/OLD\.VER\.SION/X.Y.Z/g' Cargo.toml
69 +```
70 +
71 +### 3. Refresh the lockfile and verify
72 +
73 +```sh
74 +cargo check --workspace # updates Cargo.lock, catches breakage
75 +```
76 +
77 +`cargo fmt --all -- --check`, `cargo test --locked --workspace`, and
78 +`cargo clippy --locked --workspace --all-targets -- -D warnings` must all pass —
79 +the release workflow re-runs them and fails the publish otherwise.
80 +
81 +### 4. Commit and push
82 +
83 +```sh
84 +git add Cargo.toml Cargo.lock
85 +git commit -m "Release X.Y.Z: bump the workspace"
86 +git push
87 +```
88 +
89 +Commit subject convention: `Release X.Y.Z: bump the workspace`.
90 +
91 +### 5. Open the release PR → `development`
92 +
93 +```sh
94 +gh pr create --base development --head release/X.Y.Z \
95 + --title "Release X.Y.Z" --body "..."
96 +gh pr edit <n> --add-assignee <you> --add-reviewer <reviewer>
97 +```
98 +
99 +### 6. Merge, then tag the merge commit
100 +
101 +After approval, merge the PR, update local `development`, and tag **the merge
102 +commit** (this is what every historical release tag points at):
103 +
104 +```sh
105 +gh pr merge <n> --merge
106 +git checkout development && git pull
107 +git tag vX.Y.Z <merge-sha>
108 +git push origin vX.Y.Z
109 +```
110 +
111 +> The tag name carries a `v` prefix (`vX.Y.Z`); the workspace version does not
112 +> (`X.Y.Z`). The workflows strip the `v` when comparing, so they must match on
113 +> the numeric part.
114 +
115 +### 7. Promote `development` → `main`
116 +
117 +Open a second PR to bring `main` up to the tagged state:
118 +
119 +```sh
120 +gh pr create --base main --head development --title "Merge development → main (vX.Y.Z)"
121 +```
122 +
123 +Merging it runs `ci.yml` on `main` as a final green check.
124 +
125 +## What the tag triggers
126 +
127 +Pushing a `vX.Y.Z` tag fires **two** workflows. Both start with a `preflight`
128 +job that validates the tag matches the `ed-agent` version in `Cargo.toml`, then
129 +runs fmt + test + clippy. If the version and tag disagree, the release fails
130 +here — this is the safety net for step 6.
131 +
132 +### `release-sdk-rust.yml` → crates.io
133 +
134 +- Dry-runs the workspace publish with `cargo-workspaces` (plain
135 + `cargo publish --dry-run` can't resolve the inter-crate path deps post-package).
136 +- Publishes crates in dependency order to crates.io.
137 +- `cargo workspaces plan --skip-published` makes re-runs safe: a run that dies
138 + mid-publish only publishes the crates still missing from the registry.
139 +
140 +### `release-sdk-swift.yml` → XCFramework + `ed-swift`
141 +
142 +- Builds the `EdFramework.xcframework` for Apple targets (~2h20m build;
143 + **not** cancel-in-progress — killing it leaves half-published state).
144 +- Creates a GitHub Release with the xcframework zip + checksum as assets.
145 +- Checks out `ondeinference/ed-swift`, rewrites `Package.swift` with the new
146 + release URL + checksum, copies the generated `ed_agent_ffi.swift`, commits,
147 + pushes to `ed-swift` `main`, and tags it `vX.Y.Z`.
148 +
149 +## Manual / recovery runs
150 +
151 +Both workflows have a `workflow_dispatch` with a `publish` boolean and a
152 +`release_tag` input. Use this to re-publish an existing tag if a run failed
153 +after tagging — e.g. the crates.io publish succeeded but the Swift build timed
154 +out. Point it at the existing `vX.Y.Z` tag; the crates.io re-run skips anything
155 +already on the registry.
156 +
157 +## Toolchain
158 +
159 +The Rust toolchain is pinned in `rust-toolchain.toml` (currently `1.94.0`).
160 +Workflows read `channel` from it, so a toolchain bump is a normal source change,
161 +not release-time config.
162 +
163 +## Checklist
164 +
165 +- [ ] On a `release/X.Y.Z` branch off `development`.
166 +- [ ] Root `Cargo.toml`: `[workspace.package] version` + all `ed-*` path deps bumped.
167 +- [ ] `cargo check --workspace` run (Cargo.lock updated).
168 +- [ ] fmt / test / clippy all green locally.
169 +- [ ] Commit `Release X.Y.Z: bump the workspace`.
170 +- [ ] PR → `development`, reviewed, merged.
171 +- [ ] `vX.Y.Z` tag on the merge commit, pushed.
172 +- [ ] `development` → `main` PR opened/merged.
173 +- [ ] Both tag workflows green (crates.io + Swift).