How parents are inferred#
Six passes run over the session, from a proxy-injected marker down to a same-host time cluster, and each link records the pass that made it.
GraphDagger rebuilds the graph from scratch every time you ask for it. The graph therefore always matches the current view when you load new requests, apply a filter, or switch sessions. This page explains how the engine infers parents, so you can read the graph with confidence and tell a confirmed parent from a guessed one.
The six linking passes#
Most HAR captures and most live recordings carry only partial provenance. GraphDagger does not rely on a single field. It runs a sequence of passes over the requests. Each pass is cheaper and less certain than the pass before it. The engine stops at the first pass that finds a parent for a request.
The engine tags each link with the pass that produced it. It marks links from the early passes as confirmed, and links from the later passes as heuristic.
Pass 1: browser-confirmed link#
When you record traffic through Web Debugger, the proxy injects a small marker into outgoing requests. The marker records the URL of the script or page that issued the request. If the marker is present, the engine takes the parent straight from it. This is the strongest signal the graph has. It is also the only signal that survives redirects, retries, and cross-origin code that strips the standard browser fields.
Pass 2: browser initiator#
A capture from a developer-tools export already includes an 'initiator' record for each request. The record holds the URL and stack frame of the script that called fetch, or the page that triggered a navigation. When the capture sets that field, the engine matches the initiator URL against the URLs already in the session. It uses the best match as the parent.
Pass 3: referrer header#
If neither of the first two signals is available, the engine matches the request's Referer header against the session's URLs. This pass catches most navigations and many subresource loads. It helps most with older captures, and with HAR files from tools that do not include initiator metadata.
Pass 4: redirect chain#
The engine links any 3xx response with a redirect target to the follow-up request that fetched the target URL. This rebuilds the redirect chain. A request that ended in a 200 still shows the 301s, 302s, and 307s that led to it as its ancestors.
Pass 5: page-and-subresource heuristic#
Some requests survive the first four passes, typically images, fonts, stylesheets, scripts, and media. For each one, the engine looks for the most recent page on the same domain, or on a closely related domain, that started before the request did. If the engine finds such a page, it takes that page as the parent. It marks the link heuristic because the evidence is circumstantial: the timing and the domain match, but no field in the capture names a parent.
Pass 6: same-domain temporal cluster#
This pass is the last resort. If a request still has no parent, the engine groups it with the first request on the same hostname. This keeps the graph clear of isolated nodes when only a tail of related calls survives. The engine also marks these links heuristic. They are the weakest signal in the graph.
How URLs are matched#
Several passes must ask one question: is this URL one of the URLs already in the session? The engine is tolerant in the small ways that matter.
- The engine strips the fragment (
#section) before it compares two URLs. - The engine ignores a trailing slash.
- The engine prefers an exact URL match. If it finds no exact match, it matches without the query string.
A request never matches itself, so a self-referencing initiator or referrer cannot create a self-loop in the graph.
Roots, orphans, and the fallback root#
After the engine visits every request, it sorts them into three buckets.
- Roots: requests with no parent that themselves have at least one child. These become the top-level entries in the graph view.
- Orphans: requests with no parent and no children. The graph shows these separately, so you can scan them for things that arrived without a story: preflight checks, beacons, third-party pixels, or anything that came from outside the captured context.
- Internal nodes: requests with a parent. These hang under their parent in the tree.
A session can hold requests and still get no roots from the passes. This is uncommon, but it happens when the passes link every request into a single chain. The engine then promotes the first orphan to a root, so the graph still has a start point.
What the passes mean in practice#
- Confirmed edges have hard evidence behind them. Heuristic edges are best guesses, and the graph flags them as such, so you can verify them yourself.
- A request that genuinely has no traceable parent stays an orphan. The engine does not attach it to an unrelated parent to make the graph look tidy.
- A recording through Web Debugger gives you the strongest graphs. The browser-confirmed pass only fires on sessions that went through the proxy. A live capture therefore produces more confirmed links than a HAR file from a different tool.
- The graph is computed locally from the entries already in the session. No data leaves your machine.