MyronCMS Developers Hub Docs sandbox review

Build Extensions

Add Isolated Migrations

Use extension migrations when an extension needs its own durable storage. The migration runner keeps extension storage isolated by table prefix and tracks applied versions in a vendor-owned migration table.

Goal

Create a migration that adds:

my_vendor_notes

and is tracked in:

my_vendor_schema_migrations

Prerequisites

  • The extension vendor is snake_case, such as my_vendor.
  • The extension has a valid manifest.json.
  • The operator can register, enable, and grant the extension.
  • The migration only creates or changes extension-owned tables.

Human Path

Set hasMigrations to true:

{
  "vendor": "my_vendor",
  "name": "My Vendor",
  "version": "0.1.0",
  "requiredScopes": ["content.read"],
  "hasMigrations": true,
  "adminRoutes": []
}

Create:

app/extensions/my_vendor/migrations/v001_create_notes_table.php

Return a versioned SQL list:

<?php

declare(strict_types=1);

return [
    'v001_create_notes_table' => [
        'CREATE TABLE IF NOT EXISTS my_vendor_notes (
            id VARCHAR(32) PRIMARY KEY,
            title VARCHAR(255) NOT NULL,
            body TEXT NOT NULL,
            created_at VARCHAR(40) NOT NULL
        )',
    ],
];

When the extension loads, ExtensionLoader calls ExtensionMigrationRunner before requiring bootstrap.php.

AI Path

An AI coding agent can create or edit:

  • app/extensions/my_vendor/manifest.json
  • app/extensions/my_vendor/migrations/v001_create_notes_table.php

The agent can run the foundation verification command and inspect the migration file for prefix compliance.

An installed MCP client cannot create migration files, call the migration runner directly, or bypass extension registration. After the operator loads the extension, MCP or API clients may call exposed actions that use the extension-owned tables, if those actions exist and scopes allow them.

Migration Loading

The loader reads:

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

Files are sorted by filename. A migration file may return:

  • an array of version => list<string> SQL statements,
  • an array of version => callable,
  • one callable, where the version is derived from the filename.

Array SQL migrations are checked for table isolation before each statement runs.

Scopes

Migration execution is not a caller-scoped API operation. It happens during extension load.

The extension still must pass the manifest grant gate. If the manifest declares content.read, the operator grant must include content.read; otherwise the loader rejects or skips the extension before runtime contributions are available.

Artifacts

Local files:

  • app/extensions/my_vendor/manifest.json
  • app/extensions/my_vendor/migrations/v001_create_notes_table.php

Database tables:

  • my_vendor_notes
  • my_vendor_schema_migrations

Runtime source:

  • MyronExtensionLoader::boot()
  • MyronExtensionMigrationRunner::run()

Safety Boundary

Keep every extension table under the vendor prefix:

{vendor}_*

Do not create, alter, drop, or index core tables. Do not write migrations that depend on real secrets or local-only absolute paths. Do not run migration files outside the extension loader as a workaround for grant or load failures.

Verification

Run:

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

The foundation test verifies the reference extension behavior:

  • myron_sample_pings is created,
  • myron_sample_schema_migrations is created,
  • a migration that tries to create core_table_violation is rejected.

For your extension, verify that:

  1. the manifest has hasMigrations: true,
  2. the migration file returns versioned migrations,
  3. all table names start with {vendor}_,
  4. the extension is registered and enabled,
  5. the vendor tracking table contains the applied version.

Failure Modes

FailureLikely surfaceSafe fix
Migration does not runhasMigrations is false, extension skipped, or file is missing.Enable hasMigrations, register the extension, and confirm the file path.
Version runs repeatedlyVersion key changes between boots.Keep stable version keys such as v001_create_notes_table.
Non-prefixed table rejectedMigration runner isolation check.Rename the table under {vendor}_*.
Bootstrap never loadsMigration threw before bootstrap.Fix migration failure; do not skip tracking manually.
MCP cannot run migrationExpected behavior.Migrations are local/runtime loader behavior, not MCP tools.