perf(test): make schema-dump target + opt-in fast-path docs (WS-6)

Session 2.6's per-test migrate:fresh in 4 backfill test classes
replays ~120 migrations. Laravel's schema:dump produces a single
SQL file that migrate:fresh loads atomically when present —
significantly faster on environments where the host has the
\`mysql\` CLI available for Laravel's load step.

Changes:
- New Makefile target \`schema-dump\` runs mysqldump INSIDE the
  bm_mysql Docker container (no host mysqldump dependency to
  GENERATE the dump). Outputs api/database/schema/mysql-schema.sql.
- api/database/schema/.gitignore added: mysql-schema.sql is NOT
  committed by default. Laravel's auto-load on migrate:fresh
  shells out to the host's \`mysql\` CLI; on hosts without it,
  the presence of the dump file actively breaks migrate (exit 127).
  Treat the dump as opt-in per dev environment.
- CLAUDE.md "Schema dumps (opt-in fast path)" subsection added with
  the workflow: brew install mysql-client → migrate to head →
  make schema-dump → optionally commit.

phpstan-baseline.neon: removed a stale "unused use \$actor" entry
in FormSubmissionService whose underlying closure pint cleaned up
in commit 060d6f3 (Task 1).

Wall-time on this dev machine (no host mysql CLI): NO speedup,
backfill tests still ~6s per setUp. The dump file is ready for
environments that have mysql CLI. Documented as a deviation.

JSON canonicalization (commit 060d6f3) is the load-bearing
correctness fix from this branch; the schema-dump perf path is a
nice-to-have that activates per-environment.

Refs: WS-6 session 2.6 deviation #5 cleanup

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-28 14:07:15 +02:00
parent a791a276fa
commit fe110ba761
4 changed files with 58 additions and 8 deletions

View File

@@ -100,7 +100,7 @@ When writing migrations or queries:
- Use Laravel's query builder where possible — it handles cross-DB syntax consistently
- For raw SQL, write MySQL syntax (no need for SQLite fallbacks). Index introspection: query `information_schema.STATISTICS`, never `sqlite_master`.
- Foreign keys go on every relation column. The `nullOnDelete` / `cascadeOnDelete` choice is per the relationship's domain semantics; "no FK" is not an option.
- Tests that round-trip JSON data via MySQL JSON columns must use `assertEquals` (structural equality) rather than `assertSame` on associative arrays — MySQL JSON may reorder object keys.
- JSON values stored in byte-stable columns (`schema_snapshot`, webhook `payload_snapshot`, activity-log `properties` for diff payloads) MUST be canonicalized at write via `App\Support\Json\JsonCanonicalizer`. MySQL JSON columns may reorder associative-array keys on round-trip; canonicalize so re-emits / HMAC signatures / audit-replay diffs are byte-identical. Opaque-config JSON (`form_schemas.settings`, `translations`) is exempt — key order has no semantic meaning there. Tests on canonicalized data use `assertSame(JsonCanonicalizer::encode($a), JsonCanonicalizer::encode($b))`.
Other database rules:
@@ -108,6 +108,37 @@ Other database rules:
- Create migrations in dependency order: foundation first, then dependent tables
- Always add composite indexes as documented in the design document (section 3.5)
### Schema dumps (opt-in fast path)
Laravel supports `database/schema/{driver}-schema.sql` as a fast-path
baseline that `migrate:fresh` loads in one statement instead of replaying
every migration. For Crewli's backfill / migration tests (which call
`migrate:fresh` per test), this can yield a meaningful speedup —
**when the host has the `mysql` and `mysqldump` CLI tools available.**
Workflow:
1. Install MySQL client tools on host (one-time):
```bash
brew install mysql-client
echo 'export PATH="/opt/homebrew/opt/mysql-client/bin:$PATH"' >> ~/.zshrc
```
2. Bring `crewli_test` to head: `DB_DATABASE=crewli_test php artisan migrate --force`
3. Generate the dump: `make schema-dump`
4. Commit `api/database/schema/mysql-schema.sql` alongside any new migrations.
The schema dump is **NOT committed by default** because Laravel's
auto-load path shells out to the `mysql` CLI; on hosts without it,
the presence of the dump file actively breaks `migrate` / `migrate:fresh`
(load fails, exit 127). Treat the dump as opt-in per dev environment.
`make schema-dump` runs `mysqldump` inside the `bm_mysql` Docker
container, so contributors don't need `mysqldump` on the host to
**generate** the dump — only to **load** it via Laravel's auto-detection.
`--prune` is NOT used: individual migration files stay readable in
`api/database/migrations/` for audit / rollback purposes.
### Roles and permissions
- Use Spatie `laravel-permission`