Skip to content

Versioning Guide

Automated semantic versioning for BoB-managed projects — conventional-commit-driven version bumps, git tags, CHANGELOG.md, and GitHub Releases. This is the encyclopedic reference for the BoB-wide versioning capability (capability #275).

TL;DR — Add "versioning": { "enabled": true } to your project manifest and run cdprov refresh. Commit with Conventional Commit messages. Merges to your main branch now cut versioned releases automatically.

Before this, releases across the fleet were manual or absent: bigbrain itself had a single stale v1.0.0 tag and a 0.0.0 package.json. Versioning makes releases declarative, reproducible, and consistent across every project — provisioned from one source of truth rather than hand-copied CI.

BoB standardizes on commit-and-tag-version (the maintained fork of the deprecated standard-version):

  • Minimal dependencies, language-agnostic, runs anywhere Node runs.
  • Reads Conventional Commits → computes the next semver → bumps package.json, regenerates CHANGELOG.md, and creates a git tag.

Alternative considered — release-please (Google): a release-PR model that avoids pushing release commits directly to the main branch (better when the main branch is protected). Deferred because it is less “automatic” (requires merging a release PR) and heavier to set up. Revisit if branch protection on the main branch becomes a hard fleet-wide requirement. See capability #275 for the full decision.

  1. You merge a commit to the main branch (e.g. feat: add X).
  2. .github/workflows/auto-release.yml triggers (scoped to the project’s source paths — see Trigger paths).
  3. commit-and-tag-version bumps the version from the commits since the last tag, rewrites CHANGELOG.md, commits chore(release): X.Y.Z, and tags vX.Y.Z.
  4. The workflow pushes the commit + tag with --follow-tags.
  5. A GitHub Release is published from the new CHANGELOG.md section.

The chore(release) commit is loop-guarded — it does not re-trigger the workflow. A manual workflow_dispatch lets you force a patch/minor/major bump.

Commit type Bump Appears in changelog under
fix: patch Bug Fixes
feat: minor Features
feat!: / BREAKING CHANGE: major ⚠ Breaking
perf: patch Performance
refactor:, docs:, test:, build:, ci: patch* their sections
chore:, style: none hidden

* Non-feat/fix types contribute to the changelog but only bump when present with other changes; a release with only chore: commits is a no-op.

Versioning is an opt-in manifest capability (it is not a symlinked registry item — cdprov only symlinks; versioning copies template files). It follows the same pattern as the gh_project block.

provisions/<project>.json:

{
"_meta": { "project": "my-app" },
"skills": [],
"commands": [],
"agents": [],
"versioning": {
"enabled": true,
"tool": "commit-and-tag-version",
"main_branch": "main",
"node_version": "22",
"trigger_paths": ["src/**", "package.json"]
}
}

All fields except enabled are optional:

Field Default Meaning
enabled — (required) true provisions versioning; false (or omitted block) is a no-op
tool commit-and-tag-version version-bump engine (only value supported today)
main_branch main branch the workflow triggers on
node_version 22 Node version in the release workflow
trigger_paths (none) push path filter; omitted ⇒ any push to main_branch

The orchestrator populates this block for you (see below); you rarely hand-write it.

Terminal window
cdprov refresh # or the /provision skill

provision_versioning() copies the template set into the project, never clobbering existing files, and idempotently merges package.json:

Template Destination
templates/versioning/auto-release.yml .github/workflows/auto-release.yml
templates/versioning/.versionrc.json .versionrc.json
templates/versioning/commitlint.config.cjs commitlint.config.cjs

package.json gains a release script + the commit-and-tag-version devDependency, and a 1.0.0 version if it was absent or 0.0.0.

Terminal window
cdprov status # shows which versioning files are present / missing
cdprov diff # dry run — what a refresh would create (writes nothing)

versioning is recommended by default for every project (the registry command versioning is default: true) — fulfilling the “all BoB projects should include versioning” goal of capability #275. When you run the orchestrator interview (or scripts/orchestrator/recommend.js), the generated manifest includes a versioning block with trigger_paths chosen for your project type.

The push path filter is parameterized per project type so docs-only or unrelated changes don’t cut releases:

Project type trigger_paths
cms (Drupal, …) docroot/**
api, backend, web-app, library src/**, package.json
cli src/**, bin/**, package.json
framework (BoB-like) skills/**, commands/**, agents/**, registry/**, runbooks/**, scripts/**, hooks/**, bin/**, templates/**, schemas/**, profiles/**, Makefile, package.json
research, mixed, any (none — any push to the main branch)

The mapping lives in VERSIONING_TRIGGER_PATHS in scripts/orchestrator/recommend.js. An existing versioning block in a manifest is always preserved on re-run, so opt-outs and customizations survive.

Automated bumping only works if commits follow the convention — a non-conventional subject makes the release a silent no-op or a mis-bump. Enforcement is built into BoB’s commit-msg git hook (templates/git/hooks/commit-msg, installed by cdi), which does two jobs in order (git allows only one commit-msg hook):

  1. Strip AI boilerplate (Co-Authored-By: Claude, 🤖 Generated, …) — always.
  2. Enforce Conventional Commitsonly when the project has versioning config (.versionrc.json or commitlint.config.* in the repo root). Projects without versioning keep the strip-only behavior, so nothing else is affected.

The enforcement step prefers node_modules/.bin/commitlint (using the provisioned commitlint.config.cjs) when installed, and otherwise falls back to a built-in, dependency-free check — so the convention is enforced even before npm install. Issue-ref scopes (feat(#274): …), feat!: breaking markers, and merge/revert/chore(release) subjects are all accepted; the chore(release) commit that commit-and-tag-version creates passes cleanly and is loop-guarded in CI.

The commit skill already drafts Conventional Commit messages, so commits made through /commit (or warp-drive) pass enforcement by construction.

The workflow pushes the release commit and tag directly to the main branch using the default GITHUB_TOKEN. If branch protection is enabled on the main branch without an exception for github-actions[bot], the push will fail. This is the trade-off behind deferring release-please (the PR-model alternative). No protection ⇒ it just works.

bigbrain itself is the reference implementation (#274): see its .github/workflows/auto-release.yml, .versionrc.json, and package.json release script. The capability was then generalized into the provisionable template set (#276) and wired into the orchestrator (#277).