--- phase: 01-data-foundation plan: 01 subsystem: database tags: [supabase, postgresql, migrations, typescript, monitoring, health-checks, alerts] # Dependency graph requires: [] provides: - service_health_checks table with status CHECK constraint, JSONB probe_details, checked_at column - alert_events table with alert_type/status CHECK constraints, lifecycle timestamps - HealthCheckModel TypeScript class with CRUD static methods - AlertEventModel TypeScript class with CRUD static methods - Barrel exports for both models and their types from models/index.ts affects: - 01-02 (Phase 1 Plan 2 — next data foundation plan) - Phase 2 (health probe services will write to service_health_checks) - Phase 2 (alert service will write to alert_events and use findRecentByService for deduplication) - Phase 3 (API endpoints will query both tables) # Tech tracking tech-stack: added: [] patterns: - Static class model pattern (no instantiation — all methods are static async) - getSupabaseServiceClient() called per-method, never cached at module level - PGRST116 error code handled as null return (not an exception) - Input validation in model create() methods before any DB call - Record for JSONB fields (no any types) - Named Winston logger import: import { logger } from '../utils/logger' - IF NOT EXISTS on all DDL (idempotent forward-only migrations) - TEXT + CHECK constraint pattern for enums (not PostgreSQL ENUM types) key-files: created: - backend/src/models/migrations/012_create_monitoring_tables.sql - backend/src/models/HealthCheckModel.ts - backend/src/models/AlertEventModel.ts modified: - backend/src/models/index.ts key-decisions: - "TEXT + CHECK constraint used for status/alert_type columns (not PostgreSQL ENUM types) — consistent with existing project pattern" - "getSupabaseServiceClient() called per-method (not module-level singleton) — follows Research finding about Cloud Function cold start issues" - "checked_at column added to service_health_checks separate from created_at — records actual probe run time, not DB insert time" - "Forward-only migration only (no rollback scripts) — per user decision documented in CONTEXT.md" - "RLS enabled on both tables with no explicit policies — service role key bypasses RLS; user-facing policies deferred to Phase 3" patterns-established: - "Model static class: all methods static async, getSupabaseServiceClient() per-method, PGRST116 → null" - "Input validation before Supabase call: non-empty string check, union type allowlist check" - "Error re-throw with method prefix: 'HealthCheckModel.create: ...' for log traceability" - "deleteOlderThan(days): compute cutoff in JS then filter with .lt() — Supabase client does not support date arithmetic in filters" requirements-completed: [INFR-01, INFR-04] # Metrics duration: 8min completed: 2026-02-24 --- # Phase 01 Plan 01: Data Foundation Summary **SQL migration + TypeScript model layer for monitoring: service_health_checks and alert_events tables with HealthCheckModel and AlertEventModel static classes using getSupabaseServiceClient() per-method** ## Performance - **Duration:** ~8 min - **Started:** 2026-02-24T16:29:39Z - **Completed:** 2026-02-24T16:37:45Z - **Tasks:** 2 - **Files modified:** 4 ## Accomplishments - Created migration 012 with both monitoring tables, CHECK constraints on all enum columns, JSONB columns for flexible metadata, INFR-01 required created_at indexes, composite indexes for dashboard queries, and RLS on both tables - Created HealthCheckModel with 4 static methods: create (with input validation), findLatestByService, findAll (with optional filters), deleteOlderThan (30-day retention) - Created AlertEventModel with 6 static methods: create (with validation), findActive, acknowledge, resolve, findRecentByService (deduplication support for Phase 2), deleteOlderThan - Updated models/index.ts with barrel exports for both model classes and all 4 types ## Task Commits Each task was committed atomically: 1. **Task 1: Create monitoring tables migration** - `ad6f452` (feat) 2. **Task 2: Create HealthCheckModel and AlertEventModel with barrel exports** - `4a620b4` (feat) **Plan metadata:** (docs commit — see below) ## Files Created/Modified - `backend/src/models/migrations/012_create_monitoring_tables.sql` - DDL for service_health_checks and alert_events tables with indexes and RLS - `backend/src/models/HealthCheckModel.ts` - CRUD model for service_health_checks table, exports ServiceHealthCheck and CreateHealthCheckData types - `backend/src/models/AlertEventModel.ts` - CRUD model for alert_events table with lifecycle methods (acknowledge/resolve), exports AlertEvent and CreateAlertEventData types - `backend/src/models/index.ts` - Added barrel exports for both new models and their types ## Decisions Made - TEXT + CHECK constraint used for status/alert_type columns (not PostgreSQL ENUM types) — consistent with existing project pattern established in prior migrations - getSupabaseServiceClient() called per-method (not cached at module level) — per Research finding about potential Cloud Function cold start issues with module-level Supabase client caching - checked_at column kept separate from created_at on service_health_checks — probe timestamp vs. DB write timestamp are logically distinct (Research Pitfall 5) - No rollback scripts in migration — forward-only per user decision documented in CONTEXT.md ## Deviations from Plan None — plan executed exactly as written. ## Issues Encountered - Branch mismatch: Task 1 committed to `gsd/phase-01-data-foundation`, Task 2 accidentally committed to `upgrade/firebase-functions-v7-nodejs22` due to shell context reset between Bash calls. Resolved by cherry-picking the Task 2 commit onto the correct GSD branch. - Pre-existing TypeScript error in `backend/src/config/env.ts` (Type 'never' has no call signatures) — unrelated to this plan's changes, deferred per scope boundary rules. ## User Setup Required None — no external service configuration required. The migration file must be run against the Supabase database when ready (will be part of the migration runner invocation in a future phase or manually via Supabase dashboard SQL editor). ## Next Phase Readiness - Both tables and both models are ready. Phase 2 health probe services can import HealthCheckModel and AlertEventModel from `backend/src/models` immediately. - The findRecentByService method on AlertEventModel is ready for Phase 2 alert deduplication. - The deleteOlderThan method on both models is ready for Phase 2 scheduler retention enforcement. - Migration 012 needs to be applied to the Supabase database before any runtime writes will succeed. --- *Phase: 01-data-foundation* *Completed: 2026-02-24* ## Self-Check: PASSED - FOUND: backend/src/models/migrations/012_create_monitoring_tables.sql - FOUND: backend/src/models/HealthCheckModel.ts - FOUND: backend/src/models/AlertEventModel.ts - FOUND: .planning/phases/01-data-foundation/01-01-SUMMARY.md - FOUND commit: ad6f452 (Task 1) - FOUND commit: 4a620b4 (Task 2)