Skip to content

Populate internal event pages and replace Luma registration CTAs #127

Description

@alexeygrigorev

Outcome

Visitors can read the real event description at the canonical internal detail page and start registration at the canonical internal registration page. No public page, metadata, feed, or structured data sends a visitor to Luma. Luma is a reviewed one-time migration source only; it is never a runtime dependency or a second event system.

This is the smallest P0 content-and-integration slice. It does not create the Event model or registration lifecycle and it does not redesign the event surfaces.

Normative contracts

If this issue and a normative specification differ, the specification wins unless the owner approves a decision change.

Required dependencies and gates

Blocked: do not implement or activate this issue yet.

#112 is an independent, already-present aggregate-only overlay and must remain unchanged. #130 owns event/homepage visual composition and CSS; #127 owns migrated body content, link/CTA behavior, and integration with the registration form. Neither issue depends logically on the other, but the second implementation must rebase on the first accepted template work rather than overwrite it.

Source baseline

The reviewed local source is /home/alexey/tmp/luma-exporter/luma-events from exporter commit a0ede3c1451d34c5bc99acf58459ef1c65ae51df:

  • 168 checkpoint JSON files and 168 matching, non-empty Markdown descriptions;
  • public-event allowlist digest b2859a7be33fb29821d53e25265ce91c37f376f1de7467780a351d707294fbc5;
  • description-manifest digest 6ffd000db03b71c7308d83328a5d744ccec298b1cca458aa04831689431b030a.

These digests cover only the reviewed public-safe projection, never checkpoint guest arrays or CSV identity data. The source directory is local operator input; it is not copied wholesale, committed, read during requests/builds, or contacted over the network.

Source precedence and reconciliation

  1. The accepted Implement the event lifecycle, public pages, people, and calendar contracts #45 Event import owns canonical Event UUID, slug/path, lifecycle, title, time/timezone, type, speakers, and existing useful recording links for records already known to the website.
  2. The reviewed Luma snapshot owns candidate Markdown description content and migration provenance only. It may suggest source registration state but cannot silently replace canonical identity, lifecycle, title, or time.
  3. Produce a deterministic review manifest that classifies every snapshot event as match, create, or exclude, names the canonical Event slug for every match/create, and records a bounded reason. Exact normalized source URL/ID or exact title plus instant may generate a candidate; fuzzy matching never writes or activates data. Every candidate is reviewed.
  4. Source-only current events cannot be silently dropped. They are created only from the strict public allowlist after review, with a stable non-colliding slug, or excluded with an explicit reason. Existing projection-only events remain valid. In particular, do not invent a body or relationship for a projection event absent from the source.
  5. Conflicting title, time, slug, duplicate mapping, or one source event targeting multiple canonical events fails the candidate run for review. No last-write-wins behavior is allowed.
  6. After activation, the database Event and the shared Implement the event lifecycle, public pages, people, and calendar contracts #45 application services are authoritative. Studio and the admin API edit the same revisioned content. A future exporter snapshot creates another dry-run/review candidate; it never overwrites operator edits automatically.

Public-safe import boundary

Add a network-free, explicitly invoked, idempotent migration command using the project uv workflow. It must support dry-run/report and explicit apply, validate the source baseline/schema, preflight the complete candidate, and apply atomically through #45 services. A failed or stale run changes nothing. Reapplying the same reviewed candidate is a no-op; rollback uses the project revision/activation mechanism rather than deleting history.

Only allowlisted public event fields and the matching Markdown description may cross the boundary. Raw checkpoint files, guests, attendee/guest CSVs, names/emails/answers, meeting or join URLs, access codes, private organizer/contact fields, API data, tokens, filenames/paths tied to attendee evidence, and whole-checkpoint hashes must never enter repository fixtures, database rows, reports, logs, metrics, screenshots, or rendered output. Public speaker identities already approved by the canonical Event record are unaffected; source text that appears to contain a private identity or access secret fails review rather than being guessed safe.

The command must reject unreadable/missing input, unexpected schema, invalid UTF-8, oversized content, duplicate basenames/IDs, traversal or symlink escape, unsafe fields, and partial description/checkpoint sets with a bounded non-sensitive error.

Markdown and link policy

  • Parse Markdown through the project's allowlisted sanitizer. Raw HTML, unsafe schemes/attributes, scripts, embeds, forms, tracking pixels, and unsafe remote images fail or are removed according to one tested policy; no source HTML is trusted.
  • Rewrite known datatalks.club links to exact canonical routes. Rewrite courses.datatalks.club only through an explicit accepted compatibility map. A missing or ambiguous internal target blocks activation rather than producing a broken link.
  • Classify every luma.com/lu.ma destination. A mapped event-detail link becomes /events/<slug>; a mapped registration-intent link becomes /events/<slug>/register; obsolete/unmapped Luma links are explicitly removed or block review. No Luma URL survives in rendered HTML, attributes, metadata, JSON-LD, feeds, sitemap material, or stored public content.
  • Do not hotlink Luma CDN assets. A reviewed image is imported as a checksum-addressed local asset with required accessible metadata, or omitted with a visible review warning.
  • Preserve safe, useful non-registration destinations such as recordings. This issue does not broadly migrate or reclassify historical Eventbrite data, but no external destination may be selected as the current primary registration CTA.

