Migration rules
The one rule that reads a package's migrations — every table created carries the connector's prefix and no core table is touched — and exactly how the sources are scanned.
migrations.own_tables_only
Runs when a package path is passed. Checks every *.php file under <package>/database/migrations with MigrationRules::violations($key, $path).
Fails with a connector creates only tables prefixed with its key and never alters a core table: 2026_10_01_000000_create_rooms_table.php: creates "rooms" without the example_ prefix, 2026_10_02_000000_add_column.php: alters "project_resources", which is not the connector's table.
Why the data rules: a connector owns only what the platform makes it keep, in tables named after it, so it can be removed from the codebase without breaking the core and uninstalled from a project without cascading into anything a member paid for.
How the sources are scanned
The rule reads the migration files as text, not the database, so it fails on the developer's machine before anything runs. Two patterns:
| Pattern | Must name |
|---|---|
Schema::create('<table>' | A table starting with <key>_. |
Schema::table('<table>', Schema::drop(…), Schema::dropIfExists(…), Schema::rename(…) | A table starting with <key>_. |
Both accept single or double quotes and whitespace after the parenthesis. A missing database/migrations directory is not a violation: a connector with no tables of its own passes.
What it does not catch
- Raw statements.
DB::statement('ALTER TABLE project_resources …')is not matched. Review reads for it, and Subscriby's own suite forbids raw schema changes in application migrations; hold your package to the same standard. - Foreign keys the wrong way. A
foreignId('project_id')->constrained()inside your own table is allowed and encouraged (rule 3 of the data rules); the scanner does not check the direction, because a connector table pointing into the core is exactly the intended shape. - Column-level rules inside your own tables. Anything inside
Schema::create('example_…')is yours to design.
Running it alone
use Subscriby\Connector\Testing\Conformance\MigrationRules;
$offences = MigrationRules::violations('example', __DIR__.'/../database/migrations');
expect($offences)->toBe([]);The static method needs no application and no registry, so it belongs in every connector's own test suite, whether or not you can run the rest of the kit locally.
Additive after first publish
The scanner enforces ownership, not history. Once a version of your package is installed anywhere, later migrations add rather than change; a data move is expand → verify → contract, with proof that the data moved before any drop. That rule is review's, and yours.
How is this guide?
Capability rules
The seven rules that run only when a port is bound — resource kinds for access control, commands and relay modes against the manifest, recovery vocabulary and readiness, the portal button, native provider keys, the migrator's reports.
Beyond the kit
What Subscriby's own suite checks that the kit cannot from outside a package — isolation in both directions, translation parity, skeleton parity — and what review reads for.