Files
cim_summary/.planning/milestones/v1.0-phases/01-data-foundation/01-01-SUMMARY.md
admin 38a0f0619d chore: complete v1.0 Analytics & Monitoring milestone
Archive milestone artifacts (roadmap, requirements, audit, phase directories)
to .planning/milestones/. Evolve PROJECT.md with validated requirements and
decision outcomes. Create MILESTONES.md and RETROSPECTIVE.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 10:34:18 -05:00

7.0 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
01-data-foundation 01 database
supabase
postgresql
migrations
typescript
monitoring
health-checks
alerts
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
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)
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<string, unknown> 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)
created modified
backend/src/models/migrations/012_create_monitoring_tables.sql
backend/src/models/HealthCheckModel.ts
backend/src/models/AlertEventModel.ts
backend/src/models/index.ts
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
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
INFR-01
INFR-04
8min 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)