This document describes how releases are created for AIFactory.
There is one version scheme: semantic versioning MAJOR.MINOR.PATCH (3.x).
The same number must appear, in lockstep, in:
package.json,apps/frontend-web/package.json,apps/backend/__init__.py- the git tag
vMAJOR.MINOR.PATCH - the
CHANGELOG.mdheading — unbracketed:## MAJOR.MINOR.PATCH - YYYY-MM-DD
Rules that keep the release pipeline green (.github/workflows/release.yml):
- The CHANGELOG uses Keep-a-Changelog style with a single
## [Unreleased]section at the top. Only[Unreleased]is bracketed; released versions are not. (The release job greps for^## <version>, so a bracketed## [3.4.0]would NOT match.) - Do not use product-milestone numbers (e.g. "Enterprise v1.1") as CHANGELOG headings — that caused the 1.x-vs-3.x drift that was reconciled on 2026-05-30. Refer to milestones in the body text, not the version heading.
- A release only fires when
package.json's version changes onmainand a matching## <version>CHANGELOG entry exists. Land both in the same promotion. - The docs site deploys only from
main(dev pushes just build).
AIFactory uses a simplified release process with version bumping and changelog management.
┌─────────────────────────────────────────────────────────────────┐
│ RELEASE FLOW │
├─────────────────────────────────────────────────────────────────┤
│ │
│ develop branch main branch │
│ ────────────── ─────────── │
│ │ │ │
│ │ 1. bump-version.js │ │
│ │ (creates commit) │ │
│ │ │ │
│ ▼ │ │
│ ┌─────────┐ │ │
│ │ v1.1.0 │ 2. Create PR │ │
│ │ commit │ ────────────────────► │ │
│ └─────────┘ │ │
│ │ │
│ 3. Merge PR ▼ │
│ ┌──────────┐ │
│ │ v1.1.0 │ │
│ │ on main │ │
│ └────┬─────┘ │
│ │ │
│ 4. Create tag & release │
│ ▼ │
│ ┌──────────┐ │
│ │ v1.1.0 │ │
│ │ release │ │
│ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Landing a commit on main does not deploy it. deploy.yml ships a commit only when it
changes package.json's version — the same signal release.yml already gates on. Before
#1559 every promotion to main built, signed, pinned and restarted the pods: 28 commits
in 14 days, of which 4 were releases. That restart is what kills a running build (#1465) and
what made "wait for a quiet window" a routine cost (#1425).
So there are exactly two ways to ship:
| you want to | do this |
|---|---|
| release (the normal path) | bump the version as below; the merge to main deploys and cuts the release |
| ship one commit without a version bump (a hotfix) | gh workflow run deploy.yml -f ref=<sha> |
A push to main that does not bump the version reports success with its jobs skipped, and
says so in the run summary, including the two commands above. That is not a broken deploy.
On your development branch:
# Navigate to project root
cd AIFactory
# Bump version (choose one)
node scripts/bump-version.js patch # 1.0.0 -> 1.0.1 (bug fixes)
node scripts/bump-version.js minor # 1.0.0 -> 1.1.0 (new features)
node scripts/bump-version.js major # 1.0.0 -> 2.0.0 (breaking changes)
node scripts/bump-version.js 1.2.0 # Set specific versionThis will:
- Update
apps/frontend-web/package.json - Update
package.json(root) - Update
apps/backend/__init__.py - Create a commit with message
chore: bump version to X.Y.Z
Add release notes to CHANGELOG.md:
## 1.1.0 - Release Title
### New Features
- Feature description
### Improvements
- Improvement description
### Bug Fixes
- Fix description
---Then amend the version bump commit:
git add CHANGELOG.md
git commit --amend --no-edit# Push your branch
git push origin your-branch
# Create PR to develop (or main for releases)
gh pr create --base develop --title "Release v1.1.0"After the PR is approved and merged:
# Create a git tag
git checkout develop
git pull origin develop
git tag -a v1.1.0 -m "Release v1.1.0"
git push origin v1.1.0# Create release using GitHub CLI
gh release create v1.1.0 \
--title "v1.1.0 - Release Title" \
--notes-file CHANGELOG.mdOr create the release manually via GitHub UI:
- Go to Releases
- Click "Create a new release"
- Select the tag
- Add release notes from CHANGELOG.md
We follow Semantic Versioning:
| Type | Format | When to Use | Example |
|---|---|---|---|
| MAJOR | X.0.0 | Breaking changes, incompatible API changes | 1.0.0 → 2.0.0 |
| MINOR | 0.X.0 | New features, backwards compatible | 1.0.0 → 1.1.0 |
| PATCH | 0.0.X | Bug fixes, backwards compatible | 1.0.0 → 1.0.1 |
Each version entry in CHANGELOG.md should follow this format:
## X.Y.Z - Release Title
### New Features
- Feature description with context
### Improvements
- Improvement description
### Bug Fixes
- Fix description
### Breaking Changes (if any)
- Description of breaking changes
---- Be specific: Instead of "Fixed bug", write "Fixed crash when opening large files"
- Group by impact: Features first, then improvements, then fixes
- Credit contributors: Mention contributors for significant changes
- Link issues: Reference GitHub issues where relevant (e.g., "Fixes #123")
# Full release workflow
node scripts/bump-version.js minor
# Edit CHANGELOG.md
git add CHANGELOG.md
git commit --amend --no-edit
git push origin your-branch
gh pr create --base develop --title "Release v1.1.0"
# After merge:
git checkout develop && git pull
git tag -a v1.1.0 -m "Release v1.1.0"
git push origin v1.1.0
gh release create v1.1.0 --title "v1.1.0" --notes-file CHANGELOG.md| File | Field |
|---|---|
package.json |
version |
apps/frontend-web/package.json |
version |
apps/backend/__init__.py |
__version__ |
Run the bump script again - it updates all files atomically:
node scripts/bump-version.js 1.1.0If a tag already exists for a version:
# Delete local tag
git tag -d v1.1.0
# Delete remote tag (if pushed)
git push origin :refs/tags/v1.1.0
# Create new tag
git tag -a v1.1.0 -m "Release v1.1.0"
git push origin v1.1.0Edit the release on GitHub directly, or:
gh release edit v1.1.0 --notes "Updated release notes"