A Plugin adds platform capability — a new channel adapter, a new RAG provider.
That is what separates it from a Solution, which adds
domain logic such as HR or recruitment.
Core does not yet provide an implementation of CoreRegistrarInterface, and
nothing in Core reads or validates SolutionManifest at boot. The contracts
below exist in fapost/foundation; the Core side that consumes them is not
built yet.
The service provider
A Plugin is a Composer package whose service provider extends
Fapost\Foundation\Lifecycle\AbstractPluginServiceProvider:
onActivate() and onDeactivate() are available to hook enabling and disabling;
both are no-ops unless you override them.
The manifest
SolutionManifest is what makes a Plugin checkable at boot rather than at the
moment it breaks. requiresPlatform is a version constraint on Core;
requiresCapabilities names the platform capabilities the Plugin needs to be
present. Boot-time and update-lifecycle validation of the manifest is the intended
behaviour; Core does not perform it yet.
What you may register
Everything is meant to go through CoreRegistrarInterface, the single registration
entry point defined in fapost/foundation. Core does not implement it yet.
An extension declares intent through the registrar. It must not call Route,
Schedule, or the migrator directly — the platform decides when and how those
are mounted.
Rules
Depend on public contracts, not Core internals. Everything a Plugin needs is
in fapost/foundation. A use App\… statement in a Plugin will break on a Core
refactor, and nothing in the stability promise covers it.
Do not inject Vue components into the running builder. The builder front end
is compiled before your Plugin is enabled. Make a node configurable by describing
it with a configuration schema — see Builder UI extensions.
Follow migration isolation. A Plugin migration is a DDL operation like any
other: no app(), no config(), no tenant-aware services, no reads of another
module’s tables. See Migration isolation.