felt/.planning/phases/01-tournament-engine/01-04-SUMMARY.md
Mikkel Georgsen ff85bf704e docs(01-04): complete clock engine plan
- SUMMARY.md with 25 tests, 2 task commits, 1 deviation
- STATE.md: plan 5/14, 4 completed, clock decisions recorded
- ROADMAP.md: 4/14 plans complete in Phase 1
- REQUIREMENTS.md: CLOCK-01 through CLOCK-09 marked complete

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

6.8 KiB

phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
01-tournament-engine 04 api
go
clock
websocket
ticker
state-machine
warnings
chi
rest-api
phase provides
01-tournament-engine LibSQL database with blind_levels table, WebSocket hub, chi HTTP server, JWT auth middleware
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
01-tournament-engine
display-views
player-pwa
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
created modified
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
internal/server/server.go
cmd/leaf/main.go
cmd/leaf/main_test.go
internal/auth/pin.go
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
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
CLOCK-01
CLOCK-02
CLOCK-03
CLOCK-04
CLOCK-05
CLOCK-06
CLOCK-07
CLOCK-08
CLOCK-09
8min 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