···11+// Schema for the `metadata` jsonb column on `publication_email_subscribers`.
22+//
33+// The column is nullable — most rows have no metadata at all (organic
44+// signups). Within each variant, only the `source` discriminator is
55+// required; every other field is optional because different writers
66+// populate different subsets.
77+88+export type CsvImportProvenance = {
99+ source: "csv_import";
1010+ imported_at?: string;
1111+ // Other CSV columns that the import script didn't consume directly
1212+ // (e.g. ip_address, signup_source) — kept verbatim so we don't lose
1313+ // information from the source list. The subscription date is not
1414+ // stored here; it lives on `created_at`/`confirmed_at`.
1515+ csv_columns?: Record<string, string>;
1616+};
1717+1818+// Discriminated union of known subscriber-row metadata shapes. Extend as
1919+// new sources start writing the column.
2020+export type SubscriberMetadata = CsvImportProvenance;
2121+2222+// Column-level type — most rows are null.
2323+export type SubscriberMetadataColumn = SubscriberMetadata | null;
···11+-- Provenance metadata on email subscribers (e.g. csv_import, imported_at,
22+-- original_subscription_date). The events table already has its own
33+-- per-event metadata column; this one tags the subscriber row itself so
44+-- "where did these subscribers come from" is a one-table query.
55+66+alter table "public"."publication_email_subscribers"
77+ add column "metadata" jsonb;