# Direct Messages

## Objective

Let two users hold a private conversation inside the app instead of leaving to email.

One-to-one private threads with message history, per-conversation unread counts, and blocking.

## 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. Build the conversation list, the thread view, and the unread badge. Keep the first version to one-to-one threads only and defer group conversations, reactions, threading, and file attachments rather than shipping all of them half-finished.
2. Reuse the app's existing Read and Unread State and Notification Center rather than inventing a second unread mechanism and a second alert path. A message notification should sit alongside every other notification the user gets.
3. Reuse the composer, mention handling, and rendering rules from Comments and Mentions instead of writing a second message editor with subtly different escaping.
4. Assign message ordering from the server, with a monotonic sequence per conversation, and use client timestamps only for display. Sorting by client clock produces conversations that read out of order.
5. Do not deliver a message to someone who has blocked the sender, and do not tell the sender they have been blocked. Accept the send, show it in their own thread, and stop it at delivery.

## 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

- Device clocks disagree by minutes. Order messages by a server-assigned sequence within the conversation so a reply never appears above the message it answers.
- A user with the app open on a phone and a laptop must see the unread count drop on both when they read a thread on either. Read state belongs to the conversation and the user, not to a device.
- Blocking must stop delivery in both directions, hide the existing thread from the blocker's list, and survive the blocked user creating a new conversation to get around it.
- Loading older messages must keep the visible message under the user's eye in place. Anchoring to scroll offset instead of to a message will jump the view every time a page loads.
- When an account is deleted, decide explicitly whether the other participant keeps the history with an anonymised sender or loses the thread, and apply that consistently rather than leaving orphaned rows.
- A message sent while offline must be queued and shown as pending, then confirmed or clearly failed, never silently dropped.
- Private messages are a harassment vector. Provide a report action on a conversation that feeds the existing Content Reporting flow, and rate-limit new conversations started by new accounts.
- Message bodies must be escaped on output and links treated as untrusted. A pasted script in a private message is still a stored cross-site scripting hole.

## 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

- [ ] Two users can hold a private thread with history that persists across sessions.
- [ ] Message order is determined by a server-assigned sequence and reads correctly regardless of device clocks.
- [ ] Unread counts are consistent across every device the user has open.
- [ ] Blocking stops delivery in both directions and cannot be circumvented by starting a new conversation.
- [ ] Paginating older messages preserves the reader's scroll position.
- [ ] Account deletion has a defined, consistently applied effect on existing threads.
- [ ] Conversations can be reported through the app's existing reporting flow.
- [ ] 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.
