# Extension API Versioning

Every Capell package declares the Capell API range it supports. This page explains what that declaration means, what Capell guarantees inside it, and how to tell whether a Capell release will break your package.

## `capellApiVersion`

`capell.json` requires `capellApiVersion`, a Composer semver **constraint** — not a fixed version:

```json
{
    "manifest-version": 3,
    "capellApiVersion": "^1.0"
}
```

Capell compares its own extension API version against your constraint with `Composer\Semver\Semver::satisfies()`. The current value is published as a constant:

```php
Capell\Core\Support\Extensions\CapellExtensionApi::CURRENT_VERSION; // '1.1.0'
```

This is the **extension API** version. It is deliberately separate from the Capell release version and from any package's own `version`, so the two can move independently: a Capell release that adds no extension surfaces does not bump it.

`^1.0` is the right constraint for almost every package. It accepts every 1.x extension API, including additive minor releases. Pin tighter only when you knowingly depend on behaviour introduced in a specific minor version, and never pin to an exact version — `"1.1.0"` excludes `1.2.0` and makes the package uninstallable on the next Capell release for no benefit.

### Where it is enforced

The base manifest validator only requires `capellApiVersion` to be a non-empty string. The constraint is actually evaluated in two places:

| Check | Behaviour on mismatch |
| ----- | --------------------- |
| `php artisan capell:extension-audit` | Reports "Manifest capellApiVersion does not allow the current Capell API." |
| `php artisan capell:upgrade --dry-run` | Reports a finding for an empty or non-satisfying constraint before the upgrade runs |

So an incompatible constraint surfaces at audit and upgrade-planning time, not at boot. A package whose constraint excludes the running Capell is not silently disabled — run the audit to find out.

Individual contributions narrow this further. `public static function compatibleCapellApiVersion(): string` is an abstract member of the `ExtensionContribution` contract, so every class listed in `contributes[]` must define it, and the audit checks each one against the running API in addition to the manifest-level constraint. A contribution can therefore declare a tighter range than the package as a whole.

## What The Version Covers

The extension API version tracks the **stable extension surfaces** — the registries, contracts, and manifest requirements a package is allowed to build on. It does not cover internal Capell classes, which may change in any release.

Two generated files in this directory define the surface:

- `extension-surface-catalog.json` — every extension surface, each marked with a stability level. Generated by `scripts/build-extension-surface-catalog.php` and contract-tested. Verify it is current with:

  ```bash
  composer check:extension-surfaces
  ```

  The Composer script passes `--check`, which only verifies. Run the script without that flag to rewrite the catalogue.

- `stable-extension-api-baseline.json` — a frozen snapshot of the surfaces marked `stable`, including a reflection hash of every public method signature, plus core's Composer requirements, migrations, and config keys. Verify with:

  ```bash
  composer check:stable-extension-api
  ```

The baseline check rebuilds the snapshot from current code and diffs it against the committed file. Drift is reported as `removed class: X` or `changed public signature: X`, and also as a bare section name — `manifestRequirements`, `packageConstraints`, `migrations`, or `configKeys` — so adding a core migration or changing a core Composer constraint counts as drift too.

Drift only passes with an explicit compatibility decision record in `stable-extension-api-decision.json` whose `drift` entries match the detected drift exactly. Running the script without `--check` does rewrite the baseline, so the guarantee ultimately rests on reviewing the committed baseline diff rather than on the script alone.

That is the practical guarantee behind `^1.0`. A stable surface does not lose a class or change a public signature within the major line without a recorded, reviewed decision.

## Checking Your Package Against A Capell Release

Before supporting a new Capell version:

```bash
php artisan capell:extension-audit path/to/your-package
php artisan capell:upgrade --dry-run
```

The audit validates the manifest against the v3 contract and checks the API constraint. It exits non-zero when any result has severity `error`. The upgrade dry run reports version findings across every installed package without changing anything.

Neither command verifies that your package still *behaves* correctly. Run your own suite against the target Capell release as well — see [Testing packages](testing-packages.md#standalone-package-harness).

## When Capell Bumps The API Version

- **Patch** — no extension surface change.
- **Minor** — surfaces added. Existing packages on `^1.0` keep working; this is the common case.
- **Major** — a stable surface was removed or changed. Packages must widen or move their constraint deliberately, and the change carries a recorded decision.

Declaring `^1.0` and testing against each Capell release is the whole maintenance obligation for most packages.

## Next

- [Package anatomy](package-anatomy.md)
- [Extension surface catalogue](extension-surface-catalog.md)
- [Testing packages](testing-packages.md)
- [Upgrading](../operations/upgrading.md)