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:
- branches;
- initial members;
- roles and permissions;
- role-permission joins; and
- 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:
- pauses app/background services and recreates application, platform, and test databases;
- migrates core and loaded plugins to the baseline snapshot versions;
- loads
app/tests/pg_seed_baseline.sql; - runs all forward migrations and
updateDatabasework; - synchronizes current local award/workflow development data and advances date-sensitive examples;
- migrates the platform database and registers the primary local tenant;
- stores local tenant credentials through the configured local secret store;
- provisions and migrates
kmp2, then copies only a pruned starter subset; - resets active demo-member passwords and rebuilds the test database; and
- 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:
- be clearly development-only;
- use fake/non-deliverable addresses and no customer data;
- tolerate the intended reset/update workflow;
- respect plugin ownership and foreign keys; and
- never be called by managed production provisioning.
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
- Decide whether the value is required product configuration, a managed tenant default, or development/test data.
- Put required upgrade-safe values in the owning forward migration or initializer used by provisioning.
- Put plugin values in the plugin.
- Use existing seed helpers and semantic lookups rather than raw magic IDs.
- Define dependency order explicitly.
- Make rerun behavior clear: idempotent upsert, clean-database only, or reset pipeline only.
- Add a seed/provisioning test and update
TestDataReference.mdonly 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
- Snapshot files are inputs to a controlled pipeline, not hand-maintained schema documentation.
- Keep initial/provisioning data small and deterministic.
- Remove temporary migration seeds after their last supported upgrade path only with an explicit compatibility decision.
- Update backup payload migration/restore compatibility when seeded workflow or schema state changes serialized archives.
- Never regenerate or replace the baseline as an incidental side effect of an unrelated test.