# Klaviyo Profile and Event Sync

## Objective

Sync customer profiles and behavioural events to Klaviyo for segmentation and lifecycle mail.

Two related streams: profile attributes kept current, and product events sent with stable identifiers for segmentation.

## Before You Begin

This feature is being added to an application that already exists and already
works. Do not scaffold a new project, and do not assume a blank slate.

Inspect the codebase first and establish:

- The existing application structure and where code of this kind already lives.
- The framework and version in use.
- The existing design system — colours, spacing, typography, and component conventions.
- Existing UI components you can reuse instead of writing new ones.
- The existing database structure, if this feature needs to persist anything.
- The existing authentication and authorization system, if this feature is user-scoped.
- Dependencies already installed, so you don't add a library that duplicates one.
- The existing test setup and conventions.

Only start writing code once you understand the above. If the application
already implements part of this feature, extend it rather than replacing it.

## Implementation Instructions

1. Keep profiles and events as two distinct streams with two distinct mappings. A profile attribute describes the person's current state, an event describes something that happened at a time — mixing them produces segments that cannot be reasoned about.
2. Give every event a stable identifier derived from the source record and the occurrence, and send the time the event actually happened rather than the time the job ran, so retries and delayed jobs do not distort metrics.
3. Track email consent and SMS consent as independent values with their own source and timestamp, and never let a change to one imply anything about the other.
4. Define an allowlist of the profile attributes and event properties that may leave the app, and send nothing outside it. Free-text fields, internal notes, payment details, and anything resembling special-category data stay in the app.
5. If the app already syncs to another email provider, decide in one place which system is authoritative for consent and let this brief read that decision. This feature owns the event stream; it must never mutate subscription state as a side effect of an event.

## UI and UX Requirements

Match the application's existing design system exactly. Reuse its components,
spacing, and typography. This feature should look like it was always there.

## Responsive Requirements

Works on mobile, tablet, and desktop. Touch targets are large enough to hit on a
phone, and nothing overflows horizontally at 320px.

## Accessibility Requirements

- Fully keyboard navigable.
- Correct semantic elements and ARIA roles.
- Visible focus states.
- Meets WCAG AA contrast.
- Dynamic changes are announced to screen readers.
- Respects prefers-reduced-motion.

## Edge Cases

- Retried jobs and redelivered webhooks resend the same event. Without a stable per-occurrence identifier, a single purchase is counted three times and every revenue segment built on it is wrong.
- Profile attributes and event properties serve different purposes and must not be merged. Writing an event's details onto the profile overwrites the person's current state with a snapshot of one moment, and it cannot be undone from the app.
- Email and SMS consent are separate permissions with separate legal footing. Treating one as implying the other sends messages a customer never agreed to receive.
- Anonymous activity collected before signup has to attach to the known profile afterwards without duplicating history or claiming a shared device's activity for the wrong person. Link only on an explicit, verified identification, and leave it anonymous when unsure.
- Event payloads are the easiest place for sensitive data to escape, because they are assembled ad hoc from whatever the code has to hand. Allowlist the properties, and review what a new event type carries before it ships.
- Backfills exhaust rate limits quickly. Batch and throttle historical sends, keep them behind live traffic in priority, and make a backfill resumable from where it stopped.
- Tokens expire and connections are revoked. Hold events in the queue, mark the connection broken in the app, and resume from the held queue rather than dropping a period of history.
- When the provider is unreachable, the user-facing action must still succeed. Queue the profile update and the event, and never present the outage to the customer.

## Testing

Exercise the feature end to end in the running application. Cover every edge case
above, then run the existing test suite and confirm nothing regressed.

## Acceptance Criteria

- [ ] Every event carries a stable identifier and its real occurrence time, and resends do not inflate counts.
- [ ] Profile attributes and event properties are mapped and written separately.
- [ ] Email and SMS consent are stored, sent, and changed independently.
- [ ] Anonymous activity links to a known profile only on verified identification.
- [ ] Only allowlisted attributes and properties leave the app.
- [ ] Backfills are throttled, resumable, and lower priority than live traffic.
- [ ] The event stream never changes subscription state.
- [ ] The feature matches the existing design system.
- [ ] No existing functionality is broken.

## Adaptation Rules

- Match the existing design system. Do not introduce a new colour palette,
  spacing scale, or component library.
- Reuse existing components and utilities wherever they fit.
- Follow the naming, file layout, and code style already present.
- Do not upgrade, replace, or remove existing dependencies to make this
  feature fit. Adapt the feature to the app, not the app to the feature.
- Do not break existing functionality. If a change is genuinely required in
  existing code, make the smallest one that works and say so.
- If something in these instructions conflicts with how the application is
  built, follow the application and explain the deviation.

## Final Verification

Before you report the work as done:

1. Re-read the acceptance criteria above and check each one against what you
   actually built.
2. Run the application and exercise the feature end to end.
3. Run the existing test suite and confirm you have broken nothing.
4. Check the feature on mobile, tablet, and desktop widths.
5. Check keyboard navigation and focus handling.
6. Summarize what changed: files added, files modified, and anything you
   deliberately did differently because of how this application is built.

If any acceptance criterion is unmet, fix it before reporting completion.
