# ADR 001 — Supplier Pulse Customer Operations Platform: Domain Model

**Status:** Accepted  
**Date:** 2026-04-07  
**Authors:** Platform Engineering

---

## Context

Supplier Pulse has a working Owner Portal with tenant creation, provisioning, branding, feature entitlements, and audit logging. The system supports a single tenant lifecycle (draft → provisioning → active → suspended → archived) and a single implicit tenant type (production).

To support a real go-to-market motion — including trials, demos, sandboxes, internal training environments, and professional customer operations — the domain model must be extended without breaking existing tenants.

---

## Decision

### 1. Tenant Type

Every tenant has a `tenant_type` that never changes after creation:

| Type | Purpose | Behavior |
|---|---|---|
| `production` | Real paying customer | Full enforcement, no auto-expiry |
| `trial` | Prospect in evaluation | Auto-expiry, grace period, convert to paid |
| `demo` | Showcase workspace | Demo data seeded, can be reset, no real users |
| `sandbox` | Technical evaluation | Full features, ephemeral, can be promoted |
| `internal` | Internal training / QA | All features enabled, no billing |

Tenant type is set at creation time via a **template** and is immutable after provisioning.

### 2. Tenant Lifecycle Status

The existing `cp_tenants.status` covers provisioning states. Operational lifecycle state is tracked in a separate `cp_subscriptions` table:

| Subscription Status | Meaning |
|---|---|
| `trialing` | Trial in progress, within trial window |
| `active` | Paid or internally active |
| `grace_period` | Trial expired, grace window before suspend |
| `trial_expired` | Trial ended, grace also ended — access blocked |
| `paused` | Manually paused (billing pause, operator hold) |
| `suspended` | Access blocked by operator or auto-policy |
| `cancelled` | Contract terminated |
| `archived` | Soft-deleted, data preserved |

The existing `cp_tenants.status` values (DRAFT, PROVISIONING, ACTIVE, SUSPENDED, ARCHIVED, PROVISIONING_FAILED) remain and represent the **provisioning/operational** state. The `cp_subscriptions.status` represents the **commercial/lifecycle** state. They are independent dimensions.

Enforcement: `requireActiveTenant()` checks both. A tenant with `cp_tenants.status = ACTIVE` but `cp_subscriptions.status = trial_expired` is blocked.

### 3. Tenant Templates

Templates are pre-configured starting points for tenant creation:

| Code | Type | Description |
|---|---|---|
| `automotive_supplier_standard` | production | Standard BMW supplier setup, all core modules |
| `customer_trial_standard` | trial | 14-day trial, full modules, demo data |
| `executive_demo_showcase` | demo | Seeded with showcase data, all features |
| `internal_training` | internal | Internal team, full access, no billing |
| `consulting_light` | production | Minimal plan, LSC + Assessment only |
| `sandbox_evaluation` | sandbox | Full features, 30-day auto-expiry |

Each template defines: tenant_type, default plan, module overrides, branding defaults, demo packs to apply, trial duration.

### 4. Demo Data Packs

Demo data is tracked in `cp_demo_seed_registry` at the control plane level. Each pack:

- Has a `pack_code` (e.g. `projektanlage`) and `pack_version`
- Is applied to exactly one tenant
- Is tracked as active until removed
- Records seeded_by, seeded_at, seed_metadata (record counts)
- Supports `deletion_group` for batch remove

Pack codes:
- `projektanlage` — supplier projects
- `kalender` — planning assignments
- `lsc_workshop` — shift tracking
- `fabrikanalyse` — factory analysis
- `oee` — OEE data
- `wertstrom` — value stream
- `berater` — demo Berater profiles
- `datenablage` — document repository

### 5. Berater (Platform-Level)

A `cp_berater_profiles` table holds control-plane Berater definitions used as demo-data sources. These are platform-managed employees/consultants. Tenant-level Berater (real users) continue to live in the tenant database.

### 6. Workspace Health

A `cp_workspace_health_snapshots` table captures periodic health checks per tenant. Used by the Workspace Health Center in the Owner Portal.

---

## Consequences

- Existing `cp_tenants` rows receive `tenant_type = 'production'` and `expires_at = NULL` as defaults.
- The `cp_subscriptions` row is auto-created on tenant creation (like `cp_branding_profiles`).
- All new enum values use lowercase snake_case (consistent with PostgreSQL conventions).
- `requireActiveTenant()` is updated to check subscription status in addition to provisioning status.
- The Owner Portal navigation is restructured to surface trials, demos, and sandboxes as first-class sections.
- Provisioning workflow gains an optional `seed_demo_data` step that respects the template's demo pack list.

---

## Rollout Order

1. Run SQL migration (`supabase/migrations/supabase-migration-platform-v2.sql`)
2. Deploy updated TypeScript types and enforcement
3. Enable template-based tenant creation
4. Enable subscription management API
5. Enable demo pack seeding/removal via Owner Portal
6. Enable Berater control-plane management
7. Enable workspace health monitoring
