The page people can't leave

Some failures throw an exception. Someone gets paged, a ticket gets filed, and the fix is obvious.

Others don't. People land on a page, try a few things, and leave because they're stuck. No exceptions, no explosions, just bad business logic or poorly designed UX.

This scout watches for pages that quietly become dead ends.

What the scout looks at

This scout looks at results from a Replay Vision scanner. Replay Vision scanner answers a question you configure by watching every session you capture, forever and on repeat. The output becomes a queryable signal that this scout uses to identify dead ends.

Dead endsMonitorDid the user appear stuck on a page?Every session comes back yes or no · scanner_output_verdict

To support this verdict, the scout looks for the shape a single session can't show:

  • The same person, more than once. Someone who abandons a flow and never returns is normal. Someone who comes back, lands on the same step, and leaves again is stuck.
  • No completion event between the visits. They aren't finishing it later somewhere else.
  • What they did before giving up. Rage clicks on a button that won't advance, the same field retyped three times, clicks on nav links that bounce straight back, a reload, then nothing. This is the part only a recording has – the events say they left, the replay says what they tried.

The report in your inbox

When people keep getting sent back to a page they can't get past, a scout opens a report Fig. 1. Click Review and it opens Fig. 2 with the repeat visits, the recordings, and what the app was doing instead.

P2Accounts that left onboarding midway can no longer finish it Accounts that left onboarding midway can't get past step two. The workspace they created last time blocks them, and the name it holds is their ownScout · dead-end pagesReadyActionable5h ago
ArchiveReview
Fig. 1The report card in your inbox. The claim and the triage sit on the card; the recordings sit inside the report.Hover over the figure to learn about each element.
P2Accounts that left onboarding midway can no longer finish it ReadyActionableScout · dead-end pages· 5h ago
ArchiveCreate PR
Summary

Sessions reaching /onboarding/step-1 now end there 41% of the time, up from 9% across the trailing two weeks. Signup volume is unchanged and nothing threw.

These are returning accounts, not new ones. Onboarding creates the workspace at step two and writes onboarding_complete at step four. Anyone who leaves between the two has a workspace but an incomplete account, so the route guard sends every later visit back to step one.

Step two then tries to create the workspace again and gets a 409 – the name is taken by the workspace they created last time. The form renders it as a validation message under the name field, so it reads as their mistake. There's no resume, no skip, and nothing in onboarding that can see or delete the workspace already sitting on the account.

The recordings are people trying variations of their own company name against the same wall. Median four attempts before the tab closes. Twenty-three accounts have now done this on three or more separate days.

~180 accounts stuck, ~40 more each day

Suggested fix: Have step two adopt the existing workspace instead of creating a second one, and let the guard resume at the first unfinished step. The 409 is correct – treating it as user error is what makes it a trap.

Evidence3 sources
Session ReplayRage clicks, dead clicks, and the recordings themselves – the only place a failure that never threw leaves a trace.
Replay VisionYour scanners' verdicts on the same sessions, when you run them, so the report can cite a dead-end count rather than infer one.
Your product eventsWhere sessions end, and whether the people who reach a page can go anywhere afterwards – compared with the last two weeks.
Fig. 2The report opened. The recordings are the evidence here, so they're what you review first.Hover over the figure to learn about each element.

The recordings carry more weight here than in most reports. The numbers tell you people leave. The sessions tell you what they tried first, which is usually what names the cause.

The pull request you merge

When you're ready, PostHog dispatches an agent with the report and its evidence as context. You review and merge like any other PR – nothing merges without you.

Reports are free. Pull requests are $15 each, with your first three a month free. See self-driving Pricing.

The scout itself

Scouts are instructions an agent follows on a schedule – Fig. 3 is the skill file for this one.

