---
url: /internals/releasing.md
---
# Releasing and contributing

The authoritative text is `RELEASING.md` in the repository; this page is the map. The repo's `CLAUDE.md` holds the development loop and conventions.

## Branches and PRs

`master` is the only trunk, protected: no force pushes, required checks `test`, `pint`, `require-semver-label`. Work happens on branches and lands through PRs with conventional-commit titles, because titles become changelog lines. Every PR carries exactly one `semver:major`, `semver:minor` or `semver:patch` label (the Release Drafter autolabeler sets it from the title prefix; `.github/workflows/semver-label.yml` enforces it), optionally `security` and `skip-changelog`.

**Docs label** (since the documentation overhaul of October 2026): a PR labelled `semver:minor` or `semver:major` also carries `docs:linked` (with the evolve-docs PR URL in its body) or `docs:none`. `.github/workflows/docs-label.yml` enforces it; `semver:patch` PRs are exempt. The docs PR merges before the release is published.

## CI

`.github/workflows/ci.yml` runs the suite (`vendor/bin/phpunit`, booted through the workbench provider) and `pint --test` on every PR and master push. The `Release CI guard` workflow checks that the tagged commit's CI run was green when a release is published and prepends a warning to the notes otherwise.

## Releases

Merging updates a rolling draft release (Release Drafter, `.github/release-drafter.yml` for the categories); the next version follows from the labels merged since the last tag. Releasing is publishing the draft; the tag is created on publish, never by hand. Release candidates come from the `Cut release candidate` workflow (`cut-rc.yml`), numbered `vX.Y.Z-rc.N`, published as prereleases that only projects on an `@RC` constraint see. Consumers ride three constraint tiers: `^2.x` stable, `^2.x@RC` for test sites, a dev branch alias for development.

After publishing a stable release: `.github/scripts/docs-release-entry.php <tag> <date> <notes> <releases.md>` drafts the entry for `getting-started/releases.md` in evolve-docs (heading, date, badge, one bullet per PR by category, a TODO for the summary). Write the summary, condense the bullets, move any upgrade checklist to [Upgrading within 2.x](/getting-started/upgrading#upgrading-within-2-x) and link it, fold a patch release under its minor's "Patches" heading, then deploy the docs (`deploy.sh` in evolve-docs, with Diego's confirmation).

## Development loop

```bash
composer update
vendor/bin/testbench workbench:build   # assets, sqlite, migrate, seed (rerun to reset)
composer workbench:theme               # frontend theme build (needs node)
vendor/bin/testbench serve             # panel at /evolve, auto-login
vendor/bin/phpunit                     # or --testsuite Harness for the boot smoke test
vendor/bin/pint --dirty
```

`workbench:build` exits 0 even when a seeder fails; read the output. Host-app config that real installs carry in published files is mirrored in `workbench/app/Providers/WorkbenchServiceProvider.php`; a feature that needs new host config is wired there too, or tests and the served workbench do not see it. See [Workbench](/internals/workbench).

## Conventions that bite

* Keep `theme/theme/` and the module `app-publish/` overlays in sync with component API changes; they publish into real projects and have gone stale before (2.5).
* Every change to a view a project might have copied gets a `ViewOverrides::CHANGES` entry.
* Unreleased features are not documented on the docs site's master; the docs PR waits for the code to ship.
* Code style is Pint with `pint.json` (Laravel preset, spaces around the concatenation dot).
