Skip to content

Chat Thread Identity and History Plan

Plan to unify sessions into user threads, improve chat-log grouping, and persist shopper history in widget.

Make chat behavior user-centric instead of session-centric:

  • One shopper should map to one ongoing conversation thread (not many fragmented sessions).
  • Chat logs should show one row per shopper thread, ordered by most recent activity.
  • Widget should restore previous messages so shopper context is visible when reopening chat.
  • Keep identity isolation safe so one browser cannot read another browser’s thread.

Use thread identity (stable per shopper) rather than creating a new session for every open.

Identity priority:

  1. Browser identity key (apfc_user_id) -> primary key for thread continuity
  2. Known profile fields from pre-chat (apfc_identity_v1: name/email/phone) -> metadata only
  3. Fallback anonymous only when no other signal exists

Result:

  • Reuse existing active thread for the same identity
  • Append new messages to that thread
  • Only create a new thread when identity is truly new or thread is explicitly reset
  • One accordion row per thread identity (not per raw session id)
  • Sorted by latest message timestamp (descending)
  • Row header includes:
    • display name/email (or Guest label)
    • last message datetime
    • message count
  • Expanding row shows full conversation timeline
  • Keep server-side history (source of truth)
  • Also keep local cached history in browser so shopper sees prior context instantly when reopening
  • On widget open:
    1. read local cache for quick render
    2. fetch authoritative thread history from server
    3. reconcile/replace with server data

  • Keep browser apfc_user_id as the thread identity key.
  • Store normalized visitorName, visitorEmail, and visitorPhoneNumber as profile metadata on that thread/session.
  • Do not merge separate browser identities by email alone (no verification).

Avoid IP as a primary unique identifier (IP can be shared/rotating, privacy concerns).

Recommended:

  • Generate persistent browser apfc_user_id (UUID) in localStorage
  • Send it on every chat request
  • Optional: store IP hash server-side as diagnostic signal only (not primary key)

If IP is still required in development:

  • Use it as a secondary merge hint only
  • Never as sole identity in production logic

Use explicit ChatThread model.

  • id
  • shopId
  • identityKey (apfc_user_id value, unique per shop)
  • identityType (known | guest, promoted when user fills pre-chat form)
  • displayName, displayEmail, visitorPhoneNumber (nullable, filled from pre-chat form)
  • lastMessageAt (updated after every assistant reply)
  • createdAt, updatedAt

ChatSession has a threadId FK → one thread, many sessions (one per visit/page-load).

ChatMessage has both sessionId (for visit context) and threadId (direct ref for fast history queries).

Constraints/indexes:

  • unique composite index on (shopId, identityKey) — prevents duplicate threads
  • index on (shopId, lastMessageAt DESC) — for chat-log sorting
  • index on (threadId, createdAt DESC) on ChatMessage — for fast history fetch

In chat API (/api/chat / generateChatReply flow):

  1. Build normalized identityKey from request (apfc_user_id primary)
  2. Resolve existing thread/session by (shopId, identityKey)
  3. Reuse thread/session if found; else create one
  4. Save profile metadata (visitorName, visitorEmail, visitorPhoneNumber) when provided
  5. Save user and assistant messages against this thread/session
  6. Update lastMessageAt on every write
  7. Wrap create-or-reuse in transaction/upsert pattern to avoid duplicate thread creation on concurrent requests

Add endpoint:

  • GET /api/chat/history?shop=<shopDomain>&uid=<apfc_user_id>
  • Returns latest 50 messages for widget history rehydrate
  • Identity is derived by looking up (shopId, uid) server-side — client cannot request another user’s history without knowing both shop and uid
  • Returns { messages: [{ id, role, text, createdAt }] }

  1. Persist apfc_user_id in localStorage for anonymous users
  2. Persist chat history cache (last N messages) in localStorage
  3. On open:
    • render cached history immediately
    • fetch server history and reconcile
  4. On each send/receive:
    • append to UI
    • update local cache
  5. Persist latest submitted profile snapshot in localStorage (apfc_identity_v1) for UX continuity only (not cross-browser identity merge)
  6. Gracefully handle blocked/cleared storage (private mode, cookie restrictions) by regenerating apfc_user_id

Add “Reset chat” control:

  • Clears local cache + rotates apfc_user_id + starts new thread if user wants clean context
  • Keep old thread in DB for audit unless admin/deletion flow removes it

In app.chat-logs.jsx:

  • Loader should return thread-grouped results:
    • one row per identityKey/thread
    • include lastMessageAt, messageCount, identity labels
  • Sort rows by lastMessageAt DESC
  • Header text should reference customer/thread count (not raw sessions)

Full wipe rollout (fixed):

  1. Deploy schema + code for thread identity model.
  2. Stop traffic in dev/staging environment.
  3. Truncate legacy chat tables (ChatMessage, ChatSession, and related chat usage rows if needed for consistency).
  4. Resume traffic with clean ChatThread-based flow.
  5. Run smoke tests: new guest thread, returning same-browser thread reuse, chat logs ordering, history endpoint rehydrate.

  1. Identity collisions (guest)
    • Use browser UUID as primary guest key, not IP alone
    • Add DB uniqueness on (shopId, identityKey) and transactional upsert for race safety
  2. Cross-device continuity for guests
    • expected limitation unless known identity is provided
  3. Stale local cache
    • always reconcile with server history on widget open
  4. No cross-device auto-merge
    • same person on a different device appears as a different customer unless verification is added later
  5. Privacy concerns
    • avoid storing raw IP; if needed, hash and limit retention
  6. Unauthorized history reads
    • never expose direct identityKey lookups without shop/auth scoping

  • Identity key generation (apfc_user_id)
  • Reuse thread/session by identity
  • Update chat logs sort by latest message
  • Widget local cache
  • Server history endpoint + rehydrate on open
  • Legacy migration/wipe
  • Reset chat control
  • Additional observability and QA

These decisions are now fixed for implementation:

  • Guest identity: browser apfc_user_id is primary; IP is secondary hint only.
  • Known profile fields: name/email/phone are metadata (visitorPhoneNumber included), not merge keys.
  • Thread policy: thread is long-lived/forever.
  • Widget history load size: latest 50 messages (user + AI).
  • Data migration: perform full chat data wipe before rollout in dev/staging.

Implementation should now proceed without additional product-level blockers.