Skip to content

July release candidate (DO NOT MERGE) - #919

Draft
ChristianPavilonis wants to merge 260 commits into
mainfrom
rc/july
Draft

July release candidate (DO NOT MERGE)#919
ChristianPavilonis wants to merge 260 commits into
mainfrom
rc/july

Conversation

@ChristianPavilonis

@ChristianPavilonis ChristianPavilonis commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Consolidates the July release-candidate changes for review and integration testing before the individual changes merge to main.

Included pull requests

Already included through main

Pending Review / Merge

prk-Jr and others added 30 commits July 13, 2026 13:12
Address PR review of the SSAT debug-dump change:

- The PBS non-2xx response body was attached to AuctionResponse.metadata,
  which ProviderSummary clones verbatim into ext.orchestrator.provider_details
  on the public /auction response — violating the documented invariant in
  auction/orchestrator.rs. Drop the body from metadata, keep only the numeric
  status, and log the snippet server-side at warn.
- Register ERROR_TYPE_HTTP_STATUS and match it in provider_status so PBS HTTP
  errors get their own telemetry bucket instead of the transport_error
  fallback.
- Bound the ts-debug dump: compact serialization capped at 256 KiB, and skip
  the mediator_response line when no mediator ran.
- Correct the auction_html_comment and prepend_auction_debug_comment docs to
  state the comment now embeds raw SSP creative markup (never enable in prod).
- Keep the targeted two-replace terminator neutralisation: a single
  replace("--", ...) re-forms -->/--!> at odd dash-run junctions and is not
  equivalent. Add a table-driven test over the comment-terminator vectors.
- Hoist test-local imports to module scope per CLAUDE.md.
…debug' into feat/ssat-write-prebid-response-debug
- Drop render_adm param: always include adm when creative present
- Gate GAM-bypass on per-bid debug_bid instead of a global window.tsjs flag
  (removes SPA-staleness edge case, TsjsApi change, and whole flag-emit task)
- Correct fallback scope: cache fallback only when adm absent; render failure
  after adm is supplied is not detectable
- Qualify sandbox claim: TS guarantees script-context escaping; bridge frame
  isolation depends on the Prebid Universal Creative
- Reconcile with existing ad_init.test.ts coverage (rename, no duplicates)
- Fix test assertion messages to expect("should ..."); list exact clippy gates
- Task 3: assert observable DOM (GAM iframe src) instead of spying on the
  module-private injectAdmIntoSlot
- Use existing make_bid helper + set .creative (make_test_bid_with_creative
  does not exist on this branch)
- Hostile-adm regression test covers both U+2028 and U+2029
- Add 'cd docs && npm run format' to verification
build_bid_map now always inserts the winning creative as adm so the pbRender
bridge can render it locally (no PBS Cache round trip); the verbose debug_bid
blob and the GAM-bypass gate stay behind inject_adm_for_testing. Rename the
param include_adm -> include_debug_bid and thread it through write_bids_to_state.
Reconcile the by-default test to the new behavior, drop the now-redundant
debug-only-adm test, and pin script-context escaping for a hostile adm
(</script> + U+2028/U+2029).
The direct GAM-replace path (injectAdmIntoSlot) now fires only when the bid
carries debug_bid, which is present only under inject_adm_for_testing. In
production the always-present adm is rendered by the pbRender bridge and GAM
stays in the loop. Add observable-DOM tests (GAM iframe src unchanged without
debug_bid, rewritten with it), strengthen the bridge test to prove inline adm
is preferred even when cache coords are present, and rename debug-adm
terminology to inline adm.
Resolve five correctness and resource-safety findings from the PR #867
review of the end-to-end Fastly publisher streaming path.

- Drive the deflate decoder to StreamEnd at finalization so a valid
  stream that exactly fills the internal output buffer is no longer
  rejected as truncated; the inflater is also drained after all input is
  consumed within a chunk.
- Decode concatenated (multi-member) gzip bodies via MultiGzDecoder on
  both the streaming decoder and the buffered read pipeline so adapters
  agree.
- Enforce the decoded-body cap during decompression through a bounded
  sink shared by the gzip and brotli codecs, so a compression bomb errors
  before its expanded bytes are buffered instead of after a full chunk
  expands; the deflate codec charges each produced block as it is emitted.
- Drop the body of bodiless responses (HEAD, 204, 205, 304) in both the
  streaming and buffered finalizer Buffered arms, and add RESET_CONTENT
  to response_carries_body, so a buffered-unmodified stream body is never
  streamed to the client for a response that must be bodiless.
- Keep the dispatched-auction guard armed across the collection await and
  disarm it only once collection reaches a terminal result, so a body
  dropped while collection is pending still logs the discarded SSP work.

Add regression tests for the deflate output-buffer boundary, multi-member
gzip, bodiless buffered stream bodies, and the auction guard sentinel.
Resolve the PR review by making transport-timeout canonicalization a
platform capability and hardening auction backend-name correlation.

