Extension Lifecycle
Use this when a package should be discovered, audited, installed, enabled, and surfaced by Capell without patching host package code.
The lifecycle has four parts:
- Composer makes the package classes available.
capell.jsondescribes the extension contract.- Install providers and commands prepare database/config state.
- Runtime/admin/frontend providers load only when the extension is enabled.
Minimum Files
Section titled “Minimum Files”packages/example├── composer.json├── capell.json├── src/Providers/ExampleInstallServiceProvider.php├── src/Providers/ExampleServiceProvider.php└── testsAdd admin, frontend, migrations, settings, translations, and docs only when the package needs them.
Composer Metadata
Section titled “Composer Metadata”Composer discovery should make provider classes available. Capell decides when each provider bucket is loaded.
{ "name": "vendor/example", "type": "library", "autoload": { "psr-4": { "Vendor\\Example\\": "src" } }, "extra": { "laravel": { "providers": [ "Vendor\\Example\\Providers\\ExampleMetadataServiceProvider" ] } }}Keep the Composer-discovered provider safe. It can publish metadata, but it should not register runtime behaviour that depends on the extension being enabled.
Manifest Shape
Section titled “Manifest Shape”capell.json uses manifest version 3. The validator rejects v1/v2 fields such as capell-version.
{ "manifest-version": 3, "name": "vendor/example", "slug": "example", "displayName": "Example", "kind": "package", "visibility": "catalogue", "capellApiVersion": "^4.0", "version": "1.0.0", "description": "Example extension for Capell.", "product": { "group": "Capell Foundation", "tier": "free" }, "namespace": "Vendor\\Example\\", "surfaces": ["admin", "frontend", "console"], "dependencies": { "requires": ["capell-app/core"], "supports": ["capell-app/admin", "capell-app/frontend"], "conflicts": [] }, "providers": { "metadata": [ "Vendor\\Example\\Providers\\ExampleMetadataServiceProvider" ], "install": [ "Vendor\\Example\\Providers\\ExampleInstallServiceProvider" ], "runtime": ["Vendor\\Example\\Providers\\ExampleServiceProvider"], "admin": ["Vendor\\Example\\Providers\\ExampleAdminServiceProvider"], "frontend": [ "Vendor\\Example\\Providers\\ExampleFrontendServiceProvider" ] }, "contributes": [], "contributionTraceability": [], "database": { "migrations": true, "settings": false, "requiredTables": [] }, "commands": { "install": "capell:example-install", "setup": null, "setupParams": [], "demo": null, "demoParams": [], "health": null }, "settings": [], "permissions": [], "capabilities": [], "performance": { "cacheTags": [], "cacheSafety": { "cacheable": false, "sensitiveOutput": false, "queueInvalidation": false, "variesBy": [], "invalidationSources": [] } }, "healthChecks": [], "commercial": { "privateDocsRequested": false }, "marketplace": { "summary": "Adds an example extension surface.", "categories": ["example"], "screenshots": [] }}visibility defaults to catalogue. Set it to support for dependency-only packages such as theme admin/core helpers. Support packages are hidden from normal package selection, but they remain installable when a visible package lists them in dependencies.supports.
Provider buckets must all exist, even when some lists are empty. Provider classes must be inside the package PSR-4 namespace and must extend Laravel’s ServiceProvider.
Provider Buckets
Section titled “Provider Buckets”| Bucket | Loads when | Allowed work |
|---|---|---|
metadata | The package is discovered | Safe metadata only. Do not register runtime routes, resources, views, or listeners here. |
install | Installer or extension install workflow runs | Install commands, migration publishing, setup helpers, one-off checks. |
runtime | Extension is enabled | Shared runtime services, models, policies, events, package config. |
admin | Extension is enabled and admin/console runtime is active | Filament resources, admin bridges, widgets, settings schemas, admin routes. |
frontend | Extension is enabled and frontend runtime is active | Render hooks, frontend components, frontend routes, Tailwind assets, cache dependencies. |
The separation matters. Admin code should not load on public frontend requests, and install-only wiring should not stay active after setup.
Contributions
Section titled “Contributions”Use contributes when a package exposes contract-backed capabilities that Capell can audit.
{ "type": "admin-resource", "class": "Vendor\\Example\\Extensions\\ExampleAdminResourceContribution"}Each contribution class must implement the contract expected by its type. Current contribution types include:
admin-resourcesectionpage-typepage-variationdashboard-widgetoverview-statpermissionroutesettingfrontend-componentrender-hookassetmigrationscheduled-jobhealth-checkworkflow-attention
Use existing package extension points directly when the package only needs a small local hook. Use manifest contributions when the package should be auditable by Capell tooling.
Audit Before Install
Section titled “Audit Before Install”Run the audit before wiring a package into an app:
php artisan capell:extension-audit packages/examplephp artisan capell:extension-audit packages/example/capell.jsonThe audit checks the manifest shape, provider buckets, class namespaces, contribution contracts, cache-safety metadata, health checks, and Marketplace metadata.
Install Flow
Section titled “Install Flow”Once Composer has installed the package:
php artisan capell:package-cache:clearphp artisan capell:package-cachephp artisan capell:extension-install vendor/example --dry-runphp artisan capell:extension-install vendor/exampleUse --dry-run first on a new package. Use package-scoped params when the extension declares install parameters:
php artisan capell:extension-install vendor/example \ --url=https://example.test \ --languages=en \ --sites=Main \ --param=vendor/example:seedDemo=trueRuntime Rules
Section titled “Runtime Rules”- Optional packages must not register runtime providers until enabled.
- Package writes belong in Actions, not resource classes or controllers.
- User-facing strings belong in package translations.
- Public frontend output must not include authoring markers, model IDs, field paths, package internals, permissions, or signed admin URLs.
- Cacheable frontend output needs invalidation metadata in the manifest or explicit cache invalidation registration.