Chat Thread Identity and History Plan
Plan to unify sessions into user threads, improve chat-log grouping, and persist shopper history in widget.
Chat Thread Identity and History Plan
Section titled “Chat Thread Identity and History Plan”1) Goal
Section titled “1) Goal”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.
2) Proposed product behavior
Section titled “2) Proposed product behavior”A. Threading model
Section titled “A. Threading model”Use thread identity (stable per shopper) rather than creating a new session for every open.
Identity priority:
- Browser identity key (
apfc_user_id) -> primary key for thread continuity - Known profile fields from pre-chat (
apfc_identity_v1: name/email/phone) -> metadata only - Fallback
anonymousonly 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
B. Chat logs page behavior
Section titled “B. Chat logs page behavior”- 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
C. Widget history persistence
Section titled “C. Widget history persistence”- 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:
- read local cache for quick render
- fetch authoritative thread history from server
- reconcile/replace with server data
3) Identity strategy details
Section titled “3) Identity strategy details”Known users (name/email/phone submitted)
Section titled “Known users (name/email/phone submitted)”- Keep browser
apfc_user_idas the thread identity key. - Store normalized
visitorName,visitorEmail, andvisitorPhoneNumberas profile metadata on that thread/session. - Do not merge separate browser identities by email alone (no verification).
Guests
Section titled “Guests”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
4) Data model changes
Section titled “4) Data model changes”Use explicit ChatThread model.
ChatThread model
Section titled “ChatThread model”idshopIdidentityKey(apfc_user_idvalue, 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)onChatMessage— for fast history fetch
5) Backend flow changes
Section titled “5) Backend flow changes”In chat API (/api/chat / generateChatReply flow):
- Build normalized
identityKeyfrom request (apfc_user_idprimary) - Resolve existing thread/session by
(shopId, identityKey) - Reuse thread/session if found; else create one
- Save profile metadata (
visitorName,visitorEmail,visitorPhoneNumber) when provided - Save user and assistant messages against this thread/session
- Update
lastMessageAton every write - 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 bothshopanduid - Returns
{ messages: [{ id, role, text, createdAt }] }
6) Frontend widget changes
Section titled “6) Frontend widget changes”- Persist
apfc_user_idin localStorage for anonymous users - Persist chat history cache (last N messages) in localStorage
- On open:
- render cached history immediately
- fetch server history and reconcile
- On each send/receive:
- append to UI
- update local cache
- Persist latest submitted profile snapshot in localStorage (
apfc_identity_v1) for UX continuity only (not cross-browser identity merge) - 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
7) Chat logs admin UI changes
Section titled “7) Chat logs admin UI changes”In app.chat-logs.jsx:
- Loader should return thread-grouped results:
- one row per
identityKey/thread - include
lastMessageAt,messageCount, identity labels
- one row per
- Sort rows by
lastMessageAt DESC - Header text should reference customer/thread count (not raw sessions)
8) Data migration / cleanup plan
Section titled “8) Data migration / cleanup plan”Full wipe rollout (fixed):
- Deploy schema + code for thread identity model.
- Stop traffic in dev/staging environment.
- Truncate legacy chat tables (
ChatMessage,ChatSession, and related chat usage rows if needed for consistency). - Resume traffic with clean
ChatThread-based flow. - Run smoke tests: new guest thread, returning same-browser thread reuse, chat logs ordering, history endpoint rehydrate.
9) Risks and mitigations
Section titled “9) Risks and mitigations”- 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
- Cross-device continuity for guests
- expected limitation unless known identity is provided
- Stale local cache
- always reconcile with server history on widget open
- No cross-device auto-merge
- same person on a different device appears as a different customer unless verification is added later
- Privacy concerns
- avoid storing raw IP; if needed, hash and limit retention
- Unauthorized history reads
- never expose direct
identityKeylookups without shop/auth scoping
- never expose direct
10) Recommended implementation phases
Section titled “10) Recommended implementation phases”Phase 1: Foundation
Section titled “Phase 1: Foundation”- Identity key generation (
apfc_user_id) - Reuse thread/session by identity
- Update chat logs sort by latest message
Phase 2: History UX
Section titled “Phase 2: History UX”- Widget local cache
- Server history endpoint + rehydrate on open
Phase 3: Cleanup and hardening
Section titled “Phase 3: Cleanup and hardening”- Legacy migration/wipe
- Reset chat control
- Additional observability and QA
11) Finalized decisions (approved)
Section titled “11) Finalized decisions (approved)”These decisions are now fixed for implementation:
- Guest identity: browser
apfc_user_idis primary; IP is secondary hint only. - Known profile fields: name/email/phone are metadata (
visitorPhoneNumberincluded), 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.