# Data Model: Backup Scheduling MVP (032) **Date**: 2026-01-05 This document describes the entities, relationships, validation rules, and state transitions derived from the feature spec. ## Entities ### 1) BackupSchedule (`backup_schedules`) **Purpose**: Defines a tenant-scoped recurring backup plan. **Fields** - `id` (bigint, PK) - `tenant_id` (FK → `tenants.id`, required) - `name` (string, required) - `is_enabled` (bool, default true) - `timezone` (string, required; default `UTC`) - `frequency` (enum: `daily|weekly`, required) - `time_of_day` (time, required) - `days_of_week` (json, nullable; required when `frequency=weekly`) - array in range 1..7 (Mon..Sun) - `policy_types` (jsonb, required) - array; keys MUST exist in `config('tenantpilot.supported_policy_types')` - `include_foundations` (bool, default true) - `retention_keep_last` (int, default 30) - `last_run_at` (datetime, nullable) - `last_run_status` (string, nullable) - `next_run_at` (datetime, nullable) - timestamps **Indexes** - `(tenant_id, is_enabled)` - optional `(next_run_at)` **Validation Rules (MVP)** - `tenant_id`: required, exists - `name`: required, max length (e.g. 255) - `timezone`: required, valid IANA tz - `frequency`: required, in `[daily, weekly]` - `time_of_day`: required - `days_of_week`: required if weekly; values 1..7; unique values - `policy_types`: required, array, min 1; all values in supported types config - `retention_keep_last`: required, int, min 1 **State** - Enabled/disabled (`is_enabled`) --- ### 2) BackupScheduleRun (`backup_schedule_runs`) **Purpose**: Represents one execution attempt of a schedule. **Fields** - `id` (bigint, PK) - `backup_schedule_id` (FK → `backup_schedules.id`, required) - `tenant_id` (FK → `tenants.id`, required; denormalized) - `scheduled_for` (datetime, required; UTC minute-slot) - `started_at` (datetime, nullable) - `finished_at` (datetime, nullable) - `status` (enum: `running|success|partial|failed|canceled|skipped`, required) - `summary` (jsonb, required) - suggested keys: - `policies_total` (int) - `policies_backed_up` (int) - `errors_count` (int) - `type_breakdown` (object) - `warnings` (array) - `unknown_policy_types` (array) - `error_code` (string, nullable) - `error_message` (text, nullable) - `backup_set_id` (FK → `backup_sets.id`, nullable) - timestamps **Indexes** - `(backup_schedule_id, scheduled_for)` - `(tenant_id, created_at)` - unique `(backup_schedule_id, scheduled_for)` (idempotency) **State transitions** - `running` → `success|partial|failed|skipped|canceled` --- ## Relationships - Tenant `hasMany` BackupSchedule - BackupSchedule `belongsTo` Tenant - BackupSchedule `hasMany` BackupScheduleRun - BackupScheduleRun `belongsTo` BackupSchedule - BackupScheduleRun `belongsTo` Tenant - BackupScheduleRun `belongsTo` BackupSet (nullable) ## Notes - `BackupSet` and `BackupItem` already support soft deletes in this repo; retention can soft-delete old backup sets. - Unknown policy types are prevented at save-time, but runs defensively re-check to handle legacy DB data.