Skip to content

Consolidate request timing and GPT diagnostics - #1261

Draft
ChristianPavilonis wants to merge 74 commits into
mainfrom
feat/request-timing-console-combined
Draft

ChristianPavilonis wants to merge 74 commits into
mainfrom
feat/request-timing-console-combined

Conversation

@ChristianPavilonis

@ChristianPavilonis ChristianPavilonis commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Status and merge gates

Do not merge yet. This PR remains draft. The committed EdgeZero timing pin lacks the Fastly lifecycle API required by the application entry point. Updating that dependency and the store-binding migration remain separate work. GitHub also reports conflicts with current main; this batch does not merge main or update the RC branch.

Neither the contract nor cleanup batch changes dependency manifests/lockfiles, uses local dependency patches, deploys live Tinybird, or re-requests review.

Changes

Consolidates request-phase timing, private-response Server-Timing, sampled post-send Fastly access telemetry, auction offsets/UUID joins and GPT diagnostics from #1074, #1076, #1121 and #1154. All four source PRs are closed as superseded.

Access emission is off by default. The separate access_logs_v2 datasource has 31 columns, 30-day retention and its own APPEND token. The legacy nine-column access_logs_raw remains intact, without a FORWARD_QUERY or raw-path backfill. A deployed preview v2 schema needs a coordinated migration before this producer is deployed.

Agreed timing and delivery contracts

  • ts_version is the compiled TS build identifier matching x-ts-version: pipeline override, exact tag, branch, then six-character commit fallback. It is not the numeric Fastly activation version. No extra SHA or Fastly-version column was added.
  • auction_collected_ms names the persisted collection-return milestone. Internal Rust and browser auctionResolvedMs names remain unchanged. Collection return is not a separately observed provider transport-completion timestamp.
  • /auction, page-bids and HTML use shared orchestration/outcome mapping and actual collection waits, not R minus D. No-work outcomes keep wait/collection/commit null. Actual mediator-only network work can record those fields without inventing provider dispatch. Dispatch/admission choices and outgoing request counts are preserved.
  • origin_ms remains caller-blocking origin wait, including only the remaining wait after overlap. Fastly app-build means per-request setup plus cold construction when required, not a cold-start flag. Adapter T0 differences are documented rather than treated as equivalent.
  • Access ingestion uses SDK send_async followed by dropping the pending handle. Serialization/setup/start failures are observable; later transport errors, HTTP status and remote ingestion acceptance are not. There is no response wait, retry or durability guarantee.
  • delivery_result reports local transport handoff as complete, partial or error, independently of HTTP status. It does not prove client receipt. The unused duplicate delivery-byte field was removed; response bytes remain in the timing snapshot.

Diagnostics changes

Prebid targeting readers, the bidWon listener and bounded correlation state now live in the optional diagnostics bundle. The always-served shim only calls a narrow optional hook with its exact targeted Prebid instance. The served shim measures 40,983 bytes versus 42,727 at the previous batch, a 1,744-byte reduction under the unchanged artifact budget. Destruction unsubscribes only this runtime's listener when supported; otherwise the listener becomes inert and releases its recorder reference.

Badge evidence is pointer-transparent. A small native Ad #N control is placed outside observed bound creative rectangles and hidden if no viewport-fitting position exists. Selecting the latest request focuses current details without opening history; earlier selections open history.

The earlier eight targeted review fixes remain, including zero-provider diagnostics omission, disabled-auction UUID consistency, private-header gating, Prebid registration retry, Axum readiness, access-target borrowing, legacy-dataset rejection, dictionary-link referrer suppression, fixtures and rollout documentation.

Final small cleanup

9654deb7a denies cross-site page-bids before direct-handler diagnostics preparation, returns early on disabled Server-Timing emission, shares one Fastly send context, and removes duplicate GPT recorder-call branches. Access-target construction is fallible; recording-client tests cover missing credentials, sampled-out responses, independent toggles and best-effort start failures. The guide/example document route-section limitations without new validation restrictions, and remaining Axum/Wrangler wording is corrected.

Validation

Published contract batch: 7214c6058 through 02f020a80; final cleanup: 9654deb7a. Latest checks use the committed cleanup source, verified by file fingerprints. EdgeZero pins, SDK versions, manifests and Cargo.lock are unchanged.

  • Rebuilt native core binary: 3,018 tests pass with only Cargo's build-only TRUSTED_SERVER__GIT_VERSION absent. Normal cargo test -p trusted-server-core --lib --locked still fails the same 11 inherited environment-loader tests with unknown field git_version; this is not a green normal Cargo gate.
  • Axum 56, Cloudflare 55, Spin 89, parity 17, CLI 890 and native build-digest tests/lint pass.
  • Native core/Axum/CLI/codegen and Cloudflare/Spin native/WASM Clippy pass with warnings denied. Rustfmt, portable-clock and changed-domain checks pass.
  • JS build/lint/format, type checking and all 1,264 Vitest tests pass. Independently built IIFEs verify both load orders, exact captured Prebid instance, absent/throwing recorder behavior and listener teardown/reinstall. Docs build/lint/format and repository Markdown checks pass.
  • The actual tinybird.rs, platform, backend and management modules compile in a standalone WASM test wrapper with all 281 external dependency identities/checksums matching the original lock. All 94 wrapper tests and wrapper Clippy pass, including 20 tests in the actual Tinybird module, four exact source-excerpt post-send regressions from main.rs, and 70 existing platform/backend/management tests. Coverage includes async start/drop, no response wait, framing/auth/body limits, missing-token setup, independent toggles, startup failures and no retries. The wrapper does not compile or validate the full application entry point.
  • A standalone probe with exact SDK 0.12.1 and Viceroy 0.17.0 verifies eight full NDJSON request bodies, including a synchronous control and three requests in one reused sandbox. Async callbacks return in 0 to 1 ms before approximately two-second delayed replies; the synchronous callback takes about 2,005 ms. Actual runtime completion, not just client-response latency, is checked.
  • Fastly test, reuse test and application Clippy still fail only on the committed pin's missing lifecycle API. Full main.rs streaming/finish-boundary regressions remain unexecuted; the four extracted post-send tests are isolated evidence only. Native rustdoc builds with 32 retained core warnings.

