kolu Release Runbook
Cut a release with `/release X.Y.Z` — tag master, promote the Unreleased changelog entry, GitHub release. The changelog is a website content collection the agent appends to per PR; kolu.dev renders it Unreleased-and-all.
Model — tag-on-master, rolling:
- Ships only as a Nix flake; users track
master(nix run github:juspay/kolu). - A release is a tag on master — a named point, a pin, a dated changelog entry. Nothing is published that master doesn’t already serve.
- Version is picked by hand (it’s an app, no API contract):
.0= milestone, a normal bump otherwise. Single source of truth:packages/server/package.jsonversion(valid semver) — Nix and the server runtime both read it, so there’s nothing else to bump.
The changelog is website content
- A content collection beside the blog (blog collection — same shape), not a root
CHANGELOG.md. - One entry per release;
unreleased.mdxis the open one. - Authored per PR by
/be— its implement step appends one line under the right###heading (prose a user reads + a markdown PR link). No commit-prefix, no bump field. ### Heads-up= disruptive changes (removed feature, changed default, migration), written for the user.- Conflict-free —
unreleased.mdx merge=unionin.gitattributesauto-resolves concurrent appends.
website/src/content/changelog/
unreleased.mdx ← agent appends here, every user-facing PR
1-0-0.mdx ← stamped { version: "1.0.0", date: 2026-06-08 } at release
---
version: Unreleased
---
### Added
- Compose surfaces as siblings — one canvas, no surface-app seams. ([#1201](https://github.com/juspay/kolu/pull/1201))
### Fixed
- Dock pings stop pulsing once you switch to the row. ([#1198](https://github.com/juspay/kolu/pull/1198))
### Heads-up
- `KOLU_STATE_DIR` moved under `$XDG_CONFIG_HOME/kolu`; existing sessions auto-migrate.
Keeping it filled — two checkpoints, no CI gate
- Write time —
/beauthors the entry in its implement phase (be §2 — Add a changelog entry); every PR rides through/be. - Publish time —
/release X.Y.Z’s go/no-go preview prints the exactUnreleasednotes about to publish and waits for confirmation; a thin section is visible right before it ships. - Enough for a single-maintainer,
/be-driven repo. Add a merge-blockingchangelogCI check (diff touchesunreleased.mdxorno-changeloglabel) only once PRs arrive from contributors who don’t run/be.
kolu.dev renders it — Unreleased included
- A
/changelogpage maps the collection newest-first,Unreleasedon top — shipped and upcoming, and a live preview the moment a PR merges. - The changelog is
website/**, so the existing Pages deploy already fires on every edit (pages.yml — on.push.paths website/**) — nothing extra to wire.
Cutting a release
Run /release X.Y.Z (the /release skill). It does the what below and asks (via AskUserQuestion) where a call is yours; everything is read-only until the explicit go/no-go.
Phases 1–3 are read-only; nothing is written before the go/no-go.
- Settle the version (editorial —
.0milestone, normal bump otherwise; valid semverX.Y.Z) and date. - Preflight — on
master, clean, synced, CI green onHEAD(the base the release commit builds on). Refuse otherwise. - Confirm — show the exact notes + version bump + tag, then go/no-go.
Noleaves the tree untouched. - Apply — promote
unreleased.mdx→<version>.mdx({ version, date }), consolidating any duplicate###headingsmerge=unionleft behind into one section per heading + fresh empty Unreleased; setpackages/server/package.jsonversion(the single source — Nix + runtime both read it; nothing else to bump); commit (release X.Y.Z) + pushmaster. No tag yet. - Wait for CI to go green on the pushed release commit — never tag a commit CI hasn’t passed.
- Tag & publish — annotated tag
vX.Y.Zon the green commit, push,gh release create. - Verify — tag on master, release live,
kolu.dev/changelogupdated; pin isnix run github:juspay/kolu/vX.Y.Z.
Wiring
changelogcollection — ✓ #1208 (schema,/changelogpage, seededunreleased.mdx,merge=union).- Keep-it-filled — ✓ the
/beimplement-step entry (.apm/skills/be/SKILL.md:36); no CI gate. - Identity rail — ✓ version sourced once from
packages/server/package.json, read at runtime viaserverVersion(serverVersion = pkg.version) and by Nix for the artifact version (default.nix:15-19). No env var, nothing to propagate.serverVersionis the single runtime owner — it feeds thebuildInfocell → rail (vX.Y.Z · <hash>),--version, the startup log, and the pty’sTERM_PROGRAM_VERSION. /releaseskill — ✓ .apm/skills/release/SKILL.md:1; the steps above.
Status: implemented. Surface + rail stamp + /release skill landed in
#1208 . First run done: /release 1.0.0 cut v1.0.0 on 2026-06-08 (tag v1.0.0, GitHub release, kolu.dev/changelog).