CapabilityProvision
A `provides` capability record. Declares that a plugin offers a concrete implementation (`service`) of a stable contract (`interface`) at a given `contractVersion`. Also accepts the legacy bare-FQCN string form via {@see fromInterface()} so legacy manifests (`contracts.provides = ["Foo\\BarInterface"]`) keep working until the capability records are fully adopted. The primary constructor validates exactly like {@see fromArray()} does: `id` and `interface` must be non-empty and `contractVersion` must be valid semver. There is no "trusted, pre-validated" construction path — hand-building a record (e.g. `new CapabilityProvision(...)` from reflected `#[PluginMetadata]` data) is validated identically to parsing one from a manifest. `exclusive` is TRI-STATE, and NULL means nobody decided. It used to default TRUE for a rich record and FALSE for a bare FQCN, so the same capability admitted one or several providers depending on HOW IT WAS WRITTEN — and the documented migration path runs bare → rich, so migrating a multi-provider capability as the docs recommend would have blocked boot. {@see \Milpa\Services\CapabilityMatcher} killed the same shape for `contractVersion`; this is the same lie one field over. ADR-0037 measured that nobody decides the cardinality of a capability today and refused to legislate who should. So this field stops inventing an answer: an explicit `true`/`false` is a decision, absence is the absence of one, and what the resolver does with an undecided cardinality is the resolver's to report — never this record's to guess.
CapabilityProvision::__construct()
public function __construct(string $id, string $interface, ?string $contractVersion, ?string $service = null, int $priority = 0, ?bool $exclusive = null):Parameters
| Name | Type | Description |
|---|---|---|
| $id | string | |
| $interface | string | |
| $contractVersion | (string | null) | NULL means nobody declared one. Only {@see fromInterface()} produces it: a rich record must still declare its version. |
| $service | ?string | |
| $priority | int | |
| $exclusive | ?bool |
Throws
\InvalidArgumentException If `id`/`interface` are empty or `contractVersion` is not valid semver.
CapabilityProvision::fromArray()
public static function fromArray(array $record): selfBuild a provision record from a decoded `provides` manifest entry. Coerces raw (possibly untyped) array values to their expected shape; validation of the result (`id`/`interface` non-empty, `contractVersion` valid semver) happens in the constructor, not here.
Parameters
| Name | Type | Description |
|---|---|---|
| $record | array<string, mixed> |
Throws
\InvalidArgumentException If `id`/`interface` are empty or `contractVersion` is not valid semver.
CapabilityProvision::fromInterface()
public static function fromInterface(string $interface): selfWrap a legacy bare-FQCN declaration as a record whose contract version is UNKNOWN. Leaves `exclusive` NULL: a legacy declaration predates the field, so it says nothing about how many providers the capability admits. It used to pin `false` — the mirror of the rich record's `true` — and between the two, cardinality was decided by SPELLING. Neither pin was a policy; §3.1's exclusive-by-default was retired in P17.3 for the same reason (ADR-0037).
Parameters
| Name | Type | Description |
|---|---|---|
| $interface | string |
CapabilityProvision::parse()
public static function parse(array|string $record): selfParse a `provides` manifest entry in either the legacy bare-FQCN string form or the structured-record array form, dispatching to {@see fromInterface()} or {@see fromArray()}.
Parameters
| Name | Type | Description |
|---|---|---|
| $record | (string | array<string, mixed>) |