- Move quantization behind PlatformBackend::canonicalize_transport_timeout_ms.
  Fastly floors budget-derived timeouts to a 250ms quantum with a bounded
  sub-quantum ladder [200,150,100,50]; other adapters use the exact remaining
  budget so bidder deadlines (Prebid tmax, APS timeout) are not shortened
  where no connection-pooling benefit exists.
- Bound sub-quantum backend-name cardinality: exact 1-249ms values no longer
  pass through, capping the budget-derived names a single origin can mint
  toward the per-service dynamic backend limit.
- Add a provider discriminator to PlatformBackendSpec, folded into every
  adapter's backend name, so two providers sharing one origin no longer
  collide on the response-correlation key. Reject a duplicate
  backend_to_provider insertion with an attributed launch failure instead of
  silently overwriting and misattributing a response.
- Make the orchestrator call-site tests deterministic: record predicted and
  registered transport timeouts separately and assert exact equality via a
  controllable platform backend, and enumerate the sub-quantum ladder to
  assert a bounded name cardinality.
- Correct the timeout-semantics comments that overstated absolute-deadline
  enforcement; the Fastly connect/first-byte/between-bytes timeouts bound
  connection, first-byte, and inactivity, not total response time. A true
  absolute deadline carried through the platform HTTP API remains follow-up
  work (#849).
Prebid non-2xx responses were reduced to bare provider errors, making intermittent failures difficult to diagnose. Surface safe HTTP metadata and bounded debug details while correlating server logs with the auction ID.
A configured bidder with no inline params and no matching override
expanded to `"bidder": {}`, which PBS rejects. After applying overrides,
drop fabricated empty bidders, preserve an explicitly supplied empty
object so genuine misconfiguration stays visible, and fall back to the
stored-request path when no eligible bidders remain.
Allow operators to retain sanitizer-accepted external URLs in POST /auction adm while preserving mandatory server-side sanitization and the existing default behavior.
ChristianPavilonis and others added 12 commits July 29, 2026 10:43
# Conflicts:
#	crates/trusted-server-js/lib/src/core/types.ts
…iants

Address review feedback on the opt-in creative-processing change:

- Register GET /first-party/proxy-rebuild in every adapter. The creative
  iframe sandbox no longer grants allow-same-origin, so the click guard's
  JSON POST preflights with Origin: null and fails; the guard now recovers
  by navigating to the GET endpoint, which the core handler answers with a
  302 chain that CORS does not apply to.
- Make the click guard detect an opaque origin up front and skip the doomed
  POST, going straight to the GET navigation fallback. Dynamic resource
  signing likewise bails out early instead of issuing a request that cannot
  succeed; a same-origin parent postMessage broker for opaque-origin signing
  is tracked as a follow-up.
- Enforce the 1 MiB per-creative cap independently of the sanitize/rewrite
  flags so pass-through and rewrite-only modes cannot ship oversized markup.
- Cover the remaining processing modes: rewrite-without-sanitize at both the
  helper and response-converter level, a byte-for-byte pass-through
  assertion, and oversized-creative rejection in all four modes.
- Update stale documentation that still promised mandatory sanitization and
  default-on rewriting; describe the client sanitizeCreativeHtml helper as
  validation-only and add a breaking-change changelog entry with the
  upgrade and rollback sequences.
Publishers may call googletag.display with a slot or element before the
initial request gate is released. Treating that argument as a string can
abort the request, while first-prefix slot resolution can target a hidden
responsive sibling.

Preserve the GPT display contract and resolve stable prefixes only when
one active element can be identified.
Comment thread crates/trusted-server-core/src/integrations/gpt_bootstrap.js Fixed
aram356 and others added 3 commits July 29, 2026 22:26
# Conflicts:
#	crates/trusted-server-js/lib/src/integrations/prebid/index.ts
#	crates/trusted-server-js/lib/test/integrations/prebid/index.test.ts
# Conflicts:
#	crates/trusted-server-js/lib/src/integrations/prebid/index.ts
#	crates/trusted-server-js/lib/test/integrations/gpt/ad_init.test.ts
#	crates/trusted-server-js/lib/test/integrations/prebid/index.test.ts
ChristianPavilonis and others added 8 commits July 30, 2026 08:20
# Conflicts:
#	CHANGELOG.md
#	crates/trusted-server-cli/tests/config_env_overlay.rs
#	crates/trusted-server-core/src/auction/README.md
#	crates/trusted-server-core/src/auction/formats.rs
#	crates/trusted-server-core/src/auction/orchestrator.rs
#	crates/trusted-server-core/src/auction_config_types.rs
#	crates/trusted-server-core/src/config_payload.rs
#	crates/trusted-server-core/src/creative.rs
#	crates/trusted-server-core/src/publisher.rs
#	crates/trusted-server-core/src/settings.rs
#	docs/guide/auction-orchestration.md
#	docs/guide/configuration.md
#	docs/guide/creative-processing.md
#	trusted-server.example.toml
# Conflicts:
#	crates/trusted-server-core/src/integrations/gpt_bootstrap.js
#	crates/trusted-server-js/lib/src/integrations/gpt/index.ts
#	crates/trusted-server-js/lib/test/integrations/gpt/ad_init.test.ts
Reconciles the squash-merged #945 (deferred initial adInit) with the RC's
own evolution of the same files:

- build_bids_script, the bundle scheduler, and the gpt_bootstrap.js
  fallback take main's final form: the SSR bids payload is handed to
  scheduleInitialAdInit, which applies it and runs adInit only on
  navigation generation 0; adInit rechecks its generation inside the
  queued googletag.cmd callback; the head bootstrap keeps the
  bundle-failure fallback scheduler.
- The RC's pathname-plus-query route identity is kept: docblocks and the
  query-change tests follow the RC semantics (a query change requests
  fresh page bids and cancels the pending initial pass), and the RC's
  slot-handoff/initial-request-gate machinery is preserved alongside the
  new scheduler.
- gpt_bootstrap.test.ts is adapted to the RC bootstrap: assertions target
  the pre-patch spies (the handoff patcher wraps defineSlot/display/
  refresh) and expect the TS fallback slot on the publisher's actual
  inner div per the RC handoff design.
Both branches fixed oversized HEAD response metadata independently with
mirrored parameter designs. Adopt main's implementation wholesale — the
request_is_head parameter plus response_carries_body(), which also covers
1xx/204/205/304 bodiless statuses — and keep the RC's cache-bypass
feature intact: send() retains its bypass_cache read alongside the new
request_is_head, and the RC's apply_fastly_cache_bypass tests are
restored next to main's bodiless-metadata tests.
…covery

Address the second review round:

- Keep rewrite_creatives defaulting to true. The measurement behind this PR
  justifies making sanitization opt-in; flipping rewriting too would silently
  disable first-party proxying for every existing deployment and invert the
  rollback serialization contract. Only sanitize_creatives (default false) is
  new behavior.
- Suppress the PBS Cache fallback when a supplied creative is rejected by
  processing (script-only after sanitize, malformed, or over the 1 MiB cap).
  The GPT bridge renders the cached bid's original adm, so retaining the
  hb_cache_* coordinates would deliver exactly the markup processing refused.
  Cache coordinates now ship only for bids that supplied no creative.
- Parse the GET /first-party/proxy-rebuild query from uri().query() instead of
  the full URI: browsers via the Axum and Spin adapters deliver origin-form
  URIs, which url::Url::parse rejected before the 302 could be issued.
- Strip bidder <base> elements in the rewrite pass itself. Rewriting emits
  root-relative first-party URLs, and a bidder base href would rebase them
  onto a third-party origin; sanitization also removes <base> but is
  independently optional.
- Resolve click-guard URLs against a pinned document.baseURI instead of
  location.href, which is about:srcdoc inside the sandboxed creative iframe
  and rejects root-relative signed click URLs as a base. Navigation URLs are
  absolutized the same way.
- Never write the GET rebuild fallback into data-tsclick: it is the canonical
  signed click used to diff later mutation waves. href updates, the canonical
  source persists, and a second mutation wave still rebuilds.
- Share one hasOpaqueOrigin predicate between the click guard and dynamic
  signing; reference issue #982 (parent postMessage broker) from the code and
  the creative-processing guide's new runtime-protections note.
- Document upgrade sequencing (binary first, then config: old binaries reject
  blobs carrying sanitize_creatives), the per-mode 1 MiB cap, and the
  requirement to add both TOML leaves before environment overlays apply; add
  CLI coverage for the sanitize overlay.
window.open(resolved, target, 'noopener,noreferrer');
} else {
location.href = url;
location.href = resolved;
if (canonFromFirstPartyClick(finalUrl)) {
el.setAttribute('data-tsclick', finalUrl);
}
el.setAttribute('href', finalUrl);
aram356 added 4 commits July 31, 2026 02:03
Aligns the rc/july copy with the cleaned version on the responsive-slot
display gating branch: the function early-returns when divId is absent,
so the per-candidate re-check always evaluated to true.
Adopts the gating revert and the hardened GPT slot handoff from #978:
- Removes the publisher initial-request gate (initialRequestGate,
  heldPublisherRequests, GptInitialRequestGate) that #978 reverted
- Takes matchingHandoff/displayTargetElementId and the responsive-slot
  helpers with ambiguous-hydration protection
- Keeps rc-only content intact: gpt_diagnostics types, the #948
  disableInitialLoad sync (syncInitialLoadDisabled wired into the
  refresh-selection path), and the #945 scheduleInitialAdInit coverage
- Drops the obsolete held-display test from schedule_initial_ad_init
  and ports the two #948 setConfig tests to the new zero-arg
  runGptBootstrap harness
The APS adapter spec loads the external Prebid bundle and assumed it
carries the tsjs shim (and the trustedServer adapter) inside it. Once
the shim is decoupled (PR #967) the external bundle is pure Prebid.js,
no adapter ever registers, and the in-page auction times out.

Detect the decoupled world through the window.__tsjs_prebid_bundle
manifest stamp and load dist/tsjs-prebid.js after the external bundle,
matching the script order the server actually serves. On a coupled
bundle the stamp is absent and the spec behaves exactly as before.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants