felt/.planning/phases/01-tournament-engine/01-02-SUMMARY.md
Mikkel Georgsen 9bfd959eaf docs(01-02): complete database schema + migrations plan
- SUMMARY.md with full execution record and deviations
- STATE.md updated: plan 2/14, decisions, session info
- ROADMAP.md updated: Phase 1 progress 2/14
- REQUIREMENTS.md: ARCH-03, ARCH-08, PLYR-01, PLYR-07, SEAT-01, SEAT-02 complete

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 03:39:59 +01:00

154 lines
7.8 KiB
Markdown

---
phase: 01-tournament-engine
plan: 02
subsystem: database
tags: [libsql, sqlite, fts5, migrations, schema, go-embed]
# Dependency graph
requires:
- phase: none
provides: n/a
provides:
- Complete Phase 1 database schema (23 tables)
- Embedded migration runner with statement splitting for go-libsql
- FTS5 player search with automatic sync triggers
- Seed data (venue settings, chip sets)
- Dev seed (default admin operator)
- First-run setup detection
affects: [01-tournament-engine, player-management, tournament-runtime, financial-engine]
# Tech tracking
tech-stack:
added: [go-libsql, sqlite-fts5]
patterns: [embedded-sql-migrations, statement-splitting, go-embed-migrations, append-only-audit-triggers]
key-files:
created:
- internal/store/migrations/001_initial_schema.sql
- internal/store/migrations/002_fts_indexes.sql
- internal/store/migrations/003_seed_data.sql
- internal/store/migrations/004_dev_seed.sql
modified:
- internal/store/migrate.go
- internal/store/db.go
- cmd/leaf/main.go
- Makefile
key-decisions:
- "go-libsql requires single-statement Exec: migration runner splits SQL files into individual statements"
- "go-libsql PRAGMAs need special handling: journal_mode returns a row (use QueryRow), foreign_keys/busy_timeout use fallback execPragma helper"
- "Force single DB connection during migrations (SetMaxOpenConns(1)) to ensure table visibility across migration steps"
- "Dev seed (004) is gated by --dev flag, not applied in production mode"
- "Bcrypt hash for default admin PIN (1234) generated and embedded in dev seed SQL"
patterns-established:
- "Migration files: sequential numbered SQL in internal/store/migrations/, embedded via go:embed"
- "Statement splitting: splitStatements() handles triggers (BEGIN...END blocks), comments, and semicolons"
- "Financial columns: all INTEGER (int64 cents), zero REAL/FLOAT in schema"
- "Audit trail: append-only enforced by SQLite triggers (no UPDATE except undone_by, no DELETE)"
requirements-completed: [ARCH-03, ARCH-08, PLYR-01, PLYR-07, SEAT-01, SEAT-02]
# Metrics
duration: 10min
completed: 2026-03-01
---
# Phase 1 Plan 02: Database Schema + Migrations Summary
**Complete LibSQL schema (23 tables) with embedded migration runner, FTS5 player search, and seed data for tournament engine**
## Performance
- **Duration:** 10 min
- **Started:** 2026-03-01T02:27:39Z
- **Completed:** 2026-03-01T02:37:45Z
- **Tasks:** 2
- **Files modified:** 8
## Accomplishments
- 23-table schema covering venue settings, building blocks (chips, blinds, payouts, buy-ins, points), tournament templates, runtime tournaments, players, tables/seating, financial transactions, audit trail, and operators
- Embedded migration runner that splits SQL into individual statements for go-libsql compatibility
- FTS5 virtual table on player names/nicknames/emails with automatic sync triggers
- Audit trail tamper protection via SQLite triggers (reject UPDATE except undone_by, reject DELETE)
- All financial columns use INTEGER (int64 cents) -- zero REAL/FLOAT in schema
- Seed data: DKK venue settings, Standard and Copenhagen chip sets with denominations
- Dev mode: gated admin operator seed (PIN 1234, bcrypt hashed)
## Task Commits
Each task was committed atomically:
1. **Task B1: Design and write the initial schema migration** - `17dbfc6` (feat)
2. **Task B2: Implement migration runner and FTS5 indexes** - `0afa04a` (feat)
## Files Created/Modified
- `internal/store/migrations/001_initial_schema.sql` - Complete Phase 1 schema (23 tables, indexes, audit triggers)
- `internal/store/migrations/002_fts_indexes.sql` - FTS5 virtual table and sync triggers for player search
- `internal/store/migrations/003_seed_data.sql` - Default venue settings and built-in chip sets
- `internal/store/migrations/004_dev_seed.sql` - Dev-only default admin operator
- `internal/store/migrate.go` - Embedded migration runner with statement splitting
- `internal/store/db.go` - Database open/close with PRAGMA configuration and migration wiring
- `cmd/leaf/main.go` - Dev mode flag and database integration
- `Makefile` - Added run-dev target
## Decisions Made
- **go-libsql statement splitting:** go-libsql does not support multi-statement Exec. The migration runner splits each SQL file into individual statements, handling trigger bodies (BEGIN...END blocks) as a single statement.
- **PRAGMA handling:** go-libsql is inconsistent about which PRAGMAs return rows. `journal_mode=WAL` returns the mode string; `foreign_keys=ON` and `busy_timeout=5000` do not. A fallback `execPragma` helper tries Exec first, falling back to QueryRow.
- **Single connection during migration:** Forcing `SetMaxOpenConns(1)` during migration ensures all tables created in earlier migrations are visible to later ones (go-libsql connection pooling issue).
- **Dev seed gating:** The `004_dev_seed.sql` migration is only applied when `--dev` flag is set, preventing default admin credentials in production.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 3 - Blocking] go-libsql multi-statement Exec does not work**
- **Found during:** Task B2 (Migration runner implementation)
- **Issue:** go-libsql's `tx.Exec` with multi-statement SQL silently fails -- tables in 001_initial_schema.sql were not created despite the migration being recorded as applied
- **Fix:** Implemented `splitStatements()` function that splits SQL files into individual statements, handling trigger BEGIN...END blocks correctly. Removed transaction wrapping since go-libsql single-connection mode ensures atomicity.
- **Files modified:** internal/store/migrate.go
- **Verification:** All 4 migrations apply successfully, 23 tables created, second run skips all
- **Committed in:** 0afa04a
**2. [Rule 1 - Bug] go-libsql PRAGMA handling inconsistency**
- **Found during:** Task B2 (db.go PRAGMA setup)
- **Issue:** `PRAGMA journal_mode=WAL` returns a row (Exec fails), `PRAGMA foreign_keys=ON` does not return a row (QueryRow fails). Standard approach of using either Exec or QueryRow for all PRAGMAs doesn't work.
- **Fix:** Separate handling: QueryRow for journal_mode, execPragma helper (tries Exec, falls back to QueryRow) for others, then verify with getter queries.
- **Files modified:** internal/store/db.go
- **Verification:** All PRAGMAs set correctly (journal_mode=wal, foreign_keys=1, busy_timeout=5000)
- **Committed in:** 0afa04a
**3. [Rule 3 - Blocking] Go and gcc not installed**
- **Found during:** Task B2 pre-requisite check
- **Issue:** Go 1.24.1 and gcc were not installed on the system. go-libsql requires CGO (gcc).
- **Fix:** Installed Go 1.24.1 and gcc/libc6-dev via apt-get
- **Files modified:** System packages only (not committed)
- **Verification:** `go version` returns 1.24.1, CGO_ENABLED=1 works
---
**Total deviations:** 3 auto-fixed (1 bug, 2 blocking)
**Impact on plan:** All auto-fixes necessary for go-libsql compatibility. The statement-splitting pattern is essential for the migration system to work at all. No scope creep.
## Issues Encountered
- go-libsql connection pooling causes table visibility issues across migrations -- resolved by forcing single connection with SetMaxOpenConns(1)
- Plan A (project scaffold) was executed concurrently, committing some of Plan B's files (db.go, main.go, Makefile) in its scaffold commit. This did not cause issues but meant Task B2's commit only included the migrate.go fix.
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- Database schema complete for all Phase 1 features
- Migration system ready for future schema changes (add new numbered SQL files)
- FTS5 player search operational for typeahead
- Audit trail tamper protection active
- Ready for Plan C (Auth/Operators) and Plan D (Player Management)
## Self-Check: PASSED
- All 9 files verified present
- Both task commits (17dbfc6, 0afa04a) verified in git log
---
*Phase: 01-tournament-engine*
*Completed: 2026-03-01*