--- phase: 01-tournament-engine plan: 04 subsystem: api tags: [go, clock, websocket, ticker, state-machine, warnings, chi, rest-api] # Dependency graph requires: - phase: 01-tournament-engine provides: LibSQL database with blind_levels table, WebSocket hub, chi HTTP server, JWT auth middleware provides: - Server-authoritative clock engine with state machine (stopped/running/paused) - Drift-free ticker with 1/sec normal and 10/sec final 10s broadcast - Clock registry for multi-tournament support (thread-safe) - Clock API routes (start, pause, resume, advance, rewind, jump, get, warnings) - Configurable warning thresholds with WebSocket events - Clock state persistence to DB for crash recovery - ClockSnapshot for reconnecting clients affects: [01-tournament-engine, display-views, player-pwa] # Tech tracking tech-stack: added: [] patterns: - Clock state machine with mutex-protected transitions - Ticker goroutine with 100ms resolution using monotonic clock - Broadcast rate adaptation (1/sec normal, 10/sec final 10s) - State change callbacks for async DB persistence - Crash recovery restores as paused (operator must explicitly resume) - ClockSnapshot as single source of truth for all client communication key-files: created: - internal/clock/engine.go - internal/clock/ticker.go - internal/clock/registry.go - internal/clock/warnings.go - internal/clock/engine_test.go - internal/clock/warnings_test.go - internal/clock/registry_test.go - internal/server/routes/clock.go modified: - internal/server/server.go - cmd/leaf/main.go - cmd/leaf/main_test.go - internal/auth/pin.go key-decisions: - "Clock ticker uses 100ms resolution with broadcast gating (not two separate tickers) for simplicity" - "Crash recovery always restores clock as paused -- operator must explicitly resume for safety" - "Overtime mode defaults to repeat (last level repeats indefinitely) with configurable stop option" - "State change callback is async (goroutine) to avoid holding clock mutex during DB writes" - "Clock routes use chi sub-router with floor role requirement on mutations" patterns-established: - "AuditRecorder interface for decoupled audit trail recording (avoids circular imports)" - "StateChangeCallback pattern for async persistence on meaningful state changes" - "Registry pattern for managing multiple concurrent engines per tournament" requirements-completed: [CLOCK-01, CLOCK-02, CLOCK-03, CLOCK-04, CLOCK-05, CLOCK-06, CLOCK-07, CLOCK-08, CLOCK-09] # Metrics duration: 8min completed: 2026-03-01 --- # Phase 1 Plan 04: Clock Engine Summary **Server-authoritative tournament clock with state machine, drift-free 100ms ticker, configurable warnings, multi-tournament registry, REST API, and DB persistence -- verified with 25 unit tests** ## Performance - **Duration:** 8 min - **Started:** 2026-03-01T02:48:21Z - **Completed:** 2026-03-01T02:56:31Z - **Tasks:** 2 - **Files modified:** 12 ## Accomplishments - Complete clock state machine (stopped/running/paused) with all transitions and guard conditions - Drift-free ticker using Go monotonic clock at 100ms resolution with adaptive broadcast rate - Multi-tournament clock registry with independent engines and per-engine ticker goroutines - Full REST API with role-based access (floor+ for mutations, any auth for reads) - Configurable warning thresholds (default 60s/30s/10s) with WebSocket events - Clock state persisted to DB on every meaningful state change for crash recovery - Hand-for-hand mode support (clock pauses, per-table deduction via seating engine) - Overtime handling (repeat last level or stop -- configurable) ## Task Commits Each task was committed atomically: 1. **Task D1: Clock engine state machine and ticker** - `9ce05f6` (feat) 2. **Task D2: Warnings, API routes, tests, and server wiring** - `ae90d9b` (feat) ## Files Created/Modified - `internal/clock/engine.go` - ClockEngine state machine, Level/Warning/Snapshot structs, all operations - `internal/clock/ticker.go` - StartTicker with 100ms resolution and adaptive broadcast rate - `internal/clock/registry.go` - Thread-safe ClockRegistry managing multiple engines - `internal/clock/warnings.go` - Warning system documentation (logic in engine.go) - `internal/clock/engine_test.go` - 16 tests: state machine, countdown, auto-advance, pause/resume, jump, rewind, hand-for-hand, snapshot, overtime, crash recovery - `internal/clock/warnings_test.go` - 5 tests: threshold detection, no re-emit, reset on level change, defaults, custom - `internal/clock/registry_test.go` - 4 tests: get/create, get, remove, shutdown - `internal/server/routes/clock.go` - 8 API endpoints with DB integration and role-based access - `internal/server/server.go` - Clock registry wired into server constructor and route registration - `cmd/leaf/main.go` - Clock registry created during startup, shutdown on exit - `cmd/leaf/main_test.go` - Test setup updated with clock registry parameter - `internal/auth/pin.go` - Fix missing crypto/rand import (auto-fix) ## Decisions Made - Clock ticker uses single 100ms timer with broadcast gating rather than two separate tickers (simpler, same result) - Crash recovery always restores as paused -- safer than auto-resuming with potentially stale remaining time - Overtime defaults to repeat mode (most common in poker tournaments) - State change callback runs in a goroutine to prevent DB latency from blocking the clock ticker - AuditRecorder is an interface (not direct import) to avoid circular dependency between clock and audit packages ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 3 - Blocking] Missing crypto/rand import in auth/pin.go** - **Found during:** Task D2 (compiling routes package which depends on auth) - **Issue:** internal/auth/pin.go uses `rand.Read` but was missing `crypto/rand` import, preventing compilation of the routes package - **Fix:** Added `"crypto/rand"` to the import block - **Files modified:** internal/auth/pin.go - **Verification:** `go build ./...` passes, `go vet ./...` passes - **Committed in:** ae90d9b (Task D2 commit) --- **Total deviations:** 1 auto-fixed (1 blocking) **Impact on plan:** Single missing import in a dependency. No scope creep. ## Issues Encountered None ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - Clock engine fully operational for all tournament clock requirements - API endpoints ready for frontend integration (Plan K/L) - Clock registry supports multi-tournament operation (MULTI-01) - Warning events ready for display views (Phase 2) - DB persistence ensures clock survives server restart ## Self-Check: PASSED All 8 created files verified present. Both commit hashes (9ce05f6, ae90d9b) found in git log. SUMMARY.md exists at expected path. --- *Phase: 01-tournament-engine* *Completed: 2026-03-01*