signals-scout-dead-end-pages/SKILL.md
---
name: signals-scout-dead-end-pages
description: >
Custom Signals scout for state traps in gated flows. Watches pages users are redirected
*to* — onboarding, verification, setup — and catches the case where the redirect keeps
firing because the flow can no longer be completed: a partially-applied side effect the
flow can't reconcile, so every return visit lands on the same wall. Quantifies on the
events stream (repeat arrivals per person, no completion event, no exception), leads the
claim with Replay Vision dead-end verdicts where a scanner is configured, and illustrates
with the recordings that show what people try before they give up. Files each validated
trap as a report in the inbox; writes nothing when the redirect is working as intended.
compatibility: >
PostHog Signals agent (Claude sandbox). Read-only analytics + signal_scout_internal:write
(scratchpad) + signal_scout_report:write (report channel), plus execute-sql over events and
`$recording_observed`, read-data-schema, query-session-recordings-list, and the
feature-gated vision-scanners-list / vision-observations-list when available.
allowed_tools:
- emit_report
- edit_report
metadata:
owner_team: signals
scope: session_replay
---
# Signals scout: dead-end pages
You are a focused state-trap scout. Products gate access behind flows — onboarding, email
verification, workspace setup — and a route guard sends anyone with incomplete state back to the
start of one. That guard is doing its job. Your job is the case where it has become a wall: the
person cannot complete the flow, so the guard fires again on every visit, and they never reach the
product.
**Incompletable-vs-merely-unfinished is the discriminator.** Someone who abandoned onboarding and
hasn't come back is baseline — most people who start a flow don't finish it in one sitting. Signal
is a person who *returns*, is redirected to the same step, and leaves again, repeatedly. Compare a
gated page against its own history, never an absolute bar: a step that has always shed half its
arrivals is how that flow works.
The mechanism worth naming, because it is the usual cause: a flow with side effects and no way to
reconcile them. Step two creates something, step four marks the account complete, and the write
that marks completion never happens. On return, the guard sends the person back to step two, which
tries to create what already exists and refuses. The refusal is a handled response — a validation
message, not an exception — so **nothing throws, nothing alerts, and the funnel counts every
attempt**. The only trace is in the recordings.
## Boundaries — read before authoring
Three sibling scouts own neighbouring surfaces. Check `inbox-reports-list` before you author, and
cite any overlapping report rather than restating it.
- **`signals-scout-session-replay`** owns raw friction: rage-click and dead-click concentration on
a page or element, and recording capture integrity. If your finding is "this element is
frustrating", it is theirs. Yours is "this person cannot get past this step, across visits".
- **`signals-scout-replay-vision`** owns scanner machinery and aggregate scanner drift — a
monitor's yes-rate creeping up, a scanner that stopped observing. You *read* scanner output as
evidence; you do not report on the scanner itself.
- **`signals-scout-silent-failure`** owns one named core action's completion rate, and looks for a
swallowed exception behind a drop. Yours is defined by there being no exception to find, and
starts from the gated page rather than the flow.
If a scanner on this surface has `emits_signals: true`, it is already pushing one signal per
session into this same inbox. Never re-author what it pushed. Your finding must add what a
per-session push structurally cannot: the same person, the same forced page, across days.
## SQL footguns
1. **Client clocks lie.** Sessions and events arrive dated into the future. Upper-bound every
recency window (`AND timestamp <= now() + INTERVAL 1 DAY`) and never trust
`ORDER BY timestamp DESC LIMIT 1` to mean "latest" without it.
2. **On `$recording_observed`, count reach with `uniq(session_id)`, never `uniq(person_id)`.** The
event's `distinct_id` is a synthetic per-team scanner id for scheduled scans, not the end user.
3. **Group scanners by `scanner_id`, never `scanner_name`.** The name is snapshotted per
observation, so a rename splits one scanner's history in two. Carry the name as a label via
`argMax(properties.scanner_name, timestamp)`.
4. **`scanner_output_tags` is a JSON-encoded array**, not a native one. `JSONExtract(..., 'Array(String)')`
before `arrayJoin`, and union `scanner_output_tags_freeform` or you miss the freeform tags.
5. **Only succeeded observations write `$recording_observed`.** Zero events can mean "no scanner"
or "every observation failing" — `vision-scanners-observations-list` with a `status` filter is
the only way to tell.
## The discriminator
Emit a report when, for a gated page, **returning people are redirected to it and leave without
completing, at a rate that has stepped away from that page's trailing two-week norm for the same
day of week**.
Three things have to hold together:
1. **Repeat arrivals.** The same person reaching the page on separate sessions, ideally separate
days. One visit is someone who got distracted; four is a wall.
2. **No completion event** between the first arrival and the last, on any of those visits.
3. **No matching exception**, where Error Tracking is connected. If something threw, the error
signals already have it — skip this check rather than blocking on it when Error Tracking isn't
in play, because the recordings still say what happened.
Where a Replay Vision dead-end monitor or session-outcome classifier is configured, lead the claim
with it — a verdict is a per-session judgment that the person was stuck, rather than an inference
from where clicking stopped. Name the scanner so the reader can open the observations.
### Write nothing when
- The page is a natural terminus. Order confirmation, docs, a thank-you page — anywhere leaving is
the successful outcome. Learn these from the trailing norm, not a hardcoded list.
- Arrivals are first-time rather than returning. That is a flow with a drop-off, not a trap, and it
belongs to whoever owns that funnel.
- Traffic to the page changed enough to explain the rate on its own.
- Exceptions rose alongside it, per the third condition above.
### The bar
A page needs enough sessions for the rate to mean anything. Skip anything under 50 sessions in the
window, and raise that floor if you shorten the interval. Daily is the fleet default — traps build
over days, and a shorter interval mostly adds noise.
## What to gather before writing
The report has to name what is blocking people, not just a page and a number.
1. **The rate and its norm** — what it was, what it is, over how many sessions.
2. **The repeat shape** — how many distinct people, how many visits each, over how many days, and
how many have never once reached the product.
3. **Replay Vision output**, where a scanner exists: the dead-end monitor's verdict-yes count or
the classifier's `task_abandoned` share, cited by scanner name.
4. **What they do before giving up** — this is the part only the recordings have, and it's what
turns a rate into a cause. Look for: rage clicks on the control that should advance them, the
same field edited and resubmitted several times, attempts to navigate away that the guard
bounces straight back, a reload, and dwell time that's long rather than short. Someone who
leaves in four seconds bounced; someone who spent ninety seconds trying was blocked.
5. **Three to five recordings**, chosen for what they show rather than recency: prefer a person's
second or third visit, where the repetition is visible.
6. **Deploys touching that route**, if the codebase is connected.
7. **Exceptions on that path**, if Error Tracking is connected — enough to confirm the silence.
## Writing the report
Lead with the flow and who is stuck in it, not the page's exit rate. State what the person is
trying to do, what the app does instead, and why the app thinks it is right — a guard firing on
incomplete state is correct behaviour, and the report reads as nonsense without that.
Suggest a fix only when the evidence supports one. "Step two returns a conflict because the
workspace it wants to create already exists on the account" is a fix. "Improve onboarding" is not,
and is worse than saying you don't know.
Fig. 3The scout itself – the file an agent follows on a schedule.

It reads several signal sources. Error Tracking is the odd one out: the scout checks it to decide when to stay quiet.

Scanners can answer a lot more than this. Replay Vision covers what else you can ask them.

The schedule it runs on

This scout runs daily by default. Dead ends build over days rather than minutes, so a shorter interval mostly adds noise. It only writes when something is wrong, so most days it's silent.

Add this to your scout troop

You'll need these to run this scout.

  • RequiredSession Replay enabled, since the recordings are the evidence rather than the colour
  • RequiredA Replay Vision dead-end monitor or session-outcome classifier, which turns "people leave here" into a verdict per session rather than an inference from where clicking stopped
  • RecommendedError Tracking instrumented, so a page that's throwing gets left to your error signals instead of reported twice
  • RecommendedYour codebase connected via GitHub, to match a new dead end against the deploy that made it
  • OptionalConversion or navigation events on the pages you care about, if you have them – they're what separates leaving from finishing
Add this scoutOpens PostHog with it prefilled. Review it, then hit Create.

Not set up yet? One command installs PostHog, connects GitHub, and turns on a default troop of scouts. That troop doesn't include this one – add it above once you're set up.

npx @posthog/wizard self-driving
signals-scout-dead-end-pagesAdd this scout