docs/guides/release-koment.md
1
# Cutting a release
3
**This procedure is mandatory. Follow it in order. Do not improvise a release.**
5
A release publishes signed artifacts to GitHub, GHCR, the MCP Registry, the VS
6
Code Marketplace and Open VSX. Marketplace versions are permanent: a version
7
number cannot be reused, replaced or withdrawn. A mistake here is not
8
recoverable by editing a file, so every step below exists because skipping it
9
produces something a user installs and cannot undo.
11
Agents may prepare steps 0–3, perform the read-only verification in step 6 and
12
prepare the development-pin change in step 8. **Steps 4, 5 and 7 change the
13
outside world and require explicit human approval in the conversation.**
15
---
17
## 0. Preconditions
19
Verify all of these before starting. If any fails, stop and report it.
21
Before a release that promises Zed registry support, verify that
22
`integrations/editors/zed/LICENSE` is the GPLv3 text and that the crate declares
23
`GPL-3.0-or-later`. Zed requires an accepted license inside an extension
24
subdirectory; `mise run layout-check` enforces both files as part of ADR 0145.
26
```sh
27
mise run fmt-check && mise run vet && mise run tidy-check && mise run generate-check
28
mise run lint && mise run test
29
mise run annotations && mise run comments && mise run agent-policy
30
mise run workflow-lint && mise run release-helper-test
31
mise run helm-lint && mise run helm-template && mise run vulncheck
32
mise run extension-test && mise run layout-check
33
mise --cd integrations/editors/zed run check
34
mise --cd integrations/editors/zed run build
35
```
37
All eighteen must pass. `koment check` failing means a release would ship
38
annotations that no longer describe the code.
40
The read-only publication verification in step 6 also needs authenticated
41
`gh`, a current supported `cosign`, `curl`, `docker` with Buildx, `helm`, `npm`
42
and `sha256sum` on `PATH`:
44
```sh
45
for command in gh cosign curl docker helm npm sha256sum; do command -v "$command"; done
46
gh auth status
47
```
49
`main` is protected by a ruleset — pull request required, signed commits,
50
linear history, and one required status check: **`ci`**. That is the
51
aggregating job in `.github/workflows/ci.yml`; it depends on every gating job,
52
so adding a gating job to CI does not require a ruleset edit. The setup-action
53
and Windows archive jobs are advisory because they test the last published
54
release rather than the pull request. `cla`, `codeql` and `scorecard` also
55
report separately, and `cla` cannot be required because a release pull request
56
opened by `GITHUB_TOKEN` never gets a `cla` run. The classic
57
branch-protection API returns 404 for this repository; that means the rules
58
live in a ruleset, not that the branch is unprotected. Check with:
60
```sh
61
gh api repos/koment-dev/koment/rulesets
62
```
64
## 1. Land the work
66
Merge every change through a pull request with a conventional subject. The
67
subject decides the version, so it is a release decision, not a formatting one:
69
| Subject | Effect |
70
|---|---|
71
| `feat:` | minor bump — 1.0.0 → 1.1.0 |
72
| `fix:`, `perf:`, `refactor:` | patch bump |
73
| `docs:`, `test:`, `build:`, `ci:` | patch bump, listed in the changelog |
74
| `chore:` | no release on its own |
75
| any `!` or `BREAKING CHANGE:` | major bump — 1.4.2 → 2.0.0 |
77
`bump-minor-pre-major` was turned off when 1.0.0 shipped (ADR 0120), so a `!`
78
is a major version and not a quiet minor one. Before writing `!`, check whether
79
the change is breaking at all: a claim of backward compatibility needs a
80
migration the binary performs or an ADR naming the version the old shape was
81
cut off at. Without either, it is breaking, and the subject has to say so.
83
## 2. Let release-please open the release pull request
85
Pushing to `main` runs the `release` workflow, whose first job opens or updates
86
a pull request titled `chore(release): <version>`. It edits the changelog, the
87
manifest, and every file that carries the version:
89
- `.release-please-manifest.json`
90
- `distribution/helm/koment/Chart.yaml`
91
- `integrations/editors/vscode/package.json`
92
- `integrations/editors/vscode/package-lock.json`
93
- `integrations/editors/zed/extension.toml`
94
- `integrations/agent-plugins/claude/.claude-plugin/plugin.json`
95
- `integrations/agent-plugins/hermes/plugin.yaml`
96
- `integrations/agent-plugins/opencode/plugin.json`
97
- `integrations/agent-plugins/opencode/package.json`
98
- `server.json` — the top-level `.version`; OCI packages are versioned by tag
100
Do not edit these by hand and do not bump a version in a feature branch.
101
The package-manager parity tests fail the build when they disagree.
103
## 3. Unblock that pull request's checks
105
**Expect its CI to sit at `action_required` with a 0s duration.** GitHub does
106
not run workflows for events created by `GITHUB_TOKEN`, so the required checks
107
never start and the pull request cannot merge on its own. This is normal and is
108
not a failure.
110
```sh
111
gh run list --branch release-please--branches--main --limit 5
112
gh api -X POST repos/koment-dev/koment/actions/runs/<run-id>/approve
113
```
115
Then wait for `commit-lint`, `container`, `editor`, `helm`, `lint`, `plugins`,
116
`quality`, `test` and `zed` to pass and for the aggregate `ci` job to pass.
117
Never merge a release pull request whose checks did not run—an unapproved run
118
is not a passing run. Quote `gh pr checks <number>` before asking to merge.
120
## 4. Merge the release pull request — human approval required
122
Merging tags the release and starts publication. Everything after this point is
123
public and permanent.
125
Before merging, confirm:
127
- the version in the title is the one you intend;
128
- the changelog describes real changes;
129
- `ci` is green and not skipped, and every job it aggregates ran.
131
## 5. Watch publication — human approval required to retry anything
133
Merging runs the rest of the `release` workflow in this order. The order is a
134
decision, not an accident (ADR 0109): canonical artifacts first, downstream
135
channels second.
137
```
138
please ──┬─> binaries ──┬─> editor
139
│ ├─> tap
140
│ └─> verify ──> alias
141
└─> image
143
binaries + image ──┬─> plugins
144
├─> mcp-registry
145
└─> chart
146
```
148
| Job | Publishes |
149
|---|---|
150
| `binaries` | six archives, `koment_<version>_checksums.txt`, a cosign signature, and rendered Homebrew/Scoop/WinGet metadata |
151
| `plugins` | Four self-contained plugin archives (Claude, Codex, Hermes and OpenCode), per-archive cosign signatures, a combined `koment-plugins_<version>_checksums.txt`, and its cosign signature; the OpenCode package also goes to npm |
152
| `image` | `ghcr.io/koment-dev/koment:<version>`, multi-arch, SBOM and provenance, cosign-signed |
153
| `editor` | seven VSIX — six carrying that platform's released binary, one universal — signed, attached, then pushed to both marketplaces |
154
| `tap` | the rendered formula in `koment-dev/homebrew-tap` after the binary assets exist |
155
| `verify` | installs the new GitHub release through this repository's setup Action on Linux and macOS |
156
| `alias` | moves the floating major tag only after both setup-Action verification jobs pass |
157
| — | the Zed extension is **not** built or attached by the release. Zed builds it from the submodule, so step 7 publishes it by hand |
158
| `mcp-registry` | MCP Registry metadata via GitHub OIDC |
159
| `chart` | `oci://ghcr.io/koment-dev/charts/koment`, cosign-signed |
161
`plugins` waits for both `binaries` and `image`. Its source archives do not
162
consume either artifact, but ADRs 0109 and 0129 require canonical artifacts to
163
exist before a downstream npm package or agent integration is published.
164
The chart and MCP Registry metadata use the same gate so a missing GitHub
165
release asset cannot leave them as the only completed distribution channels.
167
```sh
168
gh run watch "$(gh run list --workflow=release --limit 1 --json databaseId --jq '.[0].databaseId')"
169
```
171
If `binaries` fails, `editor` does not run. That is deliberate: the extension
172
bundles the released binary, so an extension built without one would ship
173
something that was never signed (ADR 0113).
175
## 6. Verify the release, do not assume it
177
```sh
178
tag=v<version>
179
gh release view "$tag" --json assets --jq '.assets[].name' | sort
180
curl -fsSLI -o /dev/null -w '%{http_code}\n' "https://open-vsx.org/api/koment/koment-dev"
181
curl -fsSLI -o /dev/null -w '%{http_code}\n' "https://marketplace.visualstudio.com/items?itemName=koment.koment-dev"
182
```
184
Expect six platform archives, the WinGet submission bundle, the binary checksum
185
manifest and its signature, rendered Homebrew and Scoop metadata, three plugin
186
archives and their three signatures, the plugin checksum manifest and its
187
signature, the chart and its signature, and seven VSIX files with seven
188
signatures. A release missing the archives breaks
189
every workflow using `koment-dev/koment@v<version>`, because the setup action
190
downloads them.
192
Download and verify the immutable assets rather than relying on their names:
194
```sh
195
mkdir "koment-release-<version>"
196
cd "koment-release-<version>"
197
gh release download "$tag" --repo koment-dev/koment
198
sha256sum --check "koment_<version>_checksums.txt"
199
sha256sum --check "koment-plugins_<version>_checksums.txt"
200
certificate_identity="https://github.com/koment-dev/koment/.github/workflows/release.yml@refs/heads/main"
201
certificate_issuer="https://token.actions.githubusercontent.com"
202
cosign verify-blob \
203
--bundle "koment_<version>_checksums.sigstore.json" \
204
--certificate-identity "$certificate_identity" \
205
--certificate-oidc-issuer "$certificate_issuer" \
206
"koment_<version>_checksums.txt"
207
cosign verify-blob \
208
--bundle "koment-plugins_<version>_checksums.sigstore.json" \
209
--certificate-identity "$certificate_identity" \
210
--certificate-oidc-issuer "$certificate_issuer" \
211
"koment-plugins_<version>_checksums.txt"
212
for artifact in koment-plugin-*.tar.gz koment-vscode_*.vsix koment-<version>.tgz; do
213
cosign verify-blob \
214
--bundle "${artifact}.sigstore.json" \
215
--certificate-identity "$certificate_identity" \
216
--certificate-oidc-issuer "$certificate_issuer" \
217
"$artifact"
218
done
219
npm view "@koment/opencode-koment@<version>" version
220
cosign verify \
221
--certificate-identity "$certificate_identity" \
222
--certificate-oidc-issuer "$certificate_issuer" \
223
"ghcr.io/koment-dev/koment:<version>"
224
cosign verify \
225
--certificate-identity "$certificate_identity" \
226
--certificate-oidc-issuer "$certificate_issuer" \
227
"ghcr.io/koment-dev/charts/koment:<version>"
228
docker buildx imagetools inspect "ghcr.io/koment-dev/koment:<version>"
229
helm show chart "oci://ghcr.io/koment-dev/charts/koment" --version "<version>"
230
curl -fsSL "https://raw.githubusercontent.com/koment-dev/homebrew-tap/main/Formula/koment.rb" \
231
| grep -F 'version "<version>"'
232
```
234
Run these in a new temporary directory and remove it after recording the real
235
output. Do not run `gh release download` into the repository checkout.
237
## 7. Publish the Zed extension — human approval required
239
Zed's registry is not driven by this repository's release workflow. It builds the
240
extension from a git submodule, so publishing is a pull request in another
241
organisation's repository and a person opens it (ADR 0139).
243
Do not start this step until the GPLv3 boundary required by step 0 passes the
244
local layout check.
246
Nothing else in this procedure waits on it, and the release is not fully
247
published until it merges.
249
1. Fork or update your fork of `zed-industries/extensions`.
250
2. Point the `koment` submodule at the release tag, and set the version in the
251
top-level `extensions.toml` to the version just released:
253
```toml
254
[koment]
255
submodule = "extensions/koment"
256
path = "integrations/editors/zed"
257
version = "<version>"
258
```
260
3. Run `pnpm sort-extensions` so `extensions.toml` and `.gitmodules` stay
261
sorted. A pull request that skips this is rejected.
262
4. Open the pull request. The submodule commit must be reachable on a branch,
263
not detached, and the submodule URL must be HTTPS rather than SSH.
265
On the first publication the entry and the submodule are both new; afterwards
266
only the submodule commit and the `version` field change.
268
Verify after it merges:
270
```sh
271
curl -fsSLI -o /dev/null -w '%{http_code}\n' "https://zed.dev/extensions/koment"
272
```
274
## 8. Bump the development pin
276
`.mise/config.toml` pins `github:koment-dev/koment` to a released version, which is
277
the `koment` a contributor gets in their shell. It is not what any gate runs —
278
every `mise run` task uses `go run ./cmd/koment` — so it lags a release rather
279
than blocking one. It still has to be caught up, because a pinned binary older
280
than the record shape in `.koment/` cannot read this repository at all.
282
```sh
283
mise use "github:koment-dev/koment@<version>"
284
mise run annotations
285
```
287
Land it as `chore:`, which release-please does not turn into a release of its
288
own.
290
---
292
## When something goes wrong
294
**Never delete a tag or a published release to "redo" it.** Republish nothing.
295
Cut the next patch version instead. A tag that once existed has been fetched by
296
someone.
298
| Symptom | Cause | Action |
299
|---|---|---|
300
| release pull request checks show `action_required`, 0s | `GITHUB_TOKEN` created the pull request | approve the run (step 3) |
301
| release asset upload returns an HTTP error | the create-release response or publishing token cannot upload to its exact asset endpoint | do not rerun a published registry version; inspect the response, fix the cause, and cut the next patch |
302
| `editor` job skipped | `binaries` failed | fix the binaries, cut a new patch version |
303
| `ovsx publish` fails on the first ever publish | the namespace did not exist | the workflow now creates it; if it still fails, the token lacks the Publisher Agreement |
304
| `vsce publish` rejects the version | that version already exists on the marketplace | cut the next version, never reuse one |
305
| a marketplace publish times out after earlier packages succeeded | the service response is ambiguous and the version may be partial | do not rerun a historical workflow or publish by hand; current releases retry each package three times with `--skip-duplicate`, and an exhausted or older run recovers in the next patch (ADR 0150) |
306
| `Windows Archive (advisory)` is red | advisory by decision (ADR 0111) | it does not block; read it and file a task |
307
| version files disagree | someone hand-edited one | revert the edit, let release-please own them |
309
## What an agent must never do
311
- Publish a VSIX, image, chart or binary by hand, outside this workflow.
312
ADR 0112 rejected out-of-band publishing: a marketplace would carry a version
313
the release never produced.
314
- Hand-edit any version-bearing file listed in step 2.
315
- Merge a release pull request whose checks did not run.
316
- Delete, move or re-point a tag, or force-push `main`.
317
- Re-run a publish job hoping it works the second time, without first
318
establishing why it failed.
319
- Claim a release succeeded without running step 6 and quoting its output.