MyronCMS Developers Hub Docs sandbox review

Core Concepts

Load Lifecycle

The loader turns local extension files into runtime contributions. It does not install packages from a marketplace, fetch dependencies, or sandbox third-party code. It scans local files, validates their contracts, checks grants, and registers only what passes.

Human Path

A developer authors files under app/extensions/{vendor}/. An operator registers and enables the extension. On boot, MyronCMS loads valid contributions.

AI Path

An AI coding agent can author local files and run verification. A running MCP client cannot create files or force the loader to trust an extension. It can only discover and call loaded, exposed, scope-allowed runtime tools.

Loader Sequence

MyronExtensionLoader::boot() follows this sequence:

  1. Reset prior in-memory manifests, load failures, and admin routes.
  2. Scan app/extensions/*/manifest.json.
  3. Normalize the directory vendor by replacing hyphens with underscores.
  4. Parse manifest.json with the expected vendor.
  5. Store the parsed manifest in memory.
  6. Check grant state when extension grant tables exist.
  7. Skip unregistered extensions.
  8. Skip disabled extensions.
  9. Reject extensions whose manifest requires ungranted scopes.
  10. Run extension migrations when hasMigrations is true.
  11. Require bootstrap.php if it exists.
  12. Register returned actions through ActionRegistry::registerExtension().
  13. Register returned MIC resolvers through MicElementRegistry::registerExtension().
  14. Register returned mail transports through MailTransportRegistry::registerExtension().
  15. Mount declared admin routes whose handler files exist.
  16. Clear prior load errors for successfully loaded vendors.
  17. Record manifest, migration, or bootstrap failures in extension_load_errors.

Core requests continue when an extension fails to load. The failure is logged and exposed through the Installed Extensions surface.

Bootstrap Shapes

bootstrap.php may return one direct action:

return new MyVendorHelloAction($sdk);

It may return a list of actions:

return [
    new MyVendorHelloAction($sdk),
];

It may return named contribution arrays:

return [
    'actions' => [new MyVendorHelloAction($sdk)],
    'micElementTypes' => [
        'catalog' => new MyCatalogMicResolver($sdk),
    ],
    'mailTransports' => [
        'resend' => new ResendMailTransport($sdk),
    ],
];

The loader ignores entries that do not implement the expected interface for the contribution slot.

Migrations

When hasMigrations is true, migration files are loaded from:

app/extensions/{vendor}/migrations/*.php

Migration files may return versioned SQL lists or callables. SQL statements may create, alter, drop, or index only tables prefixed with {vendor}_. Applied versions are tracked in {vendor}_schema_migrations.

Runtime Contributions

ContributionRegistration pointRuntime effect
MyronActionActionRegistry::registerExtension()Adds namespaced /api/v1/x/{vendor}/... actions and optional MCP tools.
MIC resolverMicElementRegistry::registerExtension()Adds x.{vendor}.{slug} rich-text insertion resolution.
Mail transportMailTransportRegistry::registerExtension()Adds x.{vendor}.{slug} outbound mail transport candidates.
Admin routeExtensionLoader::getAdminRoutes()Mounts isolated admin route handlers under /x/{vendor}/....

Failure Modes

FailureResult
No extensions directoryLoader returns without registering extensions.
Manifest missing or invalidFailure is recorded for that vendor.
Extension not registeredLoader skips the extension.
Extension disabledLoader skips the extension.
Ungranted required scopeLoader records a failure.
Migration touches a core tableMigration runner rejects and records failure.
Bootstrap throwsLoader records failure.
Action has wrong URI/domain/schemaAction registry rejects and loader records failure.
Admin route handler missingRoute is not mounted.

Verification

Run:

php app/tests/verify-extension-system-foundation.php

For a loaded action, verify all three layers:

  1. CLI foundation test passes.
  2. Direct API call to /api/v1/x/{vendor}/... succeeds for a scoped caller.
  3. MCP tools/list shows the tool only when exposeToMcp: true and the caller holds the required scope.