From 571777b5df6561192fabaa39a1cb9e7d76d63621 Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Tue, 28 Apr 2026 12:57:48 +0200 Subject: [PATCH] docs(claude): codify MySQL-only policy for Crewli (WS-6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- CLAUDE.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index d6039d28..0ef36927 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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)