---
apiVersion: data-spec/v1
id: campaignmember-status-runtime-picklist-20260422
description: "Configure CampaignMember.Status to resolve options from CampaignMemberStatus metadata at runtime"
issue: "#1523"
created: 2026-04-22
scope: production-only
status: pending
tables: [_System_Field]
depends_on: [campaign-default-member-statuses-flow-20260421, campaignmemberstatus-backfill-empty-campaigns-20260422]
---

# CampaignMember Status Runtime Picklist

## Summary

Repairs the runtime field metadata for `campaignmember.Status` so the form no
longer consumes the org-wide static `Options` JSON seeded in `_System_Field`.
Instead, the frontend now reads generic runtime-picklist metadata from the
field definition and queries `CampaignMemberStatus` rows filtered by the
current `CampaignId`.

Safe rollout depends on the paired forward-fill and backfill specs:

- `campaign-default-member-statuses-flow-20260421` seeds new Campaign rows
- `campaignmemberstatus-backfill-empty-campaigns-20260422` heals existing Campaign rows with zero statuses

This keeps the fix metadata-driven:

- code only implements a generic runtime-picklist contract
- object-specific ownership stays in `_System_Field`
- no UI or backend object special-case is required

## Changes

### 1. Mark `campaignmember.Status` as a runtime-scoped picklist

```sql
UPDATE _System_Field f
JOIN _System_Object o ON o.Id = f.ObjectId
SET
  f.Validator = 'runtime_scoped_picklist',
  f.ControllingField = 'CampaignId',
  f.ReferenceTo = JSON_ARRAY('CampaignMemberStatus'),
  f.LastModifiedDate = CURRENT_TIMESTAMP
WHERE LOWER(o.ApiName) = 'campaignmember'
  AND f.ApiName = 'Status'
  AND f.IsDeleted = 0
  AND (
    COALESCE(f.Validator, '') <> 'runtime_scoped_picklist'
    OR COALESCE(f.ControllingField, '') <> 'CampaignId'
    OR COALESCE(f.ReferenceTo, '') <> JSON_ARRAY('CampaignMemberStatus')
  );
```

**Idempotency**: Re-running the spec converges the field to the same validator,
controller, and source-object metadata and leaves already-correct rows
unchanged.

## Verification

```sql
SELECT
  o.ApiName AS ObjectApiName,
  f.ApiName AS FieldApiName,
  f.Type,
  f.Validator,
  f.ControllingField,
  f.ReferenceTo
FROM _System_Field f
JOIN _System_Object o ON o.Id = f.ObjectId
WHERE LOWER(o.ApiName) = 'campaignmember'
  AND f.ApiName = 'Status'
  AND f.IsDeleted = 0;
```

Expected:

- `Type = picklist`
- `Validator = runtime_scoped_picklist`
- `ControllingField = CampaignId`
- `ReferenceTo = ["CampaignMemberStatus"]`

## Rollback

```sql
UPDATE _System_Field f
JOIN _System_Object o ON o.Id = f.ObjectId
SET
  f.Validator = NULL,
  f.ControllingField = NULL,
  f.ReferenceTo = NULL,
  f.LastModifiedDate = CURRENT_TIMESTAMP
WHERE LOWER(o.ApiName) = 'campaignmember'
  AND f.ApiName = 'Status'
  AND f.IsDeleted = 0;
```
