Skip to main content

Monitoring CFO-7362 dead-session redirects

This runbook answers one question. Of the chats we redirected away from, which were genuinely dead, and did we bounce anyone off a chat that was actually alive? Run the DB audit daily. It is the authority, because it compares each redirect against what the database actually holds. The PostHog query is the fast first look and tells you why a redirect fired.

1. DB audit (authoritative)

Paste the redirected session ids, timestamps, and the viewing user’s clerk id from the PostHog query below into the values list, then run in Supabase. Worst verdicts sort first. The clerk_id column is not optional. The session GET scopes rows to the signed-in user, so a session belonging to someone else correctly reads as empty and is correctly redirected. An audit that ignores ownership reports those as REVIEW and sends you chasing a bug that is not there. This happened on 2026-08-14 with session_0e670456, which had 9 rows owned by a different user.
Deleted rows are excluded from the join itself, not just from the count, so a soft-deleted historical row cannot become the minimum and disguise a bounced live turn as a REVIEW. Reading it When a verdict looks impossible, check ownership before anything else:
first_row_delta_s carries the whole signal. Positive and small, roughly 0 to 2 seconds, means the row landed after we called the session dead. That is a race, not a dead session.

2. PostHog, daily counts and the reason each redirect fired

Healthy shape
  • chat_session_not_found_fresh_fallback, a small trickle. These should all come back CORRECT: truly dead in the audit.
  • chat_session_load_skipped_in_flight fires on sends whose load would have raced. Harmless. High counts just mean the guard is earning its keep.
  • chat_session_not_found_recheck_recovered counts races the re-check caught. These are saves, so a steady trickle is the system working. Watch p50_recheck_attempts instead of the raw count: if recoveries routinely need the last attempt before the deadline, DEAD_SESSION_CONFIRM_WINDOW_MS is too tight and should be raised. Redirects sitting at the attempt ceiling say the same thing louder.
  • chat_session_recheck_failed_fallback should sit near zero. These are redirects we could not confirm. Sustained non-zero means the session GET is failing, so check Supabase and PostgREST health rather than this code.
  • while_streaming above zero, or p50_ms_since_send under roughly 5000, is a red flag that we are redirecting live chats. Cross-check in the audit above.

Baseline for comparison

Redirects cluster after deploys, when Next.js forces hard navigations on clients still running the old bundle. A page remount is what makes a load fire for a session the context does not hold. Check the day of and the day after every release, not just quiet days.

If the audit returns a WRONG verdict

  1. Pull that user’s event stream around the redirect: chat_message_sent, then workflow_dispatch, then the fallback event, then the $pageview to /chat.
  2. Check whether they returned to the session afterwards. If not, they lost that answer. The row exists but was never rendered.
  3. The guards live in lib/chat-dead-session.ts, with the reasoning in contexts/shared-chat-context.tsx under loadSession.