A temporary-file user quota interrupted some logs/checks. Only this session's scratch artifacts were moved into the cache, and interrupted checks were rerun with retained nonempty logs. No source/dependency workaround or unrelated cleanup was used.

The SDK lifetime probe uses only local Viceroy backends. It is not a production Fastly or Tinybird guarantee. Full application Fastly/main, integration and browser verification still require the dependency gate. Real-browser geometry, hit-testing and keyboard behavior have not been exercised; current UI evidence is unit/layout mocks and independently built artifact composition.

Remaining tracked work

#1458 is the checklist with original review links and current dispositions. #1148 retains adapter support/startup diagnostics work. #1155 records delivery-result implementation and its remaining full Fastly/rollout validation.

Remaining follow-ups include route identity on fallback errors, production adapter collector/health coverage, raw-TOML secret preflight, broader historical spec/plan reconciliation and transport-completion instrumentation if needed. This PR does not claim those are implemented merely because an earlier thread was resolved.

jevansnyc and others added 30 commits August 24, 2026 19:02
* Add request phase timing design spec (Server-Timing subtimings + access telemetry)

* Address review round 1: freeze point, template-cache naming, snapshot semantics, KV scope, geo carry, route template, sink confirmation, sampling and query model, config rollback

* Address review round 2: auction-wait placement modes, conservative private-only header emission, non-null sorting key with service identity, coarse publisher route template, telemetry snapshot and outage behavior, tinybird flag decoupling, adapter phase semantics

* Add request phase timing implementation plan

* Address engineer review: KV timing decorator, try_lock sampling, route metadata extension, adapter-derived env, typed template-cache state, adapter-owned emission context, per-mode delivery semantics, Axum outer wrapper
…ite-back in middleware

Three final-review fixes for access telemetry correctness:

- Normalize the HTTP method to an allowlist (GET/HEAD/POST/PUT/DELETE/
  PATCH/OPTIONS, else "other") inside access_event_row, so a client-
  controlled extension-method token can never inflate the LowCardinality
  method column, regardless of which adapter builds the row.
- Guard emit_access_telemetry_after_send against snapshots carrying a
  degraded sample_rate of 0.0 (captured on the app-state-build-failure
  fallback path), which could otherwise be sampled in by freshly reloaded
  settings and corrupt the sum(1.0/sample_rate) volume estimator.
- Mirror the geo lookup write-back from apply_entry_point_finalize_headers
  into FinalizeResponseMiddleware::handle, so a middleware-finalized
  response that resolved geo via fallback carries the resolved
  GeoLookupState for the access-telemetry snapshot instead of showing
  country "unknown".
std::time::Instant::now() panics on wasm32-unknown-unknown, so every
publisher request on the Cloudflare adapter trapped when the timing
collector was constructed, and the two auction-wait sites would trap
once an auction dispatched. web_time re-exports std's Instant on every
other target, so Fastly, Axum, and Spin behavior is unchanged.