Visitor behavior

Studio and admin API parity

Imported description, lifecycle-derived availability, source mapping summary, and revision state are managed through #45's shared services. Studio and the admin API expose equivalent authorized operations, validation, optimistic conflict behavior, audit events, and safe errors; public views are read-only consumers. Provenance is management-only and redacted. Raw source payloads and attendee data are never returned.

Registration record operations, exports, attendance, bulk messaging, and provider diagnostics remain owned by #46/#47/#49 and are not added here.

Acceptance criteria

  • A dry run classifies all 168 source event/description pairs and every source-only current event; every row has one reviewed match, create, or exclude decision and no fuzzy or unclassified write path.
  • Apply is all-or-nothing, repeatable, and network-free. Missing/stale/malformed/unsafe source, mapping conflicts, and invalid links produce bounded errors with no writes or private data disclosure.
  • Only public-safe allowlisted values enter Event revisions. Automated canary tests prove guest/CSV/private checkpoint values cannot cross into fixtures, storage, logs, reports, APIs, pages, or screenshots.
  • Every activated matched/created Event renders its reviewed sanitized description at /events/<slug>; an unmatched or intentionally empty Event renders the accepted fallback without fabricated content.
  • The public crawl and stored activated body/link fields contain zero luma.com, lu.ma, Luma CTA, or Luma CDN destination. Known internal links resolve directly to canonical paths; useful recording links still work.
  • Every eligible event's sole primary registration CTA targets /events/<slug>/register; unavailable events expose no actionable registration CTA and accurately state why.
  • Implement accountless verified event registration and cancellation #46's anonymous registration, uniform submission, verification/management, CSRF, rate-limit, honeypot, token, and email-failure regression suite passes through the CTA journey. Decision: Confirm the MVP transactional email purpose catalog #22/Decision: Approve privacy ownership, retention, and minors policy #23 gates are approved before live activation.
  • Studio and admin API edits affect the same canonical Event revision and public view, have permission/parity/conflict/audit tests, and never expose raw migration or attendee data.
  • Import historical registration totals safely and show aggregate event counts #112's complete-total, omitted-total, no-identity, zero-store, and no-double-counting tests remain unchanged and pass. No historical aggregate import sends mail or creates a native registration.
  • Templates remain readable and non-minified. The accepted Align the homepage and events with the adopted CMP visual system #130 composition, if already present, is preserved; this issue adds no independent visual redesign.
  • Full Django/unit/integration/link/security tests pass via uv-backed commands, with no unexplained route, canonical, metadata, cache, sitemap, accessibility, or existing recording-link regression.

Browser acceptance scenarios

Verify at web.dtcdev.click at desktop (1440px) and mobile (390px), in light and dark modes where #130 supplies them. Screenshots must use synthetic addresses and contain no source/private registration data.

  1. Upcoming eligible event: enter from homepage and /events; both link to the internal detail. Confirm meaningful description hierarchy, safe links, exact canonical, internal Register CTA, readable form, keyboard focus, labels/errors, and the uniform submitted state.
  2. Completed event with recording: confirm description and recording remain usable, state is truthful, and there is no registration CTA.
  3. Cancelled/closed/unpublished/missing event: confirm the accepted closed state or non-enumerating 404, no stale CTA, no private cacheability, and no existence/registration leak.
  4. No source description: confirm the compact fallback and absence of invented content.
  5. Link edge cases: confirm an old internal URL reaches the exact canonical directly, a mapped Luma registration link is internal, unsafe/unknown links cannot activate, and the rendered DOM/source has no Luma URL.
  6. Registration failures: invalid input, duplicate submit, throttling, Relay unavailable, and verification/cancellation link edge cases retain Implement accountless verified event registration and cancellation #46's accessible, enumeration-resistant, no-store behavior and do not lose committed business state.
  7. Management parity: with authorized and unauthorized staff roles, edit/publish/revert the description through Studio and the API and confirm equivalent validation, conflict, audit, denial, and public activation behavior.

Non-goals

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0Must-have or release-blockingaccessibilityArea: accessibilityadminArea: admincontentArea: contentdata-migrationArea: data-migrationenhancementNew feature or requesteventsArea: eventsfrontendArea: frontendintegrationArea: integrationsecurityArea: securityseoArea: seotestingArea: testing

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions