You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Give event operators a reusable, versioned registration-question catalog that can be configured per event without code changes, while keeping every submitted answer and consent interpretable after later edits.
The MVP includes two deliberately different communication rules:
a prebuilt per-sponsor email-share consent that is optional, unchecked, never required, and exportable only when affirmatively accepted for the named sponsor;
automatic DataTalks.Club newsletter enrollment after verified event registration, with no newsletter question, only after Decision: Approve privacy ownership, retention, and minors policy #23 approves the exact notice, lawful basis, processor, retention, objection/unsubscribe, and re-enrollment rules.
This issue explicitly amends the old generic newsletter/marketing wording in specification 05 and #46: event registration has no configurable DataTalks.Club newsletter opt-in question. Sponsor sharing remains separate affirmative consent. Public event pages never expose attendee identities, cards, names, initials, avatars, profiles, directories, or lists. #112 may publish only a non-identifying exact aggregate registration count.
Question catalog and immutable versions
The events app owns a database-backed catalog:
EventRegistrationQuestion has a UUID, stable key, purpose, data classification, retention class, lifecycle, and current revision.
EventRegistrationQuestionVersion is immutable after publication and stores exact label, help text, type, validation, option keys/labels/order, and version number.
EventQuestionAssignment pins one published version to one Event plus order, active state, required state where allowed, assignment revision, and activation/deactivation timestamps.
A submitted EventRegistrationAnswer pins registration/version, assignment and question/version identifiers, the exact rendered question/option snapshot, canonical typed answer, and submission timestamp.
Question lifecycle is draft -> published -> archived. Editing a published question creates a new version; it never mutates old wording/options. Archiving or deactivating stops future use but preserves definitions and answers. Replacing an assignment after registrations exist requires revision/If-Match, reason, impact preview, and explicit confirmation; it affects future submissions only.
Question and option text is trimmed plain text, escaped on render, never Markdown/HTML, and never synchronously fetched. Stable option keys, not labels, are stored as canonical values; the answer snapshot retains the submitted labels for historical interpretation.
Safe code-owned question types
Type
Contract
short_text
One plain-text value; configured maximum 1–500 characters.
long_text
One plain-text value; configured maximum 1–2,000 characters.
single_select
Exactly one stable option key from 2–50 published options.
multi_select
Unique stable option keys from 2–50 options, with a configured maximum of 1–20 selections.
yes_no
Canonical boolean; ordinary answer, never consent by itself.
acknowledgement
Canonical boolean for a non-marketing statement; may be required, but cannot be relabeled or exported as consent.
Labels are 1–300 characters, help text at most 1,000, and option labels 1–200. Validation is code-defined; staff cannot supply regular expressions, HTML, executable expressions, arbitrary schemas, or client code. File upload, password/secret collection, payment, hidden tracking, conditional branching, repeating groups, and special-category/sensitive-data questions are out of MVP. A new type requires a reviewed code/schema/test change rather than arbitrary Studio configuration.
Ordinary questions may be required. Missing required answers fail before a pending registration is created; optional questions remain visibly optional. Duplicate, unknown, stale-version, disabled, or forged question/option identifiers fail safely and cannot mass-assign registration or consent fields.
Prebuilt named-sponsor email-share consent
A system-owned sponsor_email_share definition is available without custom coding. Each activation identifies exactly one reviewed sponsor record and immutable statement version. Multiple sponsors require multiple separately named unchecked choices; sponsors are never bundled.
The generated prompt names the sponsor and states that DataTalks.Club will share the registrant's email address with that sponsor for the approved purpose. The evidence pins:
event, registration/version, sponsor UUID and name snapshot;
statement/notice version and exact rendered text;
affirmative or non-affirmative value, submission timestamp, and source;
normalized email snapshot used for the approved disclosure.
This control is always optional, unchecked on every fresh form/render, and cannot be made required through Studio, API, import, query, or client payload. Silence, missing data, stale evidence, a generic agree-check, event/newsletter participation, or another sponsor's answer is not consent.
Sponsor disclosure export is scoped to one event, one named sponsor, and one statement version. It contains only affirmative, current evidence and the minimum fields: email, event identifier, sponsor name, consent timestamp, and statement version. It excludes declined/missing/revoked/stale consent, all other answers, attendee-display state, phone, employer, profile fields, and newsletter state. CSV is formula-safe; the encrypted artifact has bounded #23/#64 retention and requires explicit confirmation, reason, dedicated permission, and audit. Audit records actor/reason/scope/count/version, never email or answer text.
Automatic DataTalks.Club newsletter enrollment
There is no newsletter question in the catalog, event assignment, registration payload, answer table, or sponsor export. Operators cannot add, rename, precheck, or require one.
After #23 records an approved non-consent basis compatible with automatic enrollment, the registration page displays the exact versioned DataTalks.Club newsletter notice and unsubscribe/objection route. Successful email verification/registration confirmation atomically creates one durable NewsletterEnrollmentIntent; provider work occurs after commit and is idempotent by normalized address plus policy version. An existing subscriber is a no-op. Provider outage never rolls back registration.
A durable unsubscribe/objection/suppression state wins permanently over later event registration and cannot be reset by another event, replay, import, staff correction, or provider retry. Enrollment provenance stores the event/registration, notice and basis identifiers, requested/completed state, and safe timestamps, not a fabricated consent value. Newsletter provider credentials or raw payloads never enter the question/answer model, logs, audit, screenshots, or exports.
#23 is a hard activation dependency. Until it approves the exact basis, notice, processor, retention, minors handling, and unsubscribe/re-enrollment semantics, the system fails closed by completing event registration without creating or submitting a newsletter enrollment. If #23 concludes that affirmative consent is legally required, this automatic-enrollment requirement returns to the owner; engineers must not silently add a checkbox contrary to this issue.
Public and management flows
The question set renders on the internal event registration route /events/<slug>/register owned by #46. The base email/identity, versioned privacy acknowledgement, ordinary questions, and named-sponsor choices remain semantically distinct. There is no attendee-identity publication field or evidence. Verification, cancellation, enumeration resistance, and outbox behavior remain #46/#49 contracts.
GET/PATCH /api/v1/admin/event-registration-questions/<uuid> with If-Match;
GET/PUT /api/v1/admin/events/<uuid>/registration-questions with If-Match and Idempotency-Key;
GET /api/v1/admin/events/<uuid>/sponsor-opt-ins for bounded aggregate state;
POST /api/v1/admin/events/<uuid>/sponsor-opt-in-exports with Idempotency-Key, reason, and confirmation.
Register exact capability entries for events.registration_question.manage, events.registration_answer.view_pii, and events.sponsor_consent.export. Event operators may manage non-sensitive definitions/assignments; viewing answers or affirmative addresses requires the dedicated PII/export capability. Authorized querysets apply before lookup. Studio/admin API use the same events services, revision/idempotency rules, validation, results, redaction, and audit. Django admin remains break-glass only.
All form, answer, consent, export, Studio, and admin responses containing registration data are private/no-store/noindex and absent from sitemap/search/public serializers. Public event pages may receive only #112's exact aggregate registration total; no answer or attendee identity is public.
Migration and existing evidence
Existing event-registration privacy/marketing fields are preserved until an expand-and-contract migration classifies them. Generic legacy marketing/newsletter values are never reclassified as a named-sponsor share or DataTalks.Club newsletter consent. There is no attendee-publication purpose to infer.
#112 imports only provider/event aggregate totals and imports no legacy question definition, answer, acknowledgement, notice, consent, email, or other row-level evidence. Any future legacy-answer migration requires a separately groomed issue and cannot infer sponsor/newsletter consent or public disclosure.
Non-goals
No public answer/profile/directory, attendee search, answer-based personalization/ranking, CRM, arbitrary sponsor API delivery, direct sponsor email send, marketing campaign editor, or general survey builder.
No prechecked/bundled/required consent, inferred sponsor consent, generic marketing opt-in, or newsletter question.
Catalog definitions, immutable published versions, event assignments, and answer snapshots enforce the exact lifecycle, limits, types, ordering, revisions, and future-only edit semantics above.
Public registration renders the event's active pinned versions in order, enforces required/optional rules server-side, and rejects forged/stale/disabled/unknown fields without ghost registrations.
The built-in sponsor choice names exactly one sponsor, is always optional/unchecked/non-required, and stores immutable statement/sponsor/email evidence only when submitted.
Sponsor exports contain only affirmative current opt-ins for the exact event/sponsor/version, are minimum-field/formula-safe/private/bounded, and have permission, confirmation, idempotency, and redacted audit controls.
No generic/provider/legacy answer, newsletter state, attendance, or registration is inferred as sponsor consent or public disclosure.
DataTalks.Club newsletter enrollment has no question/answer field; after Decision: Approve privacy ownership, retention, and minors policy #23 approval it is notice/basis/unsubscribe governed, durable/idempotent after verification, provider-failure tolerant, and never overrides objection/unsubscribe/suppression.
Studio/admin API capability, service, validation, revision, idempotency, permission, audit, result, and OpenAPI parity covers every catalog/assignment/sponsor-export operation.
Focused Django, migration, security/privacy, management-parity, OpenAPI, compatibility, full event-registration, and core Playwright tests pass through repository uv/Make targets.
Templates remain readable/non-minified and WCAG 2.2 AA critical-flow behavior is verified at desktop/mobile.
Django, integration, and failure scenarios
Every type at empty/min/max/over-limit, whitespace/Unicode/control characters, duplicate/stale/unknown option, multi-select duplicates/count, forged ID/type/required flag, disabled/archive version, and mass assignment.
Draft/publish/revise/archive, assignment reorder/replace/deactivate before and after registrations, stale If-Match, duplicate Idempotency-Key, concurrent operator edits, and old-answer interpretation after wording/options change.
Sponsor absent/renamed/archived, one/multiple sponsors, unchecked/missing/false/true, attempted required/prechecked/bundled payload, stale statement, revocation/correction, export empty/one/many, denied/masked role, formula payload, replay, and audit/artifact canaries.
New/pending/verified/cancelled/reactivated registration, duplicate submit/back/refresh, provider/worker outage, subscriber already present, unsubscribe before/after registration, repeated events, suppression, processor error, and no Decision: Approve privacy ownership, retention, and minors policy #23-approved policy.
Export/deletion/retention/backup replay with shared email across events and sponsors; prove minimum disclosure and no answer/newsletter leakage.
Browser acceptance
At desktop about 1440x900 and mobile about 390x844 using synthetic identities:
Configure/reuse/reorder ordinary question types in Studio, preview the exact registration form, revise after one answer, and verify the old response remains interpretable.
Register with required/optional validation, preserved input, accessible error summary/focus, keyboard navigation, refresh/back/duplicate submit, and verification resume.
Activate one and then two named sponsors; prove each choice is visibly optional and fresh-unchecked, cannot be required, and one affirmative answer never authorizes the other.
As authorized/denied roles inspect masked state and create a confirmed sponsor export; verify it excludes negative/missing/other-answer rows and contains no unexpected PII.
Verify there is no DataTalks.Club newsletter question, the approved notice/unsubscribe copy is visible when configured, provider delay does not change registration success, and prior unsubscribe is honored.
Inspect desktop/mobile screenshots and browser history/network/console for responsive, non-error content with no email, answer, export URL, token, or other synthetic PII in artifacts.
Required specification and issue amendments
Implementation must update:
_docs/specs/05-events-registration-email.md — question/version/answer model, named-sponsor consent/export, and automatic DTC-newsletter exception to old generic marketing wording;
_docs/specs/06-studio-and-admin-api.md — exact routes, capabilities, masking, parity, and audit;
#46 and #47 are amended with cross-references: no public attendee identity, card, directory, list, search, or export is permitted; #112 may expose only an exact aggregate registered count. Sponsor consent is separate, and no generic newsletter question remains.
Source implementation and synthetic tests may be prepared before #23, but real enrollment/provider activation and real registration data remain disabled. No production or protected export data may enter the repository or issue evidence.
Outcome
Give event operators a reusable, versioned registration-question catalog that can be configured per event without code changes, while keeping every submitted answer and consent interpretable after later edits.
The MVP includes two deliberately different communication rules:
Product and architecture authority
This issue explicitly amends the old generic newsletter/marketing wording in specification 05 and #46: event registration has no configurable DataTalks.Club newsletter opt-in question. Sponsor sharing remains separate affirmative consent. Public event pages never expose attendee identities, cards, names, initials, avatars, profiles, directories, or lists. #112 may publish only a non-identifying exact aggregate registration count.
Question catalog and immutable versions
The
eventsapp owns a database-backed catalog:EventRegistrationQuestionhas a UUID, stable key, purpose, data classification, retention class, lifecycle, and current revision.EventRegistrationQuestionVersionis immutable after publication and stores exact label, help text, type, validation, option keys/labels/order, and version number.EventQuestionAssignmentpins one published version to one Event plus order, active state, required state where allowed, assignment revision, and activation/deactivation timestamps.EventRegistrationAnswerpins registration/version, assignment and question/version identifiers, the exact rendered question/option snapshot, canonical typed answer, and submission timestamp.Question lifecycle is
draft -> published -> archived. Editing a published question creates a new version; it never mutates old wording/options. Archiving or deactivating stops future use but preserves definitions and answers. Replacing an assignment after registrations exist requires revision/If-Match, reason, impact preview, and explicit confirmation; it affects future submissions only.Question and option text is trimmed plain text, escaped on render, never Markdown/HTML, and never synchronously fetched. Stable option keys, not labels, are stored as canonical values; the answer snapshot retains the submitted labels for historical interpretation.
Safe code-owned question types
short_textlong_textsingle_selectmulti_selectyes_noacknowledgementLabels are 1–300 characters, help text at most 1,000, and option labels 1–200. Validation is code-defined; staff cannot supply regular expressions, HTML, executable expressions, arbitrary schemas, or client code. File upload, password/secret collection, payment, hidden tracking, conditional branching, repeating groups, and special-category/sensitive-data questions are out of MVP. A new type requires a reviewed code/schema/test change rather than arbitrary Studio configuration.
Ordinary questions may be required. Missing required answers fail before a pending registration is created; optional questions remain visibly optional. Duplicate, unknown, stale-version, disabled, or forged question/option identifiers fail safely and cannot mass-assign registration or consent fields.
Prebuilt named-sponsor email-share consent
A system-owned
sponsor_email_sharedefinition is available without custom coding. Each activation identifies exactly one reviewed sponsor record and immutable statement version. Multiple sponsors require multiple separately named unchecked choices; sponsors are never bundled.The generated prompt names the sponsor and states that DataTalks.Club will share the registrant's email address with that sponsor for the approved purpose. The evidence pins:
This control is always optional, unchecked on every fresh form/render, and cannot be made required through Studio, API, import, query, or client payload. Silence, missing data, stale evidence, a generic
agree-check, event/newsletter participation, or another sponsor's answer is not consent.Sponsor disclosure export is scoped to one event, one named sponsor, and one statement version. It contains only affirmative, current evidence and the minimum fields: email, event identifier, sponsor name, consent timestamp, and statement version. It excludes declined/missing/revoked/stale consent, all other answers, attendee-display state, phone, employer, profile fields, and newsletter state. CSV is formula-safe; the encrypted artifact has bounded #23/#64 retention and requires explicit confirmation, reason, dedicated permission, and audit. Audit records actor/reason/scope/count/version, never email or answer text.
Automatic DataTalks.Club newsletter enrollment
There is no newsletter question in the catalog, event assignment, registration payload, answer table, or sponsor export. Operators cannot add, rename, precheck, or require one.
After #23 records an approved non-consent basis compatible with automatic enrollment, the registration page displays the exact versioned DataTalks.Club newsletter notice and unsubscribe/objection route. Successful email verification/registration confirmation atomically creates one durable
NewsletterEnrollmentIntent; provider work occurs after commit and is idempotent by normalized address plus policy version. An existing subscriber is a no-op. Provider outage never rolls back registration.A durable unsubscribe/objection/suppression state wins permanently over later event registration and cannot be reset by another event, replay, import, staff correction, or provider retry. Enrollment provenance stores the event/registration, notice and basis identifiers, requested/completed state, and safe timestamps, not a fabricated consent value. Newsletter provider credentials or raw payloads never enter the question/answer model, logs, audit, screenshots, or exports.
#23 is a hard activation dependency. Until it approves the exact basis, notice, processor, retention, minors handling, and unsubscribe/re-enrollment semantics, the system fails closed by completing event registration without creating or submitting a newsletter enrollment. If #23 concludes that affirmative consent is legally required, this automatic-enrollment requirement returns to the owner; engineers must not silently add a checkbox contrary to this issue.
Public and management flows
The question set renders on the internal event registration route
/events/<slug>/registerowned by #46. The base email/identity, versioned privacy acknowledgement, ordinary questions, and named-sponsor choices remain semantically distinct. There is no attendee-identity publication field or evidence. Verification, cancellation, enumeration resistance, and outbox behavior remain #46/#49 contracts.Studio surfaces:
GET/POST /studio/events/questions/— list/create reusable questions;GET/POST /studio/events/questions/<uuid>/— inspect/revise/publish/archive versions;GET/POST /studio/events/<uuid>/registration-questions/— preview/order/activate/deactivate pinned versions;GET/POST /studio/events/<uuid>/sponsor-opt-ins/— inspect aggregate consent state and confirm an affirmative-only export.Admin API parity:
GET/POST /api/v1/admin/event-registration-questions;GET/PATCH /api/v1/admin/event-registration-questions/<uuid>withIf-Match;GET/PUT /api/v1/admin/events/<uuid>/registration-questionswithIf-MatchandIdempotency-Key;GET /api/v1/admin/events/<uuid>/sponsor-opt-insfor bounded aggregate state;POST /api/v1/admin/events/<uuid>/sponsor-opt-in-exportswithIdempotency-Key, reason, and confirmation.Register exact capability entries for
events.registration_question.manage,events.registration_answer.view_pii, andevents.sponsor_consent.export. Event operators may manage non-sensitive definitions/assignments; viewing answers or affirmative addresses requires the dedicated PII/export capability. Authorized querysets apply before lookup. Studio/admin API use the same events services, revision/idempotency rules, validation, results, redaction, and audit. Django admin remains break-glass only.All form, answer, consent, export, Studio, and admin responses containing registration data are private/no-store/noindex and absent from sitemap/search/public serializers. Public event pages may receive only #112's exact aggregate registration total; no answer or attendee identity is public.
Migration and existing evidence
Existing event-registration privacy/marketing fields are preserved until an expand-and-contract migration classifies them. Generic legacy marketing/newsletter values are never reclassified as a named-sponsor share or DataTalks.Club newsletter consent. There is no attendee-publication purpose to infer.
#112 imports only provider/event aggregate totals and imports no legacy question definition, answer, acknowledgement, notice, consent, email, or other row-level evidence. Any future legacy-answer migration requires a separately groomed issue and cannot infer sponsor/newsletter consent or public disclosure.
Non-goals
Acceptance criteria
uv/Make targets.Django, integration, and failure scenarios
If-Match, duplicateIdempotency-Key, concurrent operator edits, and old-answer interpretation after wording/options change.Browser acceptance
At desktop about 1440x900 and mobile about 390x844 using synthetic identities:
Required specification and issue amendments
Implementation must update:
_docs/specs/05-events-registration-email.md— question/version/answer model, named-sponsor consent/export, and automatic DTC-newsletter exception to old generic marketing wording;_docs/specs/06-studio-and-admin-api.md— exact routes, capabilities, masking, parity, and audit;_docs/specs/07-security-privacy-operations.md— purposes/bases, answer/consent PII, unsubscribe, export/retention/deletion rules;_docs/specs/09-migration-rollout-roadmap.md— expand-and-contract and Luma evidence precedence;_docs/specs/10-verification-strategy.md— form/version/consent/newsletter/browser/security matrices;_docs/specs/open-decisions.md— record the owner-selected DTC newsletter behavior as conditional on Decision: Approve privacy ownership, retention, and minors policy #23 approval.#46 and #47 are amended with cross-references: no public attendee identity, card, directory, list, search, or export is permitted; #112 may expose only an exact aggregate registered count. Sponsor consent is separate, and no generic newsletter question remains.
Dependencies and delivery
Source implementation and synthetic tests may be prepared before #23, but real enrollment/provider activation and real registration data remain disabled. No production or protected export data may enter the repository or issue evidence.