Tags

Tags give the ability to mark specific points in history as being important
  • 1.5.0-beta1

    1.5.0-beta1: external services collector + probe-onboarding warnings
    
    Beta tag deliberate. The Guzzle middleware API surface is one
    we hadn't touched before, and the 1.3.0 lesson (new collector
    shipped stable, wired against the wrong API tier, dead in
    production for four releases) is recent enough that a soak gate
    is the right move.
    
    What ships:
    
    - ExternalServicesInstrumentor + ExternalServicesMiddleware
      split. Middleware tagged http_client_middleware; Drupal core's
      HandlerStackConfigurator pushes it onto every Guzzle handler
      stack. Records host + elapsed time as each Guzzle promise
      resolves (fulfilled or rejected; failed requests still count).
      Short-circuits to one bool check when the instrumentor is
      disabled, so default installs pay near-zero overhead.
    
    - ExternalServicesCollector. Shape 2 (async-decoration) plus
      Shape 4 (panel-contribution), mirroring 1.4.3's
      DatabaseCollector. Resets/configures the instrumentor at
      kernel.request priority 256; ships five counters
      (call_count, total_ms, slow_call_count, slowest_ms,
      slowest_host) to AsyncContextStore at kernel.terminate.
    
    - BeaconStorage::correlateByExternalServiceCount. Buckets at
      0 / 1-5 / 6-20 / 21-50 / 51+; median CWV metric per bucket.
      The zero bucket gets its own row so operators can compare CWV
      on pages with no third-party dependency against the
      API-heavy tail.
    
    - Two new config keys via cwv_update_10037:
      external_services_instrumentation_enabled (bool, default false)
      external_services_slow_threshold_ms (int, default 500; higher
      than database's 100ms because outbound HTTP has inherent
      network round-trip cost).
    
    - Form surface added to Correlation collectors section.
    
    - Probe-onboarding hook_requirements warnings at
      /admin/reports/status: two new runtime checks flagging the
      two common half-configured states (probe_enabled on but
      nothing to probe; probes configured but master switch off).
      Driven by 1.4.1 / 1.4.4 soak feedback.
    
    - ExternalServicesInstrumentorTest unit test (7 cases) follows
      the DatabaseInstrumentorTest template. Catches the same class
      of regression that bit 1.3.0 at commit time.
    
    Library version 1.4.4 -> 1.5.0-beta1.
    
    First all-green CI on the new beta tag: phpunit, cspell, eslint,
    phpstan, phpcs, composer-lint, composer all success, no
    allow_failure crutches.
  • 1.4.4

    1.4.4: outlier filter on by default for new installs
    
    Soak feedback from an 18-day production run on cwv 1.0.1 (~554
    beacons) surfaced a 596876ms FCP beacon — a 9.9-minute value
    from a backgrounded-tab artefact — landing in storage and
    dragging the metric's p75 around.
    
    The outlier filter that catches this class of event already
    shipped in 1.1.0 (outlier_filter_enabled with per-metric
    thresholds defaulting to LCP/FCP 30000ms, INP 5000ms, TTFB
    60000ms, CLS 5), but was opt-in. The opt-in posture was
    deliberate at 1.1.0 to avoid silently shifting distributions
    for existing operators on upgrade, but that argument doesn't
    apply to new installs.
    
    1.4.4 flips outlier_filter_enabled to true in
    config/install/cwv.settings.yml. Existing installs keep
    whatever they had (config-install runs once at module install
    time; upgrade is a no-op). New installs from 1.4.4 onwards get
    the safer default automatically.
    
    No update hook. No schema change. No behaviour change for
    upgraders who hadn't already toggled the filter on. Form
    description updated to reflect the new posture and list the
    default thresholds inline so the operator can audit them
    without expanding the fieldset.
    
    Library version bump 1.4.3 -> 1.4.4 (cache-busts the JS asset).
    
    ROADMAP also updated to record the 1.4.1 - 1.4.3 correctness
    patch chain and to note that 1.5.0 will include the
    probe-onboarding hook_requirements warnings flagged by the
    same soak.
    
    First release where the d.o. CI pipeline is genuinely green
    across all seven jobs from the first run, no allow_failure
    crutches and no follow-up commit needed.
  • 1.4.3

    1.4.3: database collector rewritten for Drupal 10/11 event API
    
    The 1.3.0 collector implemented DatabaseInstrumentor as a subclass
    of Drupal\Core\Database\Log and overrode log() — the Drupal 8/9
    hook surface. Drupal 10 routed StatementWrapperIterator through
    a modern event-based path (StatementExecutionEndEvent); the
    legacy log() callback only fires when an external caller has
    called Connection::setLogger(), and core's modern path no longer
    does that. The collector compiled and wired cleanly but received
    no queries in production.
    
    1.4.1 fixed a real but unrelated priority-ordering bug that sat
    under the API mismatch; the collector remained dead. Field-test
    on 1.4.2 confirmed via direct introspection that the legacy
    callback is never invoked under Drupal 11.3.8.
    
    1.4.3 rewires to the modern API:
    
    - DatabaseInstrumentor no longer extends Log. Implements
      EventSubscriberInterface and subscribes to
      Drupal\Core\Database\Event\StatementExecutionEndEvent.
      Counters use $event->getElapsedTime(). Listener filters by
      $event->key === 'default' so multi-DB sites don't see replica
      queries counted against per-request totals.
    
    - DatabaseCollector::onRequest calls
      $connection->enableEvents([StatementExecutionEndEvent::class])
      at kernel.request when the config toggle is on. setLogger() is
      gone.
    
    - Side benefit: the 1.3.0 trade-off "replaces other database
      loggers (devel, webprofiler) for the duration of the request"
      no longer applies. Events have multiple listeners; cwv now
      coexists with any other module tapping the same event. Form
      description and panel #empty text updated.
    
    Adds DatabaseInstrumentorTest unit test (6 cases):
    - testIsEventSubscriber — fails fast if anyone regresses the
      modern-API wiring. Catches the exact class of bug that
      shipped with 1.3.0.
    - testListenerCountsQueriesWhenEnabled — counters increment on
      dispatched events, slow-query threshold honoured.
    - testListenerSkipsWhenDisabled — zero counters when disabled.
    - testListenerFiltersByConnectionKey — only 'default' counted.
    - testResetZeroesCounters.
    - testSlowQueryThresholdClampsToPositive.
    
    cwv.database_instrumentor service tagged event_subscriber again
    (1.4.1 removed it when diagnosing the priority bug; the modern
    event listener needs it).
    
    Library version bump 1.4.2 -> 1.4.3 (cache-busts the JS asset).
    No schema change, no config-key change, no update hook. First
    release where the database correlation panel actually populates
    in production since the surface was introduced in 1.3.0.
  • 1.4.2

    1.4.2: CI hygiene release (all-green for the first time)
    
    Follow-up to 1.4.1. No functional change; addresses three CI
    jobs (phpstan, phpcs, cspell) that had been silently failing
    under the d.o. CI template's allow_failure: true defaults since
    1.0.0. The "passed with warnings" badge had been treated as
    green; it isn't. 1.4.2 is the first release on this project
    where every CI job is genuinely success without allow-failure
    crutches.
    
    phpstan (4 errors, all globalDrupalDependencyInjection):
    
    - BeaconController::resolveRouteName called \Drupal::hasService
      + \Drupal::service for path_alias.manager. Injected as nullable
      AliasManagerInterface via NULL_ON_INVALID_REFERENCE so the
      controller still constructs on sites without path_alias module.
    - CwvRequestIdSubscriber::getModuleVersion called
      \Drupal::service('extension.list.module'). Injected
      ModuleExtensionList directly.
    - CwvAsyncDecoratorBase::onTerminate called \Drupal::logger('cwv')
      for finalize() failure logging. Added LoggerInterface to the
      base class constructor; updated all four subclasses
      (KernelTimingDecorator, BackendCacheCollector, RenderTreeCollector,
      DatabaseCollector) to pass through; wired @logger.channel.cwv
      into every cwv.collector.* services.yml entry.
    
    phpcs (1 error + 4 warnings, all in CwvSettingsForm.php):
    
    - 4x Drupal.Semantics.FunctionT.BackslashSingleQuote. Switched
      the affected $this->t() calls to double-quoted strings.
    - 1x Drupal.Commenting.InlineComment.InvalidEndChar. Rephrased
      the inline comment so it ends in a period.
    
    cspell (7 unknown + 1 forbidden):
    
    - "whitelist" replaced by "allowlist" in the edge-cache probe
      target description (Drupal community moved off the term).
    - Six vocabulary additions: bref, swoole, cset, mmap, munin,
      contextualise.
    
    Library version bump 1.4.1 -> 1.4.2 to cache-bust the JS asset
    on edge caches that key on the ?v= query string. No schema
    change, no config-key change, no update hook, no behaviour
    change for operators.
  • 1.4.1

    1.4.1: code-review + regression-test correctness fixes
    
    Five separate findings from a 1.4.0 code-review pass + a 1.4.0
    field-test pass on portal-dev-litespeed; folded into one tag
    rather than 1.4.1 + 1.4.2 back to back.
    
    CRITICAL bugs that 1.4.0 shipped with:
    
    1. Settings form re-corrupted sampling_overrides on every save
       (bug present 1.1.0 - 1.4.0). The outliers and sampling_advanced
       fieldsets defined LCP/INP/CLS/FCP/TTFB child fields with
       #tree=>FALSE on the parent containers, so the HTML input
       names collided and #config_target wrote outlier-threshold
       values (30000-scale) into sampling_overrides. The next save
       then failed validation, locking operators out of further UI
       changes. Fix: #tree=>TRUE on both fieldsets.
    
    2. 1.3.0 database collector was dead code in production.
       DatabaseInstrumentor's onRequest listener at REQUEST priority
       256 ran before DatabaseCollector at 255 set enabled=true from
       config, so setLogger() was never called. Counters always 0,
       context_data.database absent on every beacon. Fix: drop the
       priority-256 listener entirely; collector at 256 owns reset
       + setLogger + threshold. One ordering, no race.
    
    3. OPcache + APCu probes returned CLI-worker stats under drush
       cron (hit_rate: 0 with cached_scripts > 1000). PHP OPcache
       and APCu are per-SAPI; CLI cron sees its own instance.
       Fix: PHP_SAPI === 'cli' check in both probes, skip the row,
       log an actionable warning each cron run pointing operators
       to HTTP-context cron (curl /cron/<key>).
    
    Hardening that wasn't urgent but pays off accumulated debt:
    
    4. Textarea fields (upstream_id_headers, probe_targets) had no
       validation. Bad lines silently dropped at runtime with no
       form-save feedback. Fix: validateForm rejects malformed
       header names and non-http(s) URLs per-line via
       setErrorByName; both fields capped at 32 entries.
    
    5. isCacheableForSharedCaches duplicated verbatim across
       CwvRequestIdSubscriber and UpstreamIdSubscriber. Both are
       SA-relevant defences; copies would have diverged silently
       on next maintenance. Extracted into Drupal\cwv\Http\
       CacheControlInspector::isCacheableForSharedCaches.
    
    Operator-experience touch-ups:
    
    6. cwv watchdog channel is now seeded by hook_install, so
       operators can filter dblog by Type=cwv from the first visit
       rather than having to wait for an error to land.
    
    7. README runtime-probes section explains the per-SAPI
       OPcache/APCu state issue and the system-crontab + curl recipe.
       Form descriptions on the OPcache, APCu, and edge-cache probe
       fields call out the requirement at configuration time.
    
    Library version bump 1.4.0 -> 1.4.1 (cache-busts the JS asset).
    No schema changes, no config-key changes, no update hook.
  • 1.4.0

    1.4.0: runtime health probes + settings-form debt
    
    OpcacheHealthProbeCollector and ApcuHealthProbeCollector are
    Shape-3 collectors that snapshot per-runtime stats once per
    cron and write to cwv_probes (hit rate, memory used, wasted /
    fragmentation, cumulative restarts, entry count). Both off by
    default; toggled per-runtime from opcache_probe_enabled and
    apcu_probe_enabled, added by cwv_update_10036.
    
    HealthTimeSeriesPanel reads the most recent 30 rows for each
    collector key and renders one table per runtime; runtimes with
    no probe rows render nothing.
    
    Settings-form debt paid off: every opt-in collector that
    landed since 1.0.0 now has form surface — backend cache,
    render tree, 1.2.0 upstream-ID headers, 1.3.0 database
    instrumentation, edge-cache probe targets / timeout, and the
    new runtime probes. Operators on 1.0.0-1.3.0 used drush cset;
    1.4.0 is the canonical surface.
  • 1.3.0

    1.3.0: database collector
    
    Adds opt-in database query instrumentation as a fourth correlation
    panel alongside backend cache, render tree, and edge cache.
    
    - DatabaseInstrumentor: Drupal\Core\Database\Log subclass that counts
      queries, total time, slow queries (>= configurable threshold), and
      slowest single query without storing query text.
    - DatabaseCollector: Shape 2 (async-decoration) + Shape 4 (panel
      contribution). Wires the instrumentor onto the default connection
      at kernel.request, finalises counters at kernel.terminate, attaches
      to the later-arriving beacon via AsyncContextStore.
    - BeaconStorage::correlateByDatabaseQueryCount(): bucketed median
      aggregation (0-25, 26-100, 101-300, 301-1000, 1001+) for each CWV
      metric, mirroring the backend-cache and render-tree correlation
      shapes.
    - Two new config keys via cwv_update_10035:
        database_instrumentation_enabled (default false)
        database_slow_query_threshold_ms (default 100)
    
    Trade-off: Drupal Connection supports a single logger at a time.
    Enabling cwv database instrumentation replaces any other database
    logger (devel query log, webprofiler) for the lifetime of the
    request. Operators who need both should leave this disabled.
  • 1.2.0

    1.2.0: upstream request-ID correlation
    
    End-to-end debugging unlock that lets operators jump from a slow
    beacon to the corresponding edge log, APM trace, or load-balancer
    log entry by correlating on the upstream-stack request ID.
    
    UpstreamIdSubscriber reads a configured list of request headers
    (typically cf-ray for Cloudflare, x-amzn-trace-id for AWS,
    x-datadog-trace-id, x-cloud-trace-context for Google Cloud, or
    generic x-request-id) on the page request and stamps the first
    non-empty value into a cwv-upstream-id Server-Timing entry on
    the response. UpstreamIdCollector reads the entry from the
    beacon's server_timing array and writes context_data.upstream_id.
    
    Operators query JSON_EXTRACT(context_data, '$.upstream_id') to
    correlate slow beacons with their upstream-stack log entries.
    
    Empty by default (upstream_id_headers: {}). Operators opt in by
    listing the headers their stack provides. Adds one config key
    via cwv_update_10034. No schema changes. No contract changes.
  • 1.1.0

    2458423e · 1.1.0: stable release ·
    1.1.0: stable release on the alpha7 contract
    
    Bundles three operator-experience features from the 1.0.0 production
    soak (outlier filtering, per-metric/adaptive sampling, deploy-event
    annotations), the four 1.0.1 correctness fixes (request_id strict
    validation, top-level try/catch, cwv-rid skip on shared-cacheable
    responses, PERFORMANCE.md sampling clarification), and approximately
    twenty code-review polish items from a full review pass on 1.0.0.
    
    No contract changes. The alpha7 collector contract that was frozen
    at beta1 remains stable.
    
    Operators upgrading directly from 1.0.0 get all of the above in
    one step.
    
    See ROADMAP.md, SECURITY.md, PERFORMANCE.md, docs/collector-contract.md
    for the full picture.
  • 1.1.0-alpha3

    1.1.0-alpha3: code-review polish bundle
    
    Lands the bulk of the post-1.0.0 code-review should-fix items as
    a focused polish pass on top of the alpha1 + alpha2 feature
    bundle. No contract changes; the four collector interfaces, the
    beacon payload envelope, the cwv_beacons / cwv_probes / cwv_events
    schemas, the AsyncContextStore service contract, and the
    CwvPanelWeight constants all remain stable.
    
    Highlights:
    
    - cwv-version Server-Timing entry on every response (operator-
      verifiable shipped version regardless of JS aggregation).
    - hook_requirements status-report rows for trusted-proxy posture
      and max_rows guard.
    - Time-series event overlay on daily counts panel.
    - enforceMaxRows correctness (id-based delete) and performance
      (information_schema cheap-check).
    - CollectorRegistry and CwvAsyncDecoratorBase now log swallowed
      exceptions to the cwv watchdog channel.
    - Probe correctness: per-iteration observed_at, split connect/
      total timeout budget, multibyte-safe + bounded token
      normalisation, SSRF defence (http(s) scheme only).
    - BeaconController hardening: random_int instead of mt_rand,
      url userinfo+fragment strip, byte-safe URL truncation, alias-
      manager-aware route resolution.
    - InstrumentedCacheBackend::getMultiple records as a single
      batched observation with full elapsed time (was apportioning
      micros / requested which rounded to 0 on fast backends).
    - cwv_page_attachments skips admin and AJAX routes.
    - js/cwv.js: bfcache pageshow handling, URL fragment strip +
      2048-char cap, sendBeacon-return-value falls through to fetch
      keepalive.
    - Defensive bounds on hottest_* strings + iterator
      materialisation in CollectorRegistry.
    - Doc consistency: CwvPanelContributionInterface PHPDoc weight
      constants and docs/collector-contract.md BeaconRequest helper
      list.
    
    Items deferred from alpha3 per the review's recommendation
    (schema-affecting refactors, settings-form server-side validation,
    RenderTreeInstrumentor value-object refactor, AsyncContextStore
    last-writer-wins doc / dead  arg cleanup) land in subsequent
    1.1.x patches or as separate issues.
    
    See ROADMAP.md for the full 1.1.0 entry; SECURITY.md and
    PERFORMANCE.md unchanged from 1.0.1; docs/collector-contract.md
    gets the BeaconRequest helper-list correction.
  • 1.1.0-alpha2

    1.1.0-alpha2: bundle 1.0.1 correctness fixes
    
    Cherry-picks the four 1.0.1 fixes onto the 1.1.x branch so
    operators upgrading 1.0.0 → 1.1.0 directly get all the
    corrections without going through 1.0.1.
    
    - request_id strict-regex validation in BeaconRequest.
    - Top-level try/catch in BeaconController::receive() preserving
      the docblock 'never 5xx' contract.
    - Cwv-rid Server-Timing entry skipped on shared-cacheable
      responses (prevents request-ID corruption on edge-cached
      pages).
    - PERFORMANCE.md sampling-rate clarification (the multiplied
      effective rate).
    
    No new 1.1.0 features beyond what alpha1 shipped (outlier
    filtering, per-metric/adaptive sampling, deploy-event
    annotations); alpha2 is purely the merged correctness fixes.
    
    See ROADMAP.md 1.0.1 and 1.1.0 entries for full descriptions.
  • 1.0.1

    1.0.1: post-release code-review correctness fixes
    
    In-tree code-review pass on the 1.0.0 codebase identified three
    correctness bugs and one documentation correction. No new
    features; no behaviour change for operators who weren't hit by
    the bugs. Recommended upgrade for all 1.0.0 adopters; required
    upgrade for any deployment fronted by a shared cache (LSCache,
    Varnish, Cloudflare full-page) where the request-ID corruption
    on cached responses (B5) was actively poisoning async-decorator
    data.
    
    - request_id validation in BeaconRequest: strict 16-hex regex,
      prevents oversize-payload abuse and cross-visitor request_id
      takeover.
    - Top-level try/catch in BeaconController::receive(): preserves
      the docblock 'never 5xx' contract under any pipeline failure.
    - Request-ID Server-Timing entry skipped on shared-cacheable
      responses: prevents cwv-rid from freezing into edge-cached
      payloads and corrupting async-decorator data across visitors.
    - PERFORMANCE.md sampling-rate clarification: documents the
      multiplied effective rate (sampling_rate * sampling_rate)
      honestly with corrected sizing tables.
    
    No schema changes. No new config keys. No update hooks.
  • 1.1.0-alpha1

    1.1.0-alpha1: operator-experience polish bundle
    
    First content release on the 1.0.0-stable contract. Three operator-
    pain features identified by 1.0.0 production soak, bundled into
    one minor release.
    
    - Outlier filtering at ingest (off by default; configurable
      per-metric thresholds; dropped beacons logged at info severity).
    - Per-metric and adaptive sampling (sampling_overrides per-metric
      rates; adaptive bumping when measured throughput falls below
      configured floor).
    - Deploy-event annotations (new cwv_events table + admin UI at
      /admin/reports/cwv/events for recording deploys, config saves,
      infrastructure changes; time-series overlay rendering ships as
      a 1.1.x patch).
    
    Four new config keys, one new table, four new update hooks
    (10030-10033). Settings form expands with three new details
    fieldsets. BeaconController gets two new constructor parameters
    (KeyValueExpirableFactoryInterface, LoggerInterface) for the
    adaptive-throughput counter and outlier-drop logging.
    
    The 1.0.0 contract surfaces (interfaces, beacon payload envelope,
    schemas, AsyncContextStore, CwvPanelWeight) are unchanged.
    Existing operators see no behaviour change unless they enable
    the new opt-in features.
    
    See ROADMAP.md for the 1.1.0 entry; SECURITY.md and PERFORMANCE.md
    unchanged from 1.0.0; docs/collector-contract.md unchanged.
    
    Field test handoff: enable outlier filtering on production with
    the default thresholds, generate beacons across a few pageviews,
    verify watchdog log entries for any dropped outliers; enable
    per-metric INP sampling override at 1.0 if INP capture rate has
    been low; record a test deploy event via the new admin UI.
  • 1.0.0

    1.0.0: first stable release
    
    Promoted directly from beta1 after 10 days of clean production
    soak on a real-traffic site (71 beacons captured at 10% sampling,
    zero bugs surfaced). Skipping the rc1 ladder because the soak
    window already provided the evidence rc1 was designed to gather.
    
    The full alpha7 collector contract ships frozen and lived-with-
    not-just-frozen. Five built-in report panels, two opt-in
    correlation collectors (backend cache instrumentation,
    render-tree size), synthetic edge-cache health probe, full
    endpoint hardening (per-IP flood protection, server-side
    sampling enforcement, max_rows table guard), production-tuned
    defaults, SECURITY.md and PERFORMANCE.md.
    
    Eligible for SA coverage application.
    
    See ROADMAP.md, SECURITY.md, PERFORMANCE.md,
    docs/collector-contract.md, and docs/vision.md for the full
    picture.
    
    Post-1.0 minor cycle reordered based on production soak feedback:
    operator-pain features (outlier filtering, deploy annotations,
    sampling improvements) bundle into 1.1.0; upstream request-id
    correlation as 1.2.0; planned correlation collectors shift to
    1.3-1.5; cwv_views to 1.6; the parked HMAC and async-queue
    feature branches slot into 1.7 and 1.8; the diagnostic
    recommendations engine remains the long-term moat at 1.9.
  • 1.0.0-beta1

    1.0.0-beta1: API freeze and endpoint hardening
    
    Freezes the collector contract for the entire 1.x line and lands
    the production-readiness hardening pass on /cwv/beacon. The
    substrate has been exercised by three async decorators, seven
    panel contributors, one probe collector, and two synchronous
    collectors without contract changes since alpha7.
    
    What freezes:
    - Four collector interfaces (synchronous, async-decoration,
      probe, panel-contribution).
    - Beacon payload envelope JSON shape.
    - cwv_beacons and cwv_probes table schemas.
    - AsyncContextStore service contract.
    - CwvPanelWeight constants.
    
    Contract-cleanup drops:
    - Alpha6-envelope flat-field fallbacks in BeaconRequest.
    - Legacy cache_state field parsing in DrupalCacheCollector.
    
    Endpoint hardening:
    - Per-IP flood protection via Drupal's flood service. Default
      60/60s/IP; configurable.
    - Server-side sampling enforcement (ceiling at the controller).
    - max_rows table-size ceiling (default 0 = unlimited) with FIFO
      prune; backstop for broken-cron failure mode.
    - New SECURITY.md documenting attack surface, mitigations, and
      recommended CDN-side rate-limit configurations.
    
    Performance defaults:
    - Default sampling_rate lowered from 1.0 to 0.1 for new installs.
      Existing operators keep their explicit value.
    - New PERFORMANCE.md quantifies DB write load and sizing.
    
    Three new config keys land via cwv_update_10008.
    
    From this commit forward the 1.x contract is stable for sibling-
    module authors. RC1 cuts after a two-week soak; 1.0.0 stable cuts
    after RC1 holds clean.
    
    See ROADMAP.md, SECURITY.md, PERFORMANCE.md, and
    docs/collector-contract.md for the full picture.
  • 1.0.0-alpha10

    1.0.0-alpha10: render-tree collector + CwvPanelWeight tightening
    
    Second content release on the alpha7 collector contract. Same
    shape as alpha9: implements async-decoration (Shape 2) plus
    panel-contribution (Shape 4), opt-in via config, zero overhead
    when disabled.
    
    - RenderTreeInstrumentor captures response-level cache metadata:
      tag count, context count, hottest tag prefix.
    - RenderTreeCollector ships per-request stats to AsyncContextStore
      at terminate and renders a Render-tree correlation panel
      showing CWV metrics bucketed by tag count.
    - BeaconStorage::correlateByRenderTreeSize aggregation buckets
      beacons by tag count (0-25 / 26-100 / 101-300 / 301-1000 / 1001+).
    
    Opt-in via render_tree_instrumentation_enabled (default FALSE).
    Default installs pay zero overhead. cwv_update_10007 lands the
    key on existing installs.
    
    Also tightens CwvPanelWeight constants: adds CORRELATION = 200
    midband, bumps TIME_SERIES from 200 to 300, repositions the
    correlation panels (per-route, backend cache, render-tree) into
    the new band. Fixes the layout discrepancy spotted in alpha9
    field testing where AGGREGATION + 100 collided with TIME_SERIES.
    
    CwvPanelWeight constants are due to freeze at beta1; alpha10 is
    the last release where they can move.
  • 1.0.0-alpha9

    1.0.0-alpha9: backend cache instrumentation
    
    The first content release built on the alpha7 collector contract.
    Surfaces backend cache health (Memcache, Redis, APCu, database,
    file backends) per request and correlates it with CWV metrics on
    a new admin panel.
    
    - CacheInstrumentor request-scoped service holds per-request
      counters with per-bin breakdown for hottest-miss tracking.
    - InstrumentedCacheBackend wraps any CacheBackendInterface and
      records hit/miss/timing on get and getMultiple.
    - InstrumentedCacheFactory decorates cache_factory so every bin
      (including those added by contrib) gets the wrapper.
    - BackendCacheCollector implements Shape 2 (async-decoration)
      and Shape 4 (panel-contribution). Renders a Backend cache
      correlation panel showing CWV metrics bucketed by miss count.
    
    Opt-in via backend_cache_instrumentation_enabled (default FALSE).
    Default installs pay zero overhead; enabled installs pay roughly
    5 microseconds per cache get.
    
    Validates the one-collector-per-minor-release cadence the alpha7
    contract was designed around. Subsequent 1.x minor releases add
    render-tree (1.1), database (1.2), runtime health (1.3), external
    services (1.4) on the same contract.
    
    See ROADMAP.md and docs/collector-contract.md for the trajectory.
  • 1.0.0-alpha8

    1.0.0-alpha8: alpha7 field-test fixes
    
    Two showstopper bugs surfaced by alpha7 field testing on a real
    LSCache-fronted Drupal 11 site. Neither required architectural
    rework; both are one-line fixes against the alpha7 substrate. No
    contract changes, no new features.
    
    Operators on alpha6 should upgrade past alpha7 directly to alpha8;
    alpha7 should not be deployed.
    
    - cwv_update_10003 split into addField + addIndex so the MySQL
      schema driver can normalise the index without looking up a
      column type that doesn't exist yet. Existing alpha6 sites can
      now upgrade.
    - BeaconController async-context foreach iterator renamed from
      $value to $async_value to break the variable shadow with the
      float metric value declared earlier in receive(). Every alpha7
      beacon with at least one async observation was being silently
      dropped.
    
    README documents the synthetic-probe accuracy caveat on
    edge-cached and proxied stacks.
  • 1.0.0-alpha7

    1.0.0-alpha7: collector contract substrate
    
    The most architecturally significant release in the 1.x line.
    Reframes cwv from "real-user CWV with cache visibility" to a
    correlation substrate where cache state is one of many backend
    signal axes. The contract defines four collector shapes
    (synchronous, async-decoration, probe, panel-contribution) frozen
    at beta1 and lived with for the entire 1.x line.
    
    What ships in alpha7:
    
    - Four interfaces and default base classes for the collector
      shapes.
    - Single-tag service discovery (cwv_collector with interface
      introspection).
    - New beacon payload envelope: forwards the entire serverTiming
      array, condensed navigation timing, viewport object. Backward
      compatibility for the alpha6 envelope rides out cached HTML;
      drops at beta1.
    - Request ID mechanism: kernel.response stamps Server-Timing:
      cwv-rid;desc=<id> on every page response. Beacon JS forwards
      it. AsyncContextStore reconciles page-request observations
      with the later-arriving beacon.
    - Four migrated/new collectors exercising every shape:
      UserStateCollector and DrupalCacheCollector (synchronous),
      KernelTimingDecorator (async-decoration), EdgeCacheProbeCollector
      (probe), five panel contributors (cache comparison,
      distribution, per-route, daily counts, recent measurements).
    - cwv_probes table for synthetic observations. Cache-vendor-
      agnostic header parsing covers LSCache, Cloudflare, Varnish,
      Fastly, AWS CloudFront, X-Drupal-Cache, X-Drupal-Dynamic-Cache.
    
    No new end-user features land in alpha7; this is the substrate
    phase. Backend cache instrumentation, render-tree, database, and
    external-services collectors all build on this contract in the
    1.1+ minor releases.
    
    See docs/collector-contract.md for the full design and ROADMAP.md
    for the multi-year trajectory.
  • 1.0.0-alpha6

    1.0.0-alpha6: operator-UX polish from alpha5 field testing
    
    Three operator-UX nits surfaced by alpha5 field testing on an
    LSCache-fronted Drupal 11 site. None changed alpha5 correctness,
    but together they made the lead diagnostic look broken on edge-
    cached stacks. No API changes.
    
    - Cache state comparison falls back from drupal_cache.page to
      drupal_cache.dynamic when page is empty.
    - Empty-state copy mentions both cwv-pc and cwv-dc.
    - README documents cwv-pc non-availability on edge-cached stacks.
    - README's developer-API column list corrected.
    
    See ROADMAP.md for full details.