consent-lifecyclelisted
Install: claude install-skill Canhada-Labs/ceo-orchestration
# Consent Lifecycle
## Cardinal Rule
**Consent is a state machine with append-only events, not a boolean
on a user row.** Every grant, revoke, expire, and re-up is a separate
event. The *current state* is derived from the event stream. Deriving
vs. mutating is the difference between passing an audit and losing it.
## The minimum consent event schema
```sql
CREATE TABLE consent_events (
id bigserial PRIMARY KEY,
user_id uuid NOT NULL,
purpose text NOT NULL, -- 'marketing', 'analytics', 'third_party_share:<vendor>', ...
state text NOT NULL, -- 'granted' | 'revoked' | 'expired' | 'renewed'
legal_basis text NOT NULL, -- 'consent' | 'legitimate_interest' | 'legal_obligation' | ...
source text NOT NULL, -- 'signup' | 'preference_center' | 'admin_override' | 'dsr' | 'ttl_expire'
actor_id uuid, -- who made the change (user themselves, admin, system)
created_at timestamptz NOT NULL DEFAULT now(),
expires_at timestamptz, -- NULL for indefinite
evidence jsonb NOT NULL, -- IP, user-agent, signed ToS hash, form version, etc.
previous_event_id bigint REFERENCES consent_events(id)
);
CREATE INDEX ON consent_events (user_id, purpose, created_at DESC);
```
Rules:
1. **Append-only.** `UPDATE consent_events` is forbidden. Corrections
create a new event with `source = 'correction'` referencing the
incorrect one via `previous_event_id`.