From 806b5644ce6ea5a47725a5dca13831381324b0cc Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 13 Aug 2026 11:31:41 +0530 Subject: [PATCH] Document GPT diagnostics attribution and correlation evidence Complete the public guide against the behavior merged in #997. Cover the per-auction hb_auction_id correlation token and the targeting boundary that keeps it out of the ad request, badge delivery labels, creative-bridge failure triggers, Ad Manager identifier normalization, and the adInit refresh context. Add troubleshooting for unattributed and competing refreshes, unconfirmed candidate delivery, and missing correlation evidence. Link the guide from the GPT integration page, use the exact ts_console=1 activation example, and drop the integrations overview claim that diagnostics make no attribution claims. --- docs/guide/integrations-overview.md | 12 +- docs/guide/integrations/gpt-diagnostics.md | 152 ++++++++++++++++++--- docs/guide/integrations/gpt.md | 2 + 3 files changed, 142 insertions(+), 24 deletions(-) diff --git a/docs/guide/integrations-overview.md b/docs/guide/integrations-overview.md index 1e837e63f..95f3d2011 100644 --- a/docs/guide/integrations-overview.md +++ b/docs/guide/integrations-overview.md @@ -186,17 +186,19 @@ enabled = true ### GPT Runtime Diagnostics -**What it does:** Observes documented GPT lifecycle callbacks and presents directly observed slot, timing, coverage, binding, and visibility facts in a local browser console. +**What it does:** Observes documented GPT lifecycle callbacks and Trusted Server integration evidence, then presents directly observed slot, request-path, delivery, timing, coverage, binding, and visibility facts in a local browser console. **Key Features:** - Explicit browser-session `ts_console` activation through a host-only HttpOnly cookie - Conditional standalone delivery only on active HTML documents -- Initial and refresh request-cycle history -- Conservative unmatched and ambiguous callback reporting +- Initial and refresh request-cycle history, with observed request paths and replacements +- Delivery states derived only from observed Trusted Server creative evidence +- Source-neutral Ad Manager identifiers and response classes reported by GPT +- Conservative unmatched, ambiguous, and attribution issue reporting - Exact DOM binding and non-layout-changing viewport badges - Versioned local JSON export with no diagnostic upload -- No creative-provenance or auction attribution claims +- No inferred demand ownership: a filled slot alone never proves Trusted Server delivery **Configuration:** @@ -207,7 +209,7 @@ enabled = true **Endpoints:** None. The feature observes GPT in the browser and makes no diagnostic network request. -**When to use:** You need to debug GPT request, response, render, load, viewability, refresh, and slot-binding behavior without changing ad delivery. +**When to use:** You need to debug GPT request, response, render, load, viewability, refresh, delivery-attribution, and slot-binding behavior without changing ad delivery. **Learn more:** [GPT Runtime Diagnostics](./integrations/gpt-diagnostics.md) diff --git a/docs/guide/integrations/gpt-diagnostics.md b/docs/guide/integrations/gpt-diagnostics.md index 78c657bf6..f3ac9779b 100644 --- a/docs/guide/integrations/gpt-diagnostics.md +++ b/docs/guide/integrations/gpt-diagnostics.md @@ -19,7 +19,7 @@ Server candidate, a PUC markup request, a successfully posted markup response, a GPT slot load are separate steps in an evidence ladder. This feature requires zero publisher-code changes. Activation remains the existing -server integration configuration plus `?ts_console=true`; it does not require new +server integration configuration plus `?ts_console=1`; it does not require new publisher JavaScript, React, Next.js, DOM, or GAM configuration. The diagnostics integration is independent of the @@ -36,11 +36,36 @@ The module is unavailable unless explicitly enabled for the deployment: enabled = true ``` -Deployment configuration only makes the module available. Inactive browser sessions -receive no diagnostics module. When activated, the standalone content-hashed module -loads synchronously after the core bundle so it can install listeners before -publisher GPT request code. The standalone static response is cookie-independent and -remains publicly cacheable; active HTML responses are private and non-storeable. +Deployment configuration makes the module available; it does not activate any browser +session. Inactive browser sessions receive no diagnostics module. When activated, the +standalone content-hashed module loads synchronously after the core bundle so it can +install listeners before publisher GPT request code. The standalone static response is +cookie-independent and remains publicly cacheable; active HTML responses are private +and non-storeable. + +### Auction correlation token + +Enabling the integration has one server-side effect that does not depend on browser +activation. For each server-side auction that produced winning bids, Trusted Server +mints a fresh correlation token and publishes it as `hb_auction_id` on each winning bid +in `window.tsjs.bids`: + +```text +ts-auc-2f8c1d5a4b7e4c0f9a3d6b1e8c5f2a7d +``` + +- The token is generated per auction from a random UUID. It is not derived from the Edge + Cookie ID, the auction request ID, or any other visitor identifier, and it does not + repeat across auctions. +- It is **not** a GAM targeting key. Of the Trusted Server bid fields, only `hb_pb`, + `hb_bidder`, `hb_adid`, `hb_cache_host`, `hb_cache_path`, and `ts_initial` are applied + as slot targeting alongside the slot's configured targeting, so the token never enters + the ad request. +- It is absent when the integration is disabled, and absent for any auction that + produced no winning bids. +- It is published on every document whose auction produced winning bids, including + documents with no active console session, because the console reads it from the same + page bid state the GPT integration already consumes. ## Activate or Deactivate a Browser Session @@ -56,7 +81,13 @@ Open a page with one of these exact, case-sensitive query directives: For example: ```text -https://publisher.example.com/article?ts_console=true +https://publisher.example.com/article?ts_console=1 +``` + +Deactivate the same browser session with the matching directive: + +```text +https://publisher.example.com/article?ts_console=0 ``` An exact directive establishes or clears the host-only, `Secure`, `HttpOnly`, @@ -124,10 +155,11 @@ arguments, result, and synchronous throw. A `refresh()` call that omits its slot list, or passes `null` or `undefined` for it, refreshes every slot; the observer reads GPT's current slot list for diagnostics only. A stale refresh function reference captured before installation bypasses that boundary and remains -`unattributed`. Prebid sets a scoped, -synchronous diagnostics context while delegating its own refresh, so nesting does not -mislabel a Prebid refresh as `competing`. Diagnostics never suppresses or changes a -GPT request. +`unattributed`. Prebid and the Trusted Server `adInit` refresh each set a scoped, +synchronous diagnostics context while delegating their own refresh, so a nested +`pubads.refresh` is not mislabeled `publisher_refresh` or `competing`. Both clear that +context even when the delegated refresh throws, and the Prebid wrapper restores the +exact prior value. Diagnostics never suppresses or changes a GPT request. For a direct observation, the optional opaque auction ID is retained only after trimming to a non-empty value no longer than 256 UTF-8 bytes. No auction payload, @@ -191,10 +223,12 @@ the state. A matched creative attempt can report these safe, non-terminal categories: -- `missing_render_source` -- `cache_fetch_failed` -- `invalid_cache_payload` -- `response_post_failed` +| Failure | Observed at the bridge | +| ----------------------- | ---------------------------------------------------------------------------------------------- | +| `missing_render_source` | The bid carried neither inline markup nor a complete PBS Cache host and path. | +| `cache_fetch_failed` | The PBS Cache fetch was rejected or returned a non-OK status. | +| `invalid_cache_payload` | The cache response was read but held no renderable creative, so nothing was posted. | +| `response_post_failed` | `port.postMessage` threw while posting markup, on either the inline or the cached-markup path. | Failures are deduplicated and retain first-observed order. Detailed URLs, cache IDs, payloads, markup, and error objects remain only in existing operational logging and do @@ -219,6 +253,11 @@ line-item backfill alike, so they classify as `reservation` only when GPT also reported the render as explicitly non-backfill. On their own they remain `unclassified_non_empty` rather than becoming an unsupported conclusion. +Identifiers are retained only as positive whole numbers, and the yield-group and company +lists keep at most eight IDs each. GPT reports these fields only for reservation and +backfill ads served by PubAdsService, so an absent identifier is a fact about the render +rather than a gap in observation. + ## Attribution Issues and Callback Coverage Creative-correlation problems are exported separately from GPT callback issues. The @@ -270,6 +309,28 @@ A concise viewport badge appears only when a slot: - Has a unique, connected exact binding. - Has a non-zero rectangle intersecting the viewport. +A badge summarizes the slot's most recent request cycle: the GPT result (Filled, Empty, +Rendered (fill unknown), or Pending), a short delivery label, a `Competing paths` +marker when the request path is `competing`, the rendered size, and the request-to- +response, response-to-render, and render-to-viewable durations that are available. It +adds `Incomplete sequence` when a callback proved a missing or invalid earlier step. + +Badge delivery labels are the same derived states the panel and export report, shortened +to fit: + +| Delivery state | Badge label | +| ------------------------------ | ----------------------- | +| `trusted_server_response_sent` | TS response sent | +| `trusted_server_selected` | TS selected | +| `pending` | TS candidate (pending) | +| `candidate_unconfirmed` | TS unconfirmed | +| `no_candidate` | No TS candidate | +| `unknown` | Delivery unknown | +| `not_applicable` | No delivery label shown | + +The badge never re-derives delivery from raw timestamps; it labels the state the store +already resolved, so a badge cannot disagree with the panel or the export. + Missing elements and duplicate DOM or GPT slot IDs remain visible in the panel as Unbound or Ambiguous and receive no badge. If DOM uniqueness cannot be verified because selector support is unavailable or throws, the export reports @@ -340,10 +401,13 @@ The allowlisted export contains: It does not contain raw targeting, bid IDs, bid prices, bidder identity, creative markup, cache URLs, cache payloads, cache or bridge error details, cookies, user -identifiers, query strings, or URL fragments. The exported -`trustedServerAuctionId` is a token minted fresh for each server-side auction: it -is not derived from the Edge Cookie ID or any other visitor identifier, and it does -not repeat across auctions, so it cannot be joined back to a visitor. +identifiers, query strings, or URL fragments. The exported `trustedServerAuctionId` +is the `hb_auction_id` value described in +[Auction correlation token](#auction-correlation-token): minted fresh for each +server-side auction, not derived from the Edge Cookie ID or any other visitor +identifier, and never repeated across auctions, so it cannot be joined back to a +visitor. Diagnostics retain it only after trimming to a non-empty value of at most +256 UTF-8 bytes. Captured records are memory-only. Diagnostics do not add an upload, diagnostics network request, `localStorage`, `sessionStorage`, IndexedDB, or other persistence. @@ -419,6 +483,56 @@ documented callbacks do not expose a request-cycle identifier. Avoid overlap in controlled tests, or use the issue record as evidence that correlation was not possible. +### A refresh is `unattributed` or `competing` + +`unattributed` means no request-path evidence was still eligible when GPT emitted +`slotRequested`. Each source's marker lives five seconds and is consumed once, so a +request more than five seconds after the observation, a refresh function reference the +publisher captured before installation, and any path Trusted Server does not observe +all stay `unattributed`. Diagnostics never fill that gap from timing, element IDs, or +targeting names. + +`competing` means two or more sources contributed evidence for the same request. It is +a warning that competition or overwrite is possible, not a statement about which values +GPT sent. To narrow it in a controlled test, trigger one path at a time and leave more +than five seconds between refreshes. + +### Delivery stays `candidate_unconfirmed` + +The cycle rendered explicitly non-empty with a Trusted Server candidate, but no matched +creative markup request arrived within five seconds of `slotRenderEnded`. Read the +cycle's other facts before concluding anything: + +- `responseClass` and the GAM identifiers show what Ad Manager reported delivering. +- A creative-bridge failure category on the same cycle shows the bridge was reached and + failed. +- An attribution issue at the same time shows the request arrived but could not be + correlated. +- No evidence at all is consistent with a different GAM result, a targeting overwrite, + and a PUC configuration or ID mismatch alike. + +A late positive observation upgrades the state, so re-read the panel rather than +exporting immediately after render. + +### Correlation evidence is missing + +Attribution issues record why creative evidence could not be attached. They never +increment callback coverage and never produce a delivery claim: + +| Reason | What was observed | +| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `creative_request_without_slot` | The markup request carried no auction slot ID, or no retained association mapped it to a GPT slot. | +| `creative_request_without_cycle` | The slot had no retained request cycle inside the 30-second attempt window, or its most recent cycle was already reported empty. | +| `creative_request_ambiguous_cycle` | An earlier non-empty cycle for the same slot was still in window before render, so no cycle was chosen. | +| `creative_request_on_empty_cycle` | GPT later reported the matched cycle empty, so the attempt was dropped instead of claiming delivery. | +| `creative_attempt_capacity` | The 128-attempt bound was reached with every retained attempt still live. | +| `creative_attempt_unknown` | A response or failure referenced an attempt no longer retained. | +| `creative_attempt_expired` | The attempt passed its 30-second lifetime before its response or failure was observed. | +| `creative_attempt_evicted` | The attempt's slot or request cycle was dropped first, by a retention bound or by GPT reporting that cycle empty. | + +Repeated issues on a busy page usually mean retention bounds, not delivery failure. +Reduce refresh overlap or capture a shorter session, then re-read the cycle. + ## Limits The integration observes six documented PubAdsService events, wraps diff --git a/docs/guide/integrations/gpt.md b/docs/guide/integrations/gpt.md index f38f68231..de0df03c4 100644 --- a/docs/guide/integrations/gpt.md +++ b/docs/guide/integrations/gpt.md @@ -144,6 +144,7 @@ Takes over `googletag.cmd` so every queued callback is wrapped before GPT execut - Check the proxy responses have `200` status (look for `X-GPT-Proxy: true` header) - Verify the `script_url` config points to the correct GPT endpoint - Review server logs for upstream fetch failures +- Open [GPT Runtime Diagnostics](./gpt-diagnostics.md) with `?ts_console=1` to see the observed request, render, load, and delivery evidence per slot ## Implementation @@ -152,6 +153,7 @@ Takes over `googletag.cmd` so every queued callback is wrapped before GPT execut ## Next Steps +- Use [GPT Runtime Diagnostics](/guide/integrations/gpt-diagnostics) to inspect GPT lifecycle and Trusted Server delivery evidence in the browser - Review [Integrations Overview](/guide/integrations-overview) for comparison with other integrations - Check [Configuration Reference](/guide/configuration) for advanced options - Learn about [First-Party Proxy](/guide/first-party-proxy) architecture