> For the complete documentation index, see [llms.txt](https://docs.flxbl.io/flxbl/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flxbl.io/flxbl/sfp/building-artifacts/automated-package-versioning/expressing-version-intent.md).

# Expressing version intent

{% hint style="warning" %}
**This feature is in limited preview** — it is available to selected customers only, and behaviour and commands may change based on feedback. Reach out to the flxbl team if you would like early access.
{% endhint %}

The version pipeline reads your intent from three sources. From highest to lowest priority:

1. `/version` comments on merged pull requests
2. `version:` footers in commit messages
3. Conventional commit prefixes (smart defaults)

If none of them apply, the change is treated as a **patch** — the pipeline never over-bumps on its own.

### Conventional commits (the everyday path)

If your team already writes [Conventional Commits](https://www.conventionalcommits.org/), you get sensible versions with zero extra effort:

| Commit message                                           | Intent |
| -------------------------------------------------------- | ------ |
| `fix(sales): correct rounding on invoice totals`         | patch  |
| `feat(sales): add multi-currency support`                | minor  |
| `feat(sales)!: rework the pricing API`                   | major  |
| `perf(sales): cache exchange rates` *(any other prefix)* | patch  |
| `update invoice layout` *(no recognised prefix)*         | patch  |

A `BREAKING CHANGE:` footer also marks a major:

```
feat(sales): rework the pricing API

BREAKING CHANGE: PricingService.calculate now requires a currency code
```

**Scoping matters when a commit touches several packages.** A scoped commit applies its intent only to the named package; other packages touched by the same commit get a patch:

```
feat(sales): add multi-currency support
```

→ `sales` gets a minor; if the commit also modified files in `sales-ui`, `sales-ui` gets a patch.

An unscoped commit applies its intent to every package it touches:

```
feat: add multi-currency support across sales and sales-ui
```

→ both `sales` and `sales-ui` get a minor.

When several commits land between builds, each package takes the **highest** intent seen across the commits that affect it. Merge commits are parsed like any other commit.

### The `version:` commit footer

For explicit control, add a `version:` footer to any commit in the range. Entries can be separated by spaces, commas or semicolons.

**Bump specific packages:**

```
fix(sales): tighten validation rules

version: sales:minor
```

**One intent for everything the build touches:**

```
chore: prepare quarterly release

version: major
```

**Mixed — a global intent with per-package overrides:**

```
feat: platform refresh

version: minor sales:major
```

→ every impacted package gets a minor, except `sales`, which gets a major.

**Bump the dependents of what you changed:**

```
feat(core-crm): change the account lookup contract

version: dependents:patch
```

→ `core-crm` resolves from its own commits (a minor here), and every package that **directly depends on** `core-crm` is bumped a patch and included in the build — even if its own source did not change. Dependents expansion is single-level (dependents of dependents are not pulled in) and only ever happens when you ask for it; a plain `feat:` never expands the build.

Invalid entries are skipped with a warning rather than failing the build — `version: sales:enormous` simply falls through to the conventional-commit default.

### `/version` comments on pull requests

Reviewers and authors can settle the version question on the pull request itself, without touching the commits. Add a comment to the PR before or after merging:

```
/version sales:major
```

When the merged PR falls into the range of the next build, the comment is picked up and **outranks every other source**.

**Per-package form** — applies to the named package unconditionally:

```
/version sales:major sales-ui:minor
```

**Global form** — applies to the packages *that this PR changed* (and only those):

```
/version minor
```

{% hint style="info" %}
The global form of a PR comment is deliberately scoped to its own pull request's files. Two PRs merged in the same build range never leak intents onto each other — a `/version minor` on PR A cannot bump a package that only PR B touched. Commit footers behave differently: a global `version: minor` footer applies to every package impacted by the build.
{% endhint %}

**Dependents form** — same semantics as the commit footer:

```
/version core-crm:minor dependents:patch
```

**Later `/version` comments win.** Unlike conventional commits, where the highest intent across the range wins, `/version` comments resolve **last-wins**: when a package has more than one `/version` comment across the PRs in a build range, the **most recent** comment for that package is used. This lets a later comment correct an earlier one — `/version sales:patch` after an earlier `/version sales:major` downgrades the bump to a patch, and `/version sales:none` cancels a bump entirely. Comments with no timestamp (as on some providers) fall back to the order in which they were read; two intents in the very same comment resolve to the stronger of the two.

### AI-suggested intent (addon)

[AI Version Assist](/flxbl/sfp/analysing-a-project/ai-version-assist.md) is an opt-in addon that reads a pull request's diff and posts a `/version` comment authored by the app. Its comment is marker-tagged and resolves in a separate, lower tier: it sits **below** every human `/version` comment and `version:` footer, and **above** the conventional-commit defaults. A human `/version` comment for a package always overrides the assist's directive for that package. The addon is off by default; see [AI Assisted Version Intent](/flxbl/sfp/analysing-a-project/ai-version-assist.md) to enable it.

### When one pull request changes several packages

A single PR often touches more than one package. What each package receives depends on how the intent is expressed:

Say a PR modifies files in `sales`, `sales-ui` and `core-crm`, with `sales` last published as `2.4.1`, `sales-ui` as `1.2.0` and `core-crm` as `3.1.0`:

| How the PR expresses intent                          | sales       | sales-ui    | core-crm    |
| ---------------------------------------------------- | ----------- | ----------- | ----------- |
| Title/commit `feat(sales): ...` (scoped)             | 2.5.0 minor | 1.2.1 patch | 3.1.1 patch |
| Title/commit `feat: ...` (unscoped)                  | 2.5.0 minor | 1.3.0 minor | 3.2.0 minor |
| Comment `/version minor` (global, scoped to this PR) | 2.5.0 minor | 1.3.0 minor | 3.2.0 minor |
| Comment `/version sales:major`                       | 3.0.0 major | 1.2.1 patch | 3.1.1 patch |
| Footer `version: minor core-crm:major`               | 2.5.0 minor | 1.3.0 minor | 4.0.0 major |

The rules behind the table:

* A **scoped** conventional prefix applies only to the named package; the other packages the PR touched fall back to a patch.
* An **unscoped** prefix and the **global** forms apply to every package the change touched.
* **Per-package** entries always win over global ones for their package.
* Every package is versioned from **its own** last published version — packages in the same PR do not share a version line.

Each package is then built and published with its own computed version; a PR spanning three packages produces three independently versioned artifacts.

### Putting it together

A typical sprint on a `sales` package last published as `2.4.1`:

1. Monday — PR merges with `fix(sales): correct tax rounding` → pending intent: patch
2. Wednesday — PR merges with `feat(sales): support payment plans` → pending intent: minor (higher wins)
3. Thursday — a reviewer realises the payment-plan change breaks an integration contract and comments `/version sales:major` on the merged PR
4. Friday — the release build runs: `sales` is built as `3.0.0.<build>` (PR comment outranks commits), published, and `3.0.0` becomes the base for next sprint

### Unlocked packages: the promoted version guard

Salesforce does not allow a new patch version on a promoted (released) unlocked package series. When the pipeline resolves a *patch* for an unlocked package whose current series is already promoted, it automatically escalates the bump to a *minor* and logs a warning — saving the build from a late rejection by the platform. The guard requires DevHub access during the build and is skipped without it.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.flxbl.io/flxbl/sfp/building-artifacts/automated-package-versioning/expressing-version-intent.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