The publisher.rs sites are qualified locally because that module's
std Instant import still serves the pre-existing template-cache sites,
which are out of scope here.
The character allowlist alone does not bound identity: [a-z0-9_-] is
exactly the alphabet UUIDs, hex ids, reset tokens, and article slugs
are built from, and truncating to 32 characters still leaves a
globally unique prefix. A first segment now rejects whole to /other/*
when it exceeds 32 characters or carries more than 7 ASCII digits,
alongside the existing charset rejection. Year archives and
hyphenated section names still pass.

Extends the adversarial tests to the publisher-fallback path with
UUID, hex-id, token, and slug shapes, and fixes the stale event_date
reference in the row-builder doc.
- Gate building the access snapshot on tinybird.enabled and
  access_enabled, threaded through SendContext: a disabled deployment
  (the default) no longer pays env reads and String allocations on
  the pre-send path. DeliveryOutcome.snapshot becomes Option and the
  emitter treats None as nothing to send.
- Classify asset-fallback responses as route_class asset with the
  operator-configured route prefix as the template, instead of
  landing in the other/unknown bucket alongside 404s.
- Pin Phase::index() to PHASE_COUNT with a uniqueness-and-bounds
  test so a future variant fails the suite instead of panicking at
  runtime.
- Drop the tautological sampled-out emission test; the 0.0-rate
  behavior is covered by sampled_in_boundary_rates_are_unconditional.
- Clarify that the local dev config env var name genuinely triples
  trusted_server_config (prefix, store, key) rather than reading as
  a find/replace mistake.
Adds section 18 to the request phase timing spec: three first-call-wins
T0 offsets (auction dispatched, resolved, committed) on RequestTimings,
emitted as additive nullable columns on access_logs_raw with auction_id
as the join key to the per-bidder auction dataset. Answers the
overlap-proof questions the two existing clocks cannot: when the
auction started relative to request entry, when the final bid landed,
and when targeting was committed toward GAM.
Implements spec section 18: three first-call-wins marks on
RequestTimings (dispatched at the DispatchAuctionOutcome::Dispatched
arm, resolved after collect at both sites, committed after
write_bids_to_state at both sites), carried through TimingSnapshot into
four additive access_logs_raw columns: auction_dispatched_ms,
auction_resolved_ms, auction_committed_ms, and auction_id as the join
key to the per-bidder auction dataset. Null offsets mean no auction
ran; a failed dispatch records nothing. FORWARD_QUERY fills the new
columns with typed defaults for pre-existing rows.

No header emission, no config surface, no adapter changes: the values
ride the existing snapshot and the tinybird.access_enabled gate.
The Cloudflare integration harness writes
wrangler.integration.generated.toml at test time; it was swept into the
previous commit by accident. Ignore it so local CI=1 runs cannot commit
it again.
The first path segment is only a section name when the path has depth:
under a /%postname%/ permalink structure every article is a
single-segment path, so keeping those segments verbatim put full
article slugs into the 30-day dataset, against spec section 9. Depth
is now required for a named template; single-segment paths, root
landing pages included, bucket to /other/*. Route slicing keeps
route_class and multi-segment templates like /news/*.
The bucket-quantized sampler truncated rates below one in a million to
a zero threshold (silently emitting nothing) and quantized other low
rates downward while rows still carried the configured rate, biasing
the sum(1.0 / sample_rate) volume estimator. Its no-rand premise was
also wrong: rand::thread_rng() is WASI-backed on this target and the
EC generation path already relies on it. The sampler is now a direct
uniform-roll comparison, and the roll gates on the rate stored in the
snapshot itself, so emission probability and the row's sample_rate
column cannot diverge; the divergence guard and its tests are removed.

Also per review: the settings-reload fallback in the post-send path
could never emit (no snapshot exists when settings were absent) and is
removed; the dead_code allow on DeliveryOutcome narrows to the one
collected-but-unemitted field; and the post-send ordering test is
narrowed to the leg it actually proves, that request_elapsed is
stamped when send returns.
On origin failure with a dispatched auction, the origin span guard
stayed alive through the emit_abandoned_auction await, so ts-origin
and origin_ms absorbed Tinybird emission time. The span now closes
when the send resolves, before either branch, with an error-path
regression test.
- Pin HEADER_PHASES against Phase::header_name() in the phase-index
  test, closing the second hand-synced list.
- Add RouteClass::Asset to the snake_case rendering test; rename the
  lowercasing test to say what it does.
- Give the Axum adapter a named, fully configured construction path
  (TrustedServerApp::dev_server_service) so server_timing_enabled is
  never silently discarded; the tuple API is private now.
- Document that Server-Timing is client-visible when enabled, in the
  configuration guide's observability section.
- Replace stale event_date references in the spec, plan, and dashboard
  guidance with the toDate(event_ts) sorting-key expression, and state
  the single-segment rejection rule in spec section 9.
…ming

# Conflicts:
#	crates/trusted-server-adapter-fastly/src/app.rs
#	crates/trusted-server-adapter-fastly/src/main.rs
#	crates/trusted-server-adapter-fastly/src/middleware.rs
#	crates/trusted-server-core/src/publisher.rs
#	crates/trusted-server-core/src/settings.rs
#	docs/guide/configuration.md
#	trusted-server.example.toml
The access emitter carried the configured body limit without enforcing it, allowing oversized rows to bypass the intended transport safeguard.
Gate duration and placement on the actual launch flag in both collectors, without skipping collection, bid-state updates or terminal summaries. Preserve measured zero waits.

Cover empty tokens in both placements, buffered and streamed routing-only navigation, and zero-duration recording. Document null and zero semantics.
Preserve the timing-aware Axum terminal wrapper alongside main's logging defaults, retain both Fastly health contracts, and keep portable publisher clocks. Align access version rustdoc with the new header split and handle fallible normalization in route-section tests.

Validated merged JS, adapter/parity and target-matched lint gates. Fastly passes only against the disclosed local lifecycle-plus-timing candidate; the public pin remains blocked. Native core passes without Cargo's build-version environment injection, whose legacy test-loader failure is reproduced on unmodified main.
@ChristianPavilonis

Copy link
Copy Markdown
Collaborator Author

Merged current main with a normal merge commit, 686826e. Conflicts are resolved and GitHub reports MERGEABLE. The dependency manifest and lockfile are unchanged.

Posted dispositions on 90 review threads and resolved 89: 58 on this PR and 31 on the related source PRs. The current EdgeZero dependency thread, r4240834032, remains open. Deferral replies explicitly describe unchanged behavior; they supersede earlier disposition entries that overstated optional cleanup as implemented.

The approved correctness, test and versioned-schema fixes, the four follow-up fixes and the launch-gated wait correction remain in the branch. Local checks on the merged tree passed JS build/lint/format and all 1,236 Vitest tests; Axum 56, Cloudflare 55, Spin 89, cross-adapter parity 17, CLI 890, native filesystem tests, native and WASM lint including the portable-clock gate, and docs/Markdown checks. Fastly adapter 268 and core 3,002 tests, reuse, build and lint pass only against the disclosed local lifecycle-plus-timing candidate, not the committed pin.

Native core has an inherited test-environment failure from main: Cargo injects the build-only TRUSTED_SERVER__GIT_VERSION into the legacy test-only environment loader, which rejects git_version. The same failure was reproduced on an unmodified archive of main. The rebuilt merged core binary passes all 3,008 tests with only that build-only variable removed. Normal cargo test is not claimed green. Rustdoc builds but retains warnings, including the deferred Axum link correction.

Deferred work remains visible in the thread replies: timing contract/transport-completion semantics, fallback route identity and adapter wiring/health coverage, synchronous ingestion/target caching, raw-TOML secret preflight, delivery-result cleanup, diagnostics bundle/UX/privacy hardening, and documentation/refactor cleanup. No follow-up issues or deployments were created.

Current CI is still blocked by the committed EdgeZero pin's missing lifecycle API. The misleadingly named cargo fmt job fails in Fastly clippy, and the Axum job fails in its Fastly release-build verification, not the Axum tests. Integration and browser jobs remain skipped. We will wait for a stable EdgeZero release, repin with a scoped lockfile update, then get committed-dependency CI and browser integration green before re-requesting review. No review request has been sent.

@aram356 aram356 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Round 2 at 686826eaf. The fixes in 06e13366c, 3d39b1ef6 and 51aff4334 hold up. I re-ran the round-1 probes and mutations: core, JS and Fastly now catch all but one of them, the bare-private gate passes a 25-case probe, and the main-merge resolutions keep everything main added. Against the local EdgeZero combination, Fastly (268), Axum (56), wasm clippy, the 3,008 core tests (run directly; see the note below), 1,236 vitest tests and the docs build all pass. What still blocks: the EdgeZero pin (CI red), one surviving mutation on the page-bids diagnostics gate, and roughly twenty deferrals that live only in resolved threads.

6 of the inline comments carry a one-click suggestion, each verified alone against the head (rustfmt, native core clippy with -D warnings and the core suite for Rust; prettier for docs; JSON and key-order checks for the fixture). The rest are prose because they span hunks or files.

Round-1 status

Verified fixed at this head: the FORWARD_QUERY (now a versioned access_logs_v2 with no forward query, and access_logs_raw untouched), the planned-Immediate launch accounting, the disabled-auction join key (null on all three routes), the field-qualified private gate, the core timeline/placement tests (6 of 7 round-1 mutations now killed), the JS classification tests (all 5 killed), the Fastly freeze-point / geo write-back / body-boundary tests, the TimedKvStore per-method tests, the first-impression fallback facts, the poll_ready swap, the operator telemetry and Tinybird docs, the env-override and Cloudflare/Spin docs, and the ts_version rustdoc. Partial or open items are below.

Blocking

🔧 wrench

  • Page-bids diagnostics gate still survives deletion: see inline at crates/trusted-server-core/src/publisher.rs:25149
  • EdgeZero pin / CI, and deferred work with no tracking issues: see below

Non-blocking

🤔 thinking / 📝 note / ⛏ nitpick / 🌱 seedling

  • ts_version vs main's x-ts-version: see inline at crates/trusted-server-adapter-fastly/src/main.rs:1174
  • auction_resolved_ms rename window: see inline at tinybird/datasources/access_logs_v2.datasource:32
  • Mediator-only collection is untimed: see inline at crates/trusted-server-core/src/publisher.rs:4361
  • set_auction_id rustdoc after the disabled-auction change: see inline at crates/trusted-server-core/src/request_timing.rs:194
  • Comment made false by the Immediate fix: see inline at crates/trusted-server-core/src/publisher.rs:7458
  • Legacy test loop still counts Immediate as a launch: see inline at crates/trusted-server-core/src/auction/orchestrator.rs:2067
  • Fixture rows the producer can't emit: see inline at tinybird/fixtures/access_logs_v2.ndjson:1
  • Telemetry doc gaps (wait on /auction and page-bids, route templates): see inline at docs/guide/telemetry.md:58
  • Badge doc vs badges.ts: see inline at docs/guide/integrations/gpt-diagnostics.md:382
  • CHANGELOG gaps: see inline at CHANGELOG.md:46
  • README cutover steps: see inline at tinybird/README.md:57
  • No guard against the legacy dataset name: see inline at crates/trusted-server-core/src/settings.rs:2037
  • bidWon flag set before registration: see inline at crates/trusted-server-js/lib/src/integrations/prebid/index.ts:1124
  • service_fn never polls readiness: see inline at crates/trusted-server-adapter-axum/src/main.rs:94

Cross-cutting / body-level findings

  • 🔧 The EdgeZero pin still cannot build Fastly, so CI is red (open thread r4240834032). cargo fmt (required, failing in its Fastly clippy step), cargo test (required), cargo test (axum native) (failing only in its Fastly release-build step) and prepare integration artifacts fail with cannot find 'lifecycle' in 'edgezero_adapter_fastly'; the integration, Fastly EC lifecycle and browser jobs are skipped, so gpt-diagnostics.spec.ts has still never run. One thing changed upstream: EdgeZero #389's head 878be472 now contains both APIs, but it is v0.0.9 plus the two timing commits, and v0.0.9 is the logical-ID store-binding migration (#381) that #1175 is working through on this side. Repinning to it here means taking that migration in this PR; landing #1175 first and repinning to a release that includes #389 keeps the two changes separable. The lock still rewrites three unrelated edges (windows-sys 0.61.2 -> 0.48.0 for colored and winapi-util, hashbrown 0.17.1 -> 0.16.1 for gix-imara-diff), and rand is still out of order in the Fastly Cargo.toml. The PR is still not a draft.
  • 🔧 About twenty deferrals exist only in resolved threads. Resolving a thread with "deferred" removes it from view without a tracker; please file issues (one tracking issue is fine) and link them from the PR before merge. Grouped:
    • Timing contract: R - D / transport-completion capture (r4240834077), early-send ts-origin (r4240834074), warm vs cold appbuild (r4240834071, r4249515006), origin timing for asset and proxy fetches (r4249515141).
    • Route identity and adapter coverage: Fastly fallback early returns without route metadata (r4240834063), Cloudflare/Spin build_router collector and /health tests (r4249515067), the duplicated middleware test (r4249515076), Cloudflare/Spin startup warning for no-op flags (r4249515086, r4240834098; partly #1148).
    • Ingestion: synchronous block_on send, per-request target rebuild, post-send expect (r4240834069, r4249515006, r4249515122).
    • Secret preflight: inactive token references validated from raw TOML at push and carried in every blob (r4249514996, 1074 r4186272326); needs an EdgeZero CLI issue.
    • Diagnostics: Prebid shim size (273 bytes of headroom under 43 KB; r4240834080, r4249515093), prepare_request before the CSRF gate (r4249515032), badge history (r4240834119), re-entered slot labelling (r4249515107).
    • Refactors: dispatch/collect mapping x3 (r4249515036), page-bids snapshot assembly (r4249515043), SendContext x3 (r4249515130), single recorder call (r4240834083, r4249515112), upstream AxumDevServer layer seam (1074 r4186272608; no EdgeZero issue exists).
    • Docs: spec/plan reconciliation (r4240834094 and the spec halves of 1074 r4186272448, r4186272474, r4186272463).
    • Several were pre-verified one-click suggestions that cost less to apply than to track: noreferrer, the test unwrap(), the .gitignore template name, the -- punctuation, the expect message form, the stale run_auction comments, the Axum intra-doc link and the auction-token error message.
    • Existing issues to link and refresh: #1155 (delivery result; its premise that the symbols are absent and its access_logs_raw target are stale) and #1148 (still says access_enabled is rejected).
    • Three of the 10-13 replies call already-fixed items deferred: 1074 r4186272503 (disabled-path allocations), 1074 r4186272613 (routes() doc) and 1121 r4162855722 (initial-document collector presence) all landed in e6d51e36c. Correcting those replies keeps them out of the issue list.
  • 📌 The October RC still carries the round-1 Tinybird wrench. origin/rc/202610 (#1228) has the stacked-branch FORWARD_QUERY in access_logs_raw.datasource and default_tinybird_access_dataset() = "access_logs_raw"; none of e6d51e36c, 06e13366c, 3d39b1ef6 or 51aff4334 is an ancestor. A tb deploy from the RC hits the original failure, so refresh it from this branch before any Tinybird deploy from it.
  • 📝 PR description. The body is still the e6d51e36c text: "Draft and dependency blocker" on a non-draft PR, "All four originals remain open; no review threads have been resolved" (closed, and their threads now resolved), "Fixes … are in e6d51e36", 2,956/262 test counts, "FORWARD_QUERY fields align", the -- punctuation row marked implemented, and no mention of access_logs_v2. Prk-Jr's body-level items also have no reply: normalize_method's doc (CONNECT and TRACE map to other), the browser spec patching attachShadow twice, the Axum runtime and serve error context, the dictionary's GPT-reported creative label, and the split suggestion.
  • 📝 Smaller items:
    • dispatch_auction's rustdoc (orchestrator.rs:1874-1877, outside the diff) still says DispatchFailed means nothing could be started and that over-budget yields NotStarted; neither holds in the planned path now.
    • routing_only_dispatched_navigation_keeps_milestones_null uses routing = "explicit" with no bidders, which skips at routing and never reaches the NoImpressions/Immediate arm, so the planned-Immediate fix is covered at unit level only and the test name over-claims.
    • On Cloudflare, send_async executes the whole request, so a transport error after the request left the edge returns without ProviderRequestStarted and is classified as not launched (plausible, unprobed; affects GPT-diagnostics timing only there).
    • Out of scope here: native cargo test -p trusted-server-core fails 12 settings::tests env tests because Cargo injects main's TRUSTED_SERVER__GIT_VERSION at runtime; running the test binary directly passes all 3,008. It reproduces from main's build script, so it is worth its own issue.

CI Status

  • cargo fmt: FAIL (required)
  • cargo test: FAIL (required)
  • cargo test (axum native): FAIL
  • prepare integration artifacts: FAIL
  • format-typescript: PASS (required)
  • format-docs: PASS (required)
  • vitest: PASS
  • cargo check (cloudflare native + wasm32-unknown-unknown): PASS
  • cargo check/build/test (spin native + wasm32-wasip1): PASS
  • cargo test (cross-adapter parity): PASS
  • cargo test (ts CLI, native) (macos-latest): PASS
  • cargo test (ts CLI, native) (ubuntu-latest): PASS
  • CLAUDE.md symlink guard: PASS
  • CodeQL: PASS
  • Analyze (actions): PASS
  • Analyze (javascript-typescript): PASS
  • Analyze (python): PASS
  • Analyze (rust): PASS
  • browser integration tests: SKIPPED
  • integration tests: SKIPPED
  • integration tests (Fastly EC lifecycle): SKIPPED

Comment thread crates/trusted-server-core/src/publisher.rs
Comment thread crates/trusted-server-adapter-fastly/src/main.rs Outdated
Comment thread tinybird/datasources/access_logs_v2.datasource Outdated
Comment thread crates/trusted-server-core/src/publisher.rs Outdated
Comment thread crates/trusted-server-core/src/request_timing.rs Outdated
Comment thread CHANGELOG.md
Comment thread tinybird/README.md
Comment thread crates/trusted-server-core/src/settings.rs
Comment thread crates/trusted-server-js/lib/src/integrations/prebid/index.ts Outdated
Comment thread crates/trusted-server-adapter-axum/src/main.rs Outdated
@aram356
aram356 requested a review from dhruv8sh October 14, 2026 06:56

@dhruv8sh dhruv8sh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This combines request-phase timing, Server-Timing, sampled access telemetry, auction timeline joins and GPT diagnostics. The privacy design is solid: Server-Timing only goes out on conclusively private responses, and route templates and HTTP methods are bounded. It can't merge yet: the EdgeZero repin to ab444946 breaks the Fastly build, so four CI checks fail, including two required ones.

5 of the inline comments below carry a one-click GitHub suggestion. Use Commit suggestion (or Add suggestion to batch for several at once) to apply them as commits on the PR branch. The other comments describe the fix in prose because it touches multiple files or lines outside the diff and can't be auto-applied.

Blocking

🔧 wrench

  • Failed CI: cargo test (required), cargo fmt (required), cargo test (axum native), prepare integration artifacts. All four fail with cannot find lifecycle in edgezero_adapter_fastly. Because the artifacts step failed, the browser and integration suites were skipped. cargo fmt --check passes locally, so the fmt job fails at its build/clippy step.
  • The EdgeZero pin lacks the lifecycle API — see inline at Cargo.toml:59-66.

Non-blocking

🤔 thinking

  • Blocking access emission after delivery — see inline at crates/trusted-server-adapter-fastly/src/main.rs:836
  • ts-origin underreports when the origin fetch is dispatched early — see inline at crates/trusted-server-core/src/publisher.rs:5360
  • Page-bids / /auction never record Phase::AuctionWait — see inline at crates/trusted-server-core/src/publisher.rs:7470
  • Stale access_logs_raw dataset not rejected — see inline at crates/trusted-server-core/src/settings.rs:2038
  • Diagnostics badges intercept ad clicks — see inline at crates/trusted-server-js/lib/src/integrations/gpt_diagnostics/overlay.ts:104
  • Prebid shim carries diagnostics-only code — see inline at crates/trusted-server-js/lib/test/prebid-artifact-integration.test.mjs:313

♻️ refactor

  • Delivery outcome is collected but never emitted — see inline at crates/trusted-server-adapter-fastly/src/main.rs:870
  • Duplicated planned-auction outcome mapping — see inline at crates/trusted-server-core/src/publisher.rs:7441

⛏ nitpick

  • expect message convention — see inline at crates/trusted-server-core/src/publisher.rs:6196, :6262
  • unwrap() in a test — see inline at crates/trusted-server-core/src/auction/endpoints.rs:1516
  • rand dependency order — see inline at crates/trusted-server-adapter-fastly/Cargo.toml:37-38
  • Full Tinybird settings cloned per sampled request — see inline at crates/trusted-server-adapter-fastly/src/main.rs:836
  • Two names for one concept — see inline at crates/trusted-server-core/src/publisher.rs:7169
  • Spec contradictions / missing asset — see inline at docs/superpowers/specs/2026-08-24-request-phase-timing-design.md:294, :323
  • Docs should say "bare" private/no-store — see inline at docs/guide/configuration.md:2984

Cross-cutting / body-level findings

  • ⛏ route_sections trimming: ObservabilitySettings::normalize (crates/trusted-server-core/src/settings.rs) trims only non-control whitespace, so " news\t" is rejected even though the docs say names are trimmed. Either trim all whitespace before the control-character check, or say "spaces are trimmed".
  • ⛏ Duplicate docs: the [tinybird] keys are documented twice in docs/guide/configuration.md (around lines 1322 and 3053) with slightly different wording. Point one table at the other.
  • 🏕 Test harness drift: scripts/template-cache-local-test.sh:555 probes for the literal s(b,a), but with diagnostics active the seam script is s(b,a,d) (publisher.rs:6269). The comment at publisher.rs:6255 asks for the harness to be kept in sync. That file is outside the diff.

👍 praise

  • append_server_timing_if_private only emits on conclusively private responses: a bare directive is required, any edge/surrogate cache header suppresses it, and the value is appended rather than overwritten. It is thoroughly tested.
  • publisher_route_template builds templates only from the operator allowlist, and normalize_method keeps the method column bounded, so no request data reaches the 30-day dataset.
  • Sampling is decided before any snapshot work. The overlay is textContent-only, and the Prebid attempt map and diagnostics store are bounded.

CI Status

  • fail: cargo test (required), cargo fmt (required), cargo test (axum native), prepare integration artifacts
  • skipped: browser integration tests, integration tests, integration tests (Fastly EC lifecycle)
  • pass: vitest, CodeQL / Analyze (rust, js-ts, actions, python), cargo test (cross-adapter parity), spin, cloudflare, ts CLI (ubuntu, macos), format-docs, format-typescript, CLAUDE.md symlink guard

Comment thread Cargo.toml
Comment on lines +59 to +66
# Temporary integration pin for EdgeZero PR #389. Before merging TS into main,
# replace all six pins with the approved EdgeZero release tag and revalidate.
edgezero-adapter-axum = { git = "https://github.com/stackpop/edgezero", rev = "ab444946fcacb1d44c020c6079abb6ba29231402", default-features = false }
edgezero-adapter-cloudflare = { git = "https://github.com/stackpop/edgezero", rev = "ab444946fcacb1d44c020c6079abb6ba29231402", default-features = false }
edgezero-adapter-fastly = { git = "https://github.com/stackpop/edgezero", rev = "ab444946fcacb1d44c020c6079abb6ba29231402", default-features = false }
edgezero-adapter-spin = { git = "https://github.com/stackpop/edgezero", rev = "ab444946fcacb1d44c020c6079abb6ba29231402", default-features = false }
edgezero-cli = { git = "https://github.com/stackpop/edgezero", rev = "ab444946fcacb1d44c020c6079abb6ba29231402" }
edgezero-core = { git = "https://github.com/stackpop/edgezero", rev = "ab444946fcacb1d44c020c6079abb6ba29231402", default-features = false }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 wrench — This repin to ab444946 breaks the build. Code already on main uses edgezero_adapter_fastly::lifecycle (crates/trusted-server-adapter-fastly/src/main.rs:125,146, sandbox.rs:61), and this rev doesn't have that module. cargo test, cargo fmt (its clippy step), cargo test (axum native) and prepare integration artifacts all fail with cannot find lifecycle in edgezero_adapter_fastly, so the browser/integration suites were skipped and the GPT diagnostics Playwright spec has never run.

Fix (apply manually; needs an upstream EdgeZero rev): publish an EdgeZero rev or release that has both the lifecycle API and the #389 timing collector, repin all six edgezero-* dependencies plus Cargo.lock, and re-run full CI including the browser/integration jobs. Until then, please mark this PR as draft. The description already says it is not mergeable.

Comment thread crates/trusted-server-adapter-fastly/src/main.rs Outdated
Comment thread crates/trusted-server-adapter-fastly/src/main.rs Outdated
Comment thread crates/trusted-server-adapter-fastly/src/main.rs
Comment thread crates/trusted-server-adapter-fastly/Cargo.toml Outdated
Comment thread crates/trusted-server-js/lib/src/integrations/gpt_diagnostics/overlay.ts Outdated
Comment thread crates/trusted-server-js/lib/test/prebid-artifact-integration.test.mjs Outdated
Comment thread docs/superpowers/specs/2026-08-24-request-phase-timing-design.md Outdated
Comment thread docs/superpowers/specs/2026-08-24-request-phase-timing-design.md Outdated
Comment thread docs/guide/configuration.md Outdated
@ChristianPavilonis

Copy link
Copy Markdown
Collaborator Author

Published eight targeted review-fix commits, ab58ced through c4cec08. The working tree is clean; EdgeZero pins and Cargo.lock are unchanged. The PR is now draft while required merge gates remain.

Resolved 26 addressed threads with commit/check dispositions. Six non-EdgeZero threads remain open for the next contract/design discussion, plus the two unchanged dependency threads. Deferred work is tracked in #1458; #1148 and #1155 now describe the consolidation branch accurately. Three source-PR replies that incorrectly called already-implemented allocation, routes-doc and collector-presence fixes deferred have been corrected.

Validation: all 1,237 Vitest tests, JS build/lint/format, docs build/lint/format, Markdown/domain checks, Axum 56, Cloudflare 55, Spin 89, parity 17, CLI 890, native/WASM Clippy outside Fastly, Rustfmt, portable-clock and build-digest checks pass. The page-bids and Axum regressions fail under their targeted mutations and pass after restoration.

The rebuilt core binary passes all 3,011 tests without Cargo's build-only TRUSTED_SERVER__GIT_VERSION injection. Normal cargo test still fails 11 inherited environment-loader tests; this is not reported as a green Cargo gate. Native rustdoc retains 32 core warnings. Fastly test/reuse/lint remain blocked by the committed pin's missing lifecycle API, so the new Fastly constructor test and browser/integration runtime are unverified. No local dependency patch or live Tinybird deployment was used.

GitHub still reports conflicts with current main. No merge-main, release-candidate update, deployment or review re-request was performed.

@ChristianPavilonis

Copy link
Copy Markdown
Collaborator Author

Published six contract-fix commits, 7214c60 through 02f020a. All six remaining non-EdgeZero review threads are addressed and resolved; fresh verification leaves only the two dependency threads open. #1458 and #1155 now distinguish implemented changes from remaining validation/follow-ups.

The access v2 row now has TS build version semantics, auction_collected_ms and delivery_result. Actual collection waits and planned-provider outcome mapping are shared across routes, including observed mediator work without fabricated provider dispatch or new requests. Access ingestion dispatches without waiting for the reply. Prebid diagnostics moved behind the optional recorder, with exact-instance/lifecycle coverage and a 1,744-byte smaller shim. Badge evidence passes clicks through; its small control sits outside observed creatives, and latest selection no longer opens history.

Checks pass: 3,017 core tests in the isolated compiled binary, 1,264 Vitest tests, Axum/Cloudflare/Spin, parity, CLI, build-digest, non-Fastly native/WASM lint, JS/docs build/lint/format, Markdown, clock and domain checks. The actual emitter module passes 19 WASM tests and Clippy using the original dependency identities. The SDK lifetime probe verifies complete asynchronous request bodies after callback completion, including reuse, with a synchronous control.

Limits remain explicit: normal Cargo core execution still fails the inherited build-only git_version environment tests; full Fastly/main tests/lint cannot build the committed lifecycle pin, so the new delivery-boundary regressions and browser integration are unverified. Native rustdoc retains 32 core warnings. No live Tinybird deployment or production Fastly guarantee is claimed.

Dependencies/lockfile unchanged, worktree clean. PR stays draft and conflicts with current main. No merge-main, RC update, deployment or review re-request.

Deny cross-site page-bids before diagnostics setup, preserve the disabled timing fast path, and consolidate send contexts and recorder calls. Return access-target setup errors instead of panicking after send, with recording-client regressions. Document route-section limitations and finish review wording corrections.\n\nDependency pins and lockfiles remain unchanged. Full Fastly checks still require the EdgeZero lifecycle API; post-send helper coverage is isolated WASM evidence.
@ChristianPavilonis

Copy link
Copy Markdown
Collaborator Author

Implemented the approved small cleanup batch in 9654deb7a, as one commit. No dependency pins, manifests or lockfiles changed.

  • Page-bids denies cross-site requests before direct-handler diagnostics preparation. The new invalid-configuration regression fails on the old ordering and passes on both page-bids paths after the fix.
  • Disabled Server-Timing emission returns after stamping headers-ready, before parsing privacy headers.
  • All three Fastly send sites share one SendContext; both GPT opportunity paths use one recorder call with optional auction facts.
  • Access-target construction returns an error instead of panicking for an unresolved token. Post-send setup/start errors remain best-effort. Recording-client assertions cover sampled-out responses, missing credentials, independent toggles and no retry after dispatch failure.
  • The route-section example and guide document TOML-only arrays, raw-path matching and the shared other bucket. No new validation restrictions were added. Remaining Axum punctuation and Wrangler-generation wording are corrected.

Executed checks

  • Native core: the new CSRF regression passes under Cargo. The full normal Cargo run has 3,007 passes and the same 11 inherited unknown field git_version environment-loader failures. The same compiled binary passes all 3,018 tests with only Cargo's build-only TRUSTED_SERVER__GIT_VERSION absent. That isolated run is not a green normal Cargo gate.
  • Axum 56, Cloudflare 55, Spin 89, parity 17, CLI 890 and build-digest 10 pass. Non-Fastly native/WASM Clippy, Rustfmt and the portable-clock check pass.
  • All 1,264 Vitest tests pass with no type errors, including 145 ad-init tests and the independently built artifact suites. JS build/lint/format, docs build/lint/format, repository Markdown and changed-domain checks pass.
  • The original-dependency WASM wrapper passes 94 tests and Clippy with warnings denied. This includes 20 tests in the actual tinybird.rs, four exact source-excerpt post-send regressions from main.rs, and 70 existing platform/backend/management tests. All 281 external package identities/checksums match the committed dependency graph. This does not compile or exercise the full Fastly entry point.
  • Native rustdoc builds with the 32 retained core warnings.

Still blocked or unverified

Full Fastly test/reuse/Clippy checks still fail on the committed EdgeZero pin's missing lifecycle API. Full application delivery-boundary, browser and live Tinybird deployment/ingestion/quarantine/rollback proof remain outstanding. Main conflicts are unchanged.

The matching review replies and #1458 distinguish completed cleanup from larger deferred work. Only the two dependency threads are to remain unresolved. The PR stays draft, with no review re-request.

This branch has not been deployed

No deployments
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.

Improvements to TS_CONSOLE for ad observability

5 participants