← Data architecture

3.6 Data seeding

KMP has different data paths for a newly provisioned tenant, a rich local baseline, and automated tests. They are intentionally not interchangeable. Production provisioning must never load development identities or snapshots.

Data paths

Purpose Supported path
Managed tenant TenantProvisioningService / bin/cake tenant provision
Fresh minimal legacy/local schema migrations plus initialization seeds/reset command
Rich Docker development ./dev-reset-db.sh --seed
PHPUnit databases test bootstrap and app/bin/setup_test_database.sh
Second local isolation tenant created and pruned by the seeded Docker reset

All tenant seeding runs against one explicitly selected tenant database. Platform registry/admin/secret setup runs separately against platform.

Managed provisioning

A hosted tenant is initialized through the provisioning workflow, not by running DevLoadSeed. Provisioning creates trusted registry/host metadata and database credentials, applies core and every loaded plugin migration, initializes required settings/defaults, performs smoke checks, and optionally creates the first tenant superuser before activation.

Provisioning is documented in Multi-tenant architecture. Treat its service and tests as authoritative for production defaults.

Initialization seeds

Core seed classes live in app/config/Seeds. InitMigrationSeed orchestrates the minimal historical initialization order:

  1. branches;
  2. initial members;
  3. roles and permissions;
  4. role-permission joins; and
  5. initial member-role assignments.

Additional initialization seeds cover warrants and workflow definitions where the current reset/update path calls them. Active plugins own their reference seeds under their own config/Seeds directories. Do not invoke a plugin seed before its migrations and dependencies exist.

Some released migrations also seed or transform required configuration. That makes the value part of the forward schema lifecycle; duplicating it in a standalone seed can produce divergent fresh and upgraded tenants.

Rich local baseline

Run from the repository root:

./dev-reset-db.sh --seed

This command is destructive to local KMP databases. For PostgreSQL it:

  1. pauses app/background services and recreates application, platform, and test databases;
  2. migrates core and loaded plugins to the baseline snapshot versions;
  3. loads app/tests/pg_seed_baseline.sql;
  4. runs all forward migrations and updateDatabase work;
  5. synchronizes current local award/workflow development data and advances date-sensitive examples;
  6. migrates the platform database and registers the primary local tenant;
  7. stores local tenant credentials through the configured local secret store;
  8. provisions and migrates kmp2, then copies only a pruned starter subset;
  9. resets active demo-member passwords and rebuilds the test database; and
  10. clears caches and restarts coordinated services.

The second tenant intentionally excludes operational, personal, workflow, attendance, document, queue, and other transactional examples. It exists to make isolation tests meaningful, not to be a byte-for-byte clone.

dev_seed_clean.sql remains the legacy MySQL/MariaDB baseline input. Do not load it directly into PostgreSQL or treat either snapshot as the current schema; forward migrations are required after the baseline.

After forward migrations and calendar advancement, the seeded reset runs the debug-only DevRepairWarrantRosterApprovals seed. It restores anonymized approval responses represented by historical Approved roster counters, populates the policy lookup fields used by the unified approval queue, and keeps cached workflow request titles aligned with shifted roster names. The repair is idempotent, preserves complete multi-approver response history, and rejects missing approval counts, conflicting responses, or incomplete multi-approver history instead of inventing additional identities.

When the legacy MySQL/MariaDB snapshot is regenerated, make_amp_seed_db.sh maps each distinct source warrant approver to a distinct retained demo member in deterministic ID order. This preserves approval cardinality without retaining production identity data. The generator exits before deleting source members if there are too few demo identities for a one-to-one mapping or if an Approved roster’s counter does not match its distinct approval rows.

Development-only seeds

DevLoadSeed orchestrates gathering types, activities, gatherings, joins, and Waivers gathering-activity examples. Other focused development seeds create Award bestowal-to-do personas and scenarios.

These seeds may contain synthetic identities and deterministic references for manual testing. They must:

Prefer a small purpose-specific seed for a repeatable scenario over adding thousands of volatile records to the baseline.

Stable test references

Tests should use named constants from the project base test classes for the few IDs that are deliberately stable. Query by a unique semantic attribute when no constant exists. app/tests/TestDataReference.md documents only supported stable references; record counts and incidental IDs are not contracts.

$admin = $this->getTableLocator()
    ->get('Members')
    ->get(self::ADMIN_MEMBER_ID);

Do not infer sequential IDs, assert the entire seed count, or depend on dates that age naturally. The reset pipeline advances designated date-sensitive data, and tests should freeze/supply an effective time for lifecycle boundaries.

Adding seed data

  1. Decide whether the value is required product configuration, a managed tenant default, or development/test data.
  2. Put required upgrade-safe values in the owning forward migration or initializer used by provisioning.
  3. Put plugin values in the plugin.
  4. Use existing seed helpers and semantic lookups rather than raw magic IDs.
  5. Define dependency order explicitly.
  6. Make rerun behavior clear: idempotent upsert, clean-database only, or reset pipeline only.
  7. Add a seed/provisioning test and update TestDataReference.md only for a genuinely stable identity.

Never put secret keys, real email addresses, production password hashes, customer records, backup payloads, or platform recovery material in a seed.

Verification

For an initialization/seed change, exercise the path it owns:

# Complete local baseline and second tenant
./dev-reset-db.sh --seed

# Test database setup from app/
bash bin/setup_test_database.sh

# Seed-focused or provisioning tests
vendor/bin/phpunit --filter Seed
vendor/bin/phpunit --filter TenantProvisioning

Also verify that kmp.localhost and kmp2.localhost contain different transactional data, both migration catalogs are current, and background workers start without consuming another tenant’s queue.

Maintenance rules