# Release Workflow

The `release` skill accumulates a running log of user-impact changes and pending doc page updates between releases of your site, then publishes them atomically when you ship. It's the end-of-session ritual that keeps your changelog page accurate without forcing you to remember what you did three weeks ago.

The standard flow is: do work → run `/release` at the end of the session → repeat over many sessions → run `/release finalize` when you're ready to ship.

## How does it work?

Each `/release` run scans the work you just did, drafts changelog bullets in your changelog's voice, stages any doc page updates the work warrants, and writes everything to a staging area inside your project. Nothing goes live until you run `/release finalize`, which prepends the accumulated bullets to your changelog page, copies staged docs into `pages/docs/`, and deploys.

Two principles drive the design:

1. **Logs accumulate; they never get reconstructed.** Reconstructing release notes from `git log` at ship time loses the user-impact framing your changelog actually uses. Logging at the end of each session, while context is fresh, produces better notes.
2. **Docs and changelog ship together.** A new feature isn't really shipped until its docs page exists. Staging doc updates alongside changelog entries forces them to land in the same deploy.

## Where is staging stored?

All staging state lives at `sitemd/staging/release/` inside your project root — agent-agnostic, visible to any coding agent without hiding inside `.claude/` or `.agents/`.

```
sitemd/staging/release/
  UNRELEASED.md          # running log of bullets for the next release
  docs-staged/           # mirrors pages/docs/ — pending doc page updates
```

`UNRELEASED.md` looks like this:

```markdown
# Unreleased — targeting v0.1.1

Base: v0.1.0 (commit abc1234, 2026-04-03)

### Added
- new feature description in user-impact voice
  Docs: docs/new-feature.md

### Fixed
- bug description

---
Last synced: <commit-sha> <iso-timestamp>
```

The `Last synced:` marker tracks the most recent commit the skill has already processed, so back-to-back `/release` runs don't double-log.

Section names mirror whatever style your changelog page already uses. If you don't have a changelog page yet, the skill falls back to Keep a Changelog conventions: `Added`, `Changed`, `Fixed`, `Removed`.

## What does each subcommand do?

### `/release`

The end-of-session ritual. Scans your session work plus any commits since the last sync, drafts changelog bullets in user-impact voice, invokes the `write` skill in staged mode to draft doc page updates, presents a full proposal, and on confirmation appends everything to staging.

This is the command you run almost every time. Internal-only changes — refactors, formatting, test edits, build tooling — get filtered out automatically.

### `/release add <description>`

Append a single bullet manually, bypassing the session scan. Useful when you want to log something in your own words. The skill rewrites it in changelog voice and infers the right section.

### `/release sync`

Same as the default but ignores the `Last synced:` marker and rebuilds proposals from `git log` since the base version. Recovery path for when work happened without running `/release`.

### `/release bump [patch|minor|major]`

Update the target version in the `UNRELEASED.md` header. Defaults to `patch`. Doesn't touch `package.json` — version metadata bumps belong at ship time, not during release planning.

### `/release status`

Read-only print of the current `UNRELEASED.md` plus a summary: target version, bullets per section, staged doc files, commits since base.

### `/release docs-list`

Show what's currently in `sitemd/staging/release/docs-staged/`.

### `/release docs-discard <path>`

Remove a single staged doc file without applying it. Path is relative to `docs-staged/`.

### `/release finalize`

When you're ready to ship the next release:

1. Formats `UNRELEASED.md` into a new `## vX.Y.Z — <date>` section.
2. Prepends it to your changelog page.
3. Copies every file in `docs-staged/` to its matching path under `pages/docs/`.
4. For each new doc, inserts a sidebar nav entry into `settings/groups.md` via the `sitemd_groups_add_pages` MCP tool, using the planned sub-anchor list staged earlier in `UNRELEASED.md`'s `Pending nav additions:` block. Runs after the doc copy because validation rejects nav entries pointing at pages that don't yet exist.
5. Resets `UNRELEASED.md` to a fresh template seeded with the new base.
6. Invokes the `deploy` skill so changelog, docs, and nav additions go live in a single deploy.

Does not commit, push, or modify `package.json`.

## Voice rules

The skill writes bullets in **user-impact voice**: present tense, lowercase first word, no trailing period. Lead with what changed for the reader, not implementation details. One bullet per user-visible change, not one per commit.

Internal-only changes are skipped. If you try to log one with `/release add`, the skill pushes back rather than logging it.

## How does the skill find my changelog page?

Path discovery is automatic:

| What | How |
| --- | --- |
| Changelog page | Looks for any page in `pages/` with `slug: /changelog` in its frontmatter. Falls back to `pages/changelog.md`. If neither exists, asks you where to put it and offers to create one. |
| Docs section | Checks whether `pages/docs/` exists. If not, asks you where docs live or skips docs staging entirely. |
| Current version | Reads the latest `## vX.Y.Z` heading from the changelog page. Defaults to `v0.0.0` if the changelog has no entries yet. |

Different projects use different conventions, so the skill never hardcodes paths.

## Related

- [Agent Skills](/docs/skills) — full reference for every skill that ships with sitemd
- [Content Generation](/docs/content-generation) — deep dive on `/write`, which the release skill invokes in staged mode for docs updates
- [Deploy](/docs/deploy) — how `/deploy` publishes your site to its hosting target