← Docs hub

Release process

Audience: whoever is cutting the next tag.

How to run the cut: load the cross-agent skill .claude/skills/release/SKILL.md (Claude Code / Cursor: /release <version>). The wrapper lives at .claude/commands/release.md, which both harnesses load. This document is the canonical checklist order; the skill is the operational walkthrough and must stay aligned with it.

llmwiki uses semantic versioning. Past 1.0 / 2.x, a normal vX.Y.Z tag is a full GitHub Release. Tags whose names contain rc, alpha, beta, or dev are marked prerelease by automation.

Minor bumps (X.Y.0) ship when a coherent feature batch lands. Patch bumps (X.Y.Z) ship when a fix cannot wait for the next minor.

Pre-flight

Bump version

The package version in llmwiki/__init__.py and pyproject.toml must match (test_pyproject_version_matches_package).

Update CHANGELOG and UPGRADING

Commit + tag (local only)

git add llmwiki/__init__.py pyproject.toml README.md CHANGELOG.md docs/UPGRADING.md
git commit -m "release(vX.Y.Z): bump version + CHANGELOG"
git tag vX.Y.Z

Human gate, then push

Direct push of the release commit to main is the maintainer path for a cut (distinct from normal PR flow). Still requires an explicit OK in the session.

GitHub Release + PyPI (automation)

Pushing the v*.*.* tag triggers .github/workflows/release.yml, which:

  1. Builds sdist + wheel
  2. Signs artifacts with Sigstore
  3. Creates (or updates) the GitHub Release with notes + artifacts — this is the happy path; do not run a second gh release create unless automation is broken
  4. Publishes to PyPI via OIDC only when repository variable PYPI_PUBLISHING is true (otherwise the publish job is skipped; the GitHub Release still ships)
  5. Runs a post-publish smoke job that installs llm-wiki-plus==X.Y.Z from real PyPI and asserts llmwiki --version matches the tag — it also fails the run outright when publish was skipped, so a gate-off tag can't pass quietly

Prerelease: the workflow passes --prerelease only when the tag name matches rc / alpha / beta / dev. Stable tags are full releases.

Manual fallback (only if release.yml is broken):

gh release create vX.Y.Z --title "vX.Y.Z" --generate-notes
# add --prerelease only for rc/alpha/beta/dev tags

Verify Pages deploy (expected, not optional)

The same tag push that triggers release.yml also triggers .github/workflows/pages.yml (#213). The demo site is part of what a release ships — treat a failed or missing Pages run the way you'd treat a failed PyPI upload, not as cosmetic. Skipping this check is how the live demo ended up four releases stale.

There is no separate scheduled freshness workflow — the post-deploy assert on pages.yml is the version gate. Session/docs corpus freshness is still a pre-tag maintainer step (#225; see pre-flight above and docs/uptime.md).

Announce (optional)

Rollback

If a release is broken, do not delete the tag. Do:

  1. Cut a patch release (vX.Y.Z+1) that reverts the bad change
  2. Mark the broken release superseded in the GitHub Release notes (use Pre-release only when appropriate)
  3. Never delete tags — downstream packages may pin to them
  4. Never force-push main to rewrite the cut

Pitfalls (from recent cuts)

Pitfall What to do
Leftover root wiki/ Warn / move aside with approval; breaks demo self-containment style checks
Shipping without regenerating demo sessions Default is refresh; skip only on an explicit human opt-out (#225). Pages version assert does not rewrite session dates
Emptying Unreleased Keep shipping bullets under the new version section; rely on shipping_section_text scanning versioned sections
Double-creating the GitHub Release Trust release.yml after the tag push
Always marking prerelease Only for rc/alpha/beta/dev tags — not every release past 1.0
Pushing without approval Human gate is mandatory; no unattended publish

Keyboard shortcuts

⌘K / Ctrl+KOpen command palette
/Focus search
g hGo to home
g pGo to projects
g sGo to sessions
j / kNext / prev row (tables)
?Show this help
EscClose dialogs

Structured queries

Mix key:value filters with free text in the palette:

type:sessionOnly session pages
project:llm-wikiFilter by project name (substring)
model:claudeFilter by model name (substring)
date:>2026-03-01Sessions after a date
date:<2026-04-01Sessions before a date
tags:rustPages mentioning a tag/topic
sort:dateSort results by date (newest first)

Example: type:session project:llm-wiki date:>2026-04 sort:date