Release Process¶
This document describes how JARVIS RD Assistant versions are tagged, how the CHANGELOG is generated, and how Docker images are versioned and published.
How We Tag¶
Releases follow Semantic Versioning: vMAJOR.MINOR.PATCH.
MAJOR— breaking API or schema changes that require manual operator steps.MINOR— new features; existing deployments can upgrade withjarvis-research update.PATCH— backwards-compatible fixes, security patches, release metadata updates, and maintainer-approved documentation or UX corrections that do not require API, schema, or operator migration changes.
To cut a release:
# 1. Start from the current remote main branch.
git switch main
git pull --ff-only
git switch -c chore/release-vX.Y.Z
# 2. Prepare and verify the release metadata on this branch.
git cliff --tag vX.Y.Z -o /tmp/jarvis-changelog-draft.md
make check
# 3. Commit the reviewed version, CHANGELOG, and roadmap changes, then open a
# pull request. Do not push a release commit directly to main.
git add CHANGELOG.md ROADMAP.md pyproject.toml frontend/package.json \
frontend/package-lock.json docker-compose.yml
git commit -m "chore(release): prepare vX.Y.Z"
git push -u origin chore/release-vX.Y.Z
Review the generated draft and incorporate the release notes into
CHANGELOG.md before the commit.
The release metadata may travel in the final feature pull request or in a small release-only pull request. In either case, review it and merge it before tagging. After the pull request is merged and required checks are green:
git switch main
git pull --ff-only
# The tag target must be the reviewed commit now reachable from origin/main.
git merge-base --is-ancestor HEAD origin/main
git tag -a vX.Y.Z -m "Release vX.Y.Z — <one-line summary>"
git push origin vX.Y.Z
Never point a stable tag at a local candidate commit, and never push a stable tag together with an unreviewed branch. The image workflow rejects a stable tag that is not main-reachable.
Pre-release Tags¶
The only supported pre-release tag shape is vX.Y.Z-rc.N. Tag the reviewed
candidate commit before it is squash-merged:
git cliff --tag vX.Y.Z-rc.N --pre-release -o /tmp/jarvis-changelog-draft.md
git tag -a vX.Y.Z-rc.N -m "Release vX.Y.Z-rc.N"
git push origin vX.Y.Z-rc.N
The candidate tag does not have to be reachable from main; the publishing
workflow applies the main-ancestry gate only to stable tags. Treat the candidate
tag as disposable. After the candidate changes are squash-merged, create the
stable tag separately: the stable tag must point to the reviewed commit on
main. See the release-candidate update note
for the corresponding operator behavior.
How CHANGELOG Is Generated¶
CHANGELOG.md is generated by git-cliff,
configured in cliff.toml at the repo root. The tool reads conventional-commit
messages and groups them into sections.
Commit-to-Section Mapping¶
The cliff.toml configuration maps conventional-commit prefixes to CHANGELOG sections:
| Commit prefix | CHANGELOG section |
|---|---|
feat |
Features |
fix |
Bug Fixes |
perf |
Performance |
refactor |
Refactoring |
security |
Security |
docs |
Documentation |
test |
Testing |
chore |
Miscellaneous Tasks |
Breaking changes (commits with BREAKING: in body) are highlighted in the output.
Merge commits and reverts are automatically filtered out.
Re-generating the Changelog¶
To regenerate or update CHANGELOG.md after a release, run:
The --tag flag is optional; if omitted, the tool generates entries for all
commits since the last Git tag.
Docker Image Versioning¶
Five images are published to GHCR on a release tag by .github/workflows/ghcr-publish.yml:
ghcr.io/limitcycle-oss/jarvis-{paper-ingestion,learning-engine,telegram-bot,dashboard,restore-uploader}.
Image tags drop the leading v (vX.Y.Z → :X.Y.Z), matching the ${JARVIS_VERSION} interpolation in
docker-compose.yml. paper-ingestion also publishes a :X.Y.Z-cuda flavor for NVIDIA hosts; the
default install selects it from the detected GPU. langfuse-hardened is not published — it is
observability-profile-only and built locally.
Because the images are published, rolling forward or back to a specific version is a registry pull,
not a rebuild. Pin JARVIS_VERSION and pull:
The build: blocks remain for contributors; ./setup.sh --build-local (and ./update.sh --build-local)
build from source instead of pulling.
Release Gate Map¶
Run every gate against the exact release-candidate ref and confirm the workflow run's commit before accepting its result.
| Class | Required path | Acceptance rule |
|---|---|---|
| Fast deterministic PR | Run make check locally. The pull request runs CI / Lint + fast test suite, the complete frontend job, and Docs / MkDocs build; release-metadata tests also lock the workflow wiring. |
Every job succeeds. The docs job runs in strict mode, where warnings fail the build. |
| GitHub-hosted integration | Require the CI aggregate's live PostgreSQL cross-user and contract jobs plus Security / npm-audit. Dispatch gh workflow run nightly-llm-smoke.yml --ref <candidate-branch> for its independent Corpus visibility (PostgreSQL + Qdrant) job. |
The CI gate and Security gate succeed. Each required JUnit selection contains at least one pass and no skips, failures, or errors; the frontend audit rejects high-severity advisories. |
| Isolated release | Dispatch gh workflow run lifecycle-smoke.yml --ref <candidate-branch> -f leg=all. |
The GitHub-hosted lifecycle run proves CA-verified HTTPS and the generated destructive restore round trip. Its restore leg rejects SKIP and removes only its owned fixture project. |
Do not move the Qdrant or destructive restore suites into fast PR CI, and never run a public-repository workflow on a self-hosted runner.
Release Checklist¶
Before tagging a release:
- [ ] All planned changes for the milestone have merged to
main. - [ ] Version bumped in lockstep:
pyproject.toml,frontend/package.json(andpackage-lock.json, vianpm version), thedocker-compose.ymlJARVIS_VERSIONfallback, and theCHANGELOG.mdheading + date. - [ ]
make checkpasses on the reviewed candidate. - [ ] Required CI, Security, and strict Docs workflow gates are green, including the high-severity npm audit and the Python and OSV dependency scans.
- [ ] The candidate's manually dispatched nightly Qdrant and lifecycle runs pass the acceptance rules above; no required selection is skipped or empty.
- [ ]
CHANGELOG.mdgenerated and reviewed. - [ ] Release metadata was reviewed in a pull request and the merge commit is on
origin/main. - [ ]
docs/known-residual-risks.mdupdated with any newly accepted risks. - [ ] Docker images built and smoke-tested against a fresh stack.
- [ ] All external service keys verified (LiteLLM, Langfuse, SMTP, Telegram).
- [ ] Annotated tag points to that main-reachable commit, then is pushed by itself.
Publishing a stable image is gated by two independent controls: a refs/tags/v* repository ruleset
(admin-only tag creation) and a release deployment environment on the publish job (the owner must
approve the run). Release-candidate tags (vX.Y.Z-rc.N) publish unattended for dry-runs; only a clean
vX.Y.Z tag enters the release environment.
After the final vX.Y.Z publish completes:
- [ ] Confirm the tag still resolves to the reviewed
maincommit. A squash-merged release candidate has a different SHA and must not be reused. - [ ] Run the anonymous cold-install smoke against the final tags (
gh workflow run first-run-smoke.ymlwith the release version) and confirm zero app-image builds and all mandatory services healthy. - [ ]
docker manifest inspectevery published image from an emptyDOCKER_CONFIG(DOCKER_CONFIG="$(mktemp -d)"), including the-cudaflavor, to prove they pull anonymously. - [ ] Announce the release only after both the cold-install smoke and the anonymous manifest inspection pass.
Rollback Procedures¶
Code Rollback¶
Database warning: Moving the source checkout back without a matching database restore can leave the schema ahead of the code. Coordinate a code rollback with a restore point, or use a forward-only corrective migration.
To revert to a previously published release after confirming the DB state is compatible, pin
JARVIS_VERSION to that tag and pull it back from the registry:
JARVIS_VERSION=<previous-version> docker compose pull
JARVIS_VERSION=<previous-version> docker compose up -d --no-build
Only tags already published to GHCR can be rolled back this way. If the target predates the published
images, or you run a --build-local install, check out the target tag's source and rebuild
(./update.sh --build-local) instead.
Database Rollback¶
Important: Database migrations are intentionally forward-only. There is no automated rollback mechanism. To revert a schema change:
- Restore from backup — take a restore point before upgrading; see
Backup and restore. The default setup encrypts
restore points through
BACKUP_ENCRYPT_KEYFILE. - Manually craft a "reverse" migration — if a change must be undone in-place, add a new migration that restores the old schema state (e.g., re-add a dropped column with default value).
Always review migration diffs before upgrading. Test migrations on a copy of production data before applying to the live database.