# Phase 1 — Data Model (Spec 114: System Console Control Tower)

This feature is primarily **read-only UI** over existing platform/ops metadata.

## Entities (existing)

### Workspace (`workspaces`)
- Purpose: group tenants; scope boundary for tenant plane.
- Relevant fields: `id`, `name`, `slug`, timestamps.
- Relationships:
  - `Workspace::tenants()`
  - `Workspace::memberships()`

### Tenant (`tenants`)
- Purpose: customer tenant inventory + onboarding/health metadata.
- Relevant fields (high level):
  - `id`, `external_id`, `name`, `workspace_id`, `status`, `environment`
  - RBAC signals: `rbac_last_checked_at`, `rbac_last_setup_at`, `rbac_canary_results`, `rbac_last_warnings`
  - `metadata` (array)
- Relationships:
  - `Tenant::providerConnections()` → `provider_connections`
  - `Tenant::permissions()` → `tenant_permissions`
  - `Tenant::auditLogs()` → `audit_logs`

### ProviderConnection (`provider_connections`)
- Purpose: connectivity + health-check metadata for external provider.
- Relevant fields: `provider`, `is_default`, `scopes_granted`, `last_health_check_at`, `metadata`.

### TenantPermission (`tenant_permissions`)
- Purpose: cached/recorded permission checks.
- Relevant fields: `key` (permission name), `status`, `details`, `last_checked_at`.

### OperationRun (`operation_runs`)
- Purpose: canonical operations observability record (non-negotiable per constitution).
- Relevant fields:
  - identity/scope: `workspace_id` (NOT NULL), `tenant_id` (nullable), `user_id` (nullable), `initiator_name`
  - lifecycle: `type`, `status` (`queued|running|completed`), `outcome` (`pending|succeeded|failed|canceled|…`)
  - audit UX: `summary_counts` (numeric-only keys), `failure_summary` (sanitized bounded array), `context` (sanitized/limited)
  - timing: `created_at`, `started_at`, `completed_at`

### AuditLog (`audit_logs`)
- Purpose: security/audit trail.
- Relevant fields: `workspace_id`, `tenant_id` (nullable), `actor_*`, `action`, `status`, `metadata` (sanitized), `recorded_at`.
- System console relevant actions (already emitted):
  - `platform.auth.login`
  - `platform.break_glass.enter|exit|expired`

## Derived/Computed concepts (new, no new table)

### Time window
- Enumerated: `1h`, `24h` (default), `7d`.
- Used for Control Tower and for failures/stuck scoping.

### “Stuck” run classification
- Definition: a run is “stuck” when:
  - `status=queued` and `created_at <= now() - queued_threshold_minutes` AND `started_at IS NULL`, OR
  - `status=running` and `started_at <= now() - running_threshold_minutes`
- Thresholds are configurable (v1):
  - `system_console.stuck_thresholds.queued_minutes`
  - `system_console.stuck_thresholds.running_minutes`

### Tenant/workspace health badge
- “Worst wins” aggregation over signals:
  - Tenant status (active/onboarding/archived)
  - Provider connection health/status
  - Permission status
  - Recent failed/stuck runs within the time window
- Display-only; does not mutate state.

## Validation rules
- Any operator-provided reason/note (break-glass, mark investigated): min length 5, max length 500.
- Filters: only allow known enum values (time window, run status/outcome).

## Storage/indexing plan (Phase 2 tasks will implement)
- `operation_runs`:
  - Indexes to support windowed queries and grouping:
    - `(workspace_id, created_at)`
    - `(tenant_id, created_at)`
    - optional: `(status, outcome, created_at)` and `(type, created_at)` depending on explain plans
- `audit_logs`:
  - `(action, recorded_at)` and `(actor_id, recorded_at)` for Access Logs filters

## Notes on data minimization
- Use `RunFailureSanitizer` + `SummaryCountsNormalizer` contracts.
- Avoid rendering raw `context` by default; when displayed, cap size and redact sensitive keys.
