docs(claude): codify MySQL-only policy for Crewli (WS-6)

CLAUDE.md's existing "Database" subsection is expanded to spell out
that MySQL 8.0 is the exclusive database engine for dev, testing, and
production. SQLite is forbidden — its quirks (rebuild-on-FK-add
cascade behaviour, JSON key-order non-determinism on round-trip,
VARCHAR length enforcement gaps, concurrency model) mask bugs that
would surface in production MySQL.

Context: WS-6 session 2.5 had to omit a FK constraint due to an
SQLite-only quirk; session 2.6 (Task 1+2 of this branch) moved test
infrastructure to MySQL and restored the FK. This policy ensures the
issue cannot recur. Includes the cross-engine query-rewrite rules
the migration surfaced (information_schema.STATISTICS over
sqlite_master, assertEquals over assertSame on JSON-derived data).

Refs: RFC-WS-6.md v1.1, WS-6 session 2.5 deviation #1, session 2.6

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-28 12:57:48 +02:00
parent ae06b16d9b
commit 571777b5df

View File

@@ -81,6 +81,29 @@ Configure two frontend origins in both Laravel (`config/cors.php` via env) and t
### Database
Crewli uses **MySQL 8.0 exclusively** across all environments:
- **Local development**: MySQL 8.0 via `docker-compose.yml` (`bm_mysql` container, port 3306). Run `make services` to start.
- **Testing**: MySQL 8.0 via the same Docker container, separate `crewli_test` database. Run `make test-db-create` once, then `make test`.
- **Staging/production**: MySQL 8.0 on the deployment VPS.
**SQLite is forbidden** in all environments. Reasons:
- SQLite has known quirks around foreign-key constraints (rebuild-on-FK-add cascades), JSON queries (key-order non-determinism on round-trip differs from MySQL), and concurrent writes that mask bugs which MySQL would surface in production
- Cross-database query syntax differences cause silent test/production drift (e.g. `sqlite_master` vs. `information_schema`, VARCHAR length enforcement)
- Crewli's enterprise-grade product philosophy doesn't tolerate "but it works on SQLite" as a passing test
If a contributor finds SQLite references in `phpunit.xml`, `.env.testing`, or any config file, treat it as a regression and fix immediately.
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.
Other database rules:
- Primary keys: ULID via `HasUlids` (not UUID v4, not auto-increment on business tables)
- Create migrations in dependency order: foundation first, then dependent tables
- Always add composite indexes as documented in the design document (section 3.5)