Skip to content

Improve Sentry instrumentation, coroutine isolation and performance - #662

Merged
binaryfire merged 36 commits into
0.4from
upstream-sync-sentry-reconciliation
Oct 13, 2026
Merged

binaryfire merged 36 commits into
0.4from
upstream-sync-sentry-reconciliation

Conversation

@binaryfire

@binaryfire binaryfire commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

This improves Sentry's data collection, tracing and behavior in long-lived workers. It brings the integration up to date with the released PHP SDK, fixes missing or incorrectly attributed telemetry, and reduces repeated work in request collection and scope access.

Data collection and configuration

  • Require sentry/sentry ^4.34 and support its data_collection policy across requests, responses, user information, database queries, Redis commands, queued jobs and console input. Leaving the option unset preserves the existing collection settings. Command arguments and options are filtered by name instead of exposing the raw command line under the new policy.
  • Honor default_integrations, callable integration overrides, custom transports, HTTP clients and loggers. The asynchronous pool is the default transport; explicit SDK configuration takes precedence. A send without a DSN or Spotlight returns before borrowing a transport or starting a coroutine.
  • Keep the configurable feature list, use the upstream integration class names, and combine Redis instrumentation with CacheIntegration while retaining independent switches. Disabled features avoid their recording work; inactive Sentry registers no instrumentation listeners, middleware, Guzzle aspect, coroutine propagation hook or Blade decorator.
  • Fix sentry:publish handling of exported, spaced, multiline and empty dotenv assignments. An empty DSN no longer skips the prompt. Explicitly captured validation exceptions follow the application's ignore_exceptions setting; the framework exception handler still excludes them from automatic reporting.

Correct tracing and coroutine lifetimes

  • Record each outgoing Guzzle transfer once when it settles, using its final prepared request and response. Redirects previously produced duplicate breadcrumbs and could finish spans with the first response. Rejected HTTP responses now retain their status and response data. The caller's on_stats callback still runs on every hop, and rejections pass through unchanged. This covers the HTTP client, direct Guzzle clients and SDKs built on Guzzle.
  • Add configurable HTTP origin capture. Synchronous origin lookup runs only above the configured duration threshold; asynchronous calls capture their caller before it returns. Classify generated AOP frames using their original source so vendor proxies aren't mistaken for application code.
  • Preserve parent scopes and spans across nested synchronous jobs and notifications. A job's sampling middleware can only resample the transaction that job started, so it cannot drop an enclosing request or parent-job trace. Failed-job context remains available for exception reporting.
  • Keep bulk cache spans open until their last key result. Nested failover reads and writes no longer finish their enclosing span early; whole-operation failures still finish immediately.
  • Keep scheduled check-in IDs in coroutine context instead of application log context, and copy the finishing check-in so asynchronous delivery cannot change the earlier in-progress event.
  • Let child events inherit request information without replacing the application's request context or cloning its request. Keep scopes isolated, add the SDK's attachment support and callback-failure behavior, and let coroutine cancellation propagate.
  • Remove request-level bootstrap spans: workers boot before requests arrive, so those spans had incorrect timestamps. Avoid eagerly decoding JSON bodies and opening uploaded files while converting requests; moving an upload no longer causes event capture to fail. Preserve sensitive-cookie filtering and numeric cookie/header names.
  • Check the SDK body policy before reading request content. Excluded bodies are not materialized, and eligible content is read once and reused for request conversion and legacy JSON parsing.

Worker startup and related fixes

Sentry sends boot-time events and buffered logs once before the server forks. The master does not keep SDK shared connections for its workers to inherit, and workers restore the configured sharing behavior. Resolved logging channels are closed and forgotten before the fork so each worker creates its own handlers.

Sentry log channels and the deprecation channel are derived again from each worker's rebuilt configuration. Previously, literal copies of boot-time settings could overwrite the worker's settings. Explicit application channels remain intact.

The same HTTP completion correction applies to Telescope's client-request watcher. Sentry, Telescope and the HTTP client share rejected-response extraction for Guzzle 7 and 8. The exception renderer and Telescope also use the AOP source mapping. Process lifecycle log observers now accept the dispatcher's event-name/payload arguments rather than throwing before the process runs.

Performance and resource usage

The retained optimizations use direct cookie-name comparisons, construct valid PSR-7 headers together instead of cloning per header, and avoid repeated coroutine-context lookups and scope-stack copies. They add no request cache or worker-lifetime lookup registry.

Request collection, in microseconds per call:

Case Before optimization After Reduction
Legacy GET 28.5 20.7 27%
Legacy JSON POST, about 2 KB 53.7 45.4 15%
data_collection GET 27.8 19.4 30%
data_collection JSON POST, about 2 KB 32.0 23.7 26%

An isolated Hub::getSpan() comparison measured 0.446 → 0.210 µs. Copying a populated scope into a child coroutine measured 5.49 → 2.96 µs between the original and final packages.

For the fully sampled mixed HTTP workload at concurrency 32:

Metric Original package Final package
Total server CPU per request, including work after the response 2,815 µs 2,769 µs
Requests/sec 393 400
p50 latency 80.74 ms 79.38 ms
p99 latency 98.58 ms 95.12 ms
PHP heap after the complete workload and collection 11.15 MiB 11.17 MiB

The CPU reduction is 1.6%. These latency figures include queueing in a single worker driven to capacity. Small differences in other configurations were within run-to-run variation. Fully sampled tracing still has a substantial absolute cost: in this mixed fixture it adds about 1,484 µs CPU per request over excluding Sentry, while errors-only adds about 267 µs. Sampling remains important at high traffic levels.

Correct outgoing HTTP observation costs more than the previous incomplete recording:

Guzzle case, including the underlying operation Original Final
Explicit instrumentation opt-out 77.4 µs 77.4 µs
Sentry inactive 76.9 µs 76.0 µs
Sampled synchronous request 141.4 µs 153.6 µs
Sampled asynchronous request 143.5 µs 155.8 µs
Pending sampled async request, default origin capture 11,471 bytes 18,991 bytes

The approximately 12 µs increase works out to 0.06 CPU cores at 5,000 instrumented calls/sec and 0.12 at 10,000 calls/sec. These are calculations from isolated timings, not measured throughput at those rates, and exclude transaction serialization and delivery. The extra memory is about 7.34 KiB per outstanding sampled request, released when that work finishes. With 100 ms average duration, those rates imply about 3.6 MiB and 7.2 MiB extra pending memory respectively. Disabling origin capture avoids about 3.67 KiB per pending request in this fixture while preserving completion recording.

Removing the completion observer would lose final responses, redirects and rejection information. Capturing async origins at completion cannot recover a caller that has already returned. Both paths remain; synchronous origins are threshold-gated and origin capture can be disabled.

Transport saturation stayed bounded: after repeated waves, both revisions returned to ten idle connections with no borrowed connections, waiting borrowers or pending sends. Every application request succeeded. The deliberately slow sink received only about 11% of attempted telemetry on both revisions; this is a bounded transport, not a lossless queue. Separate checks verify that delivery resumes after saturation.

Measurements used an idle six-vCPU machine, PHP 8.4.25, Swoole 6.3.0RC1, matching dependencies, independent production-only apps, OPcache enabled and JIT disabled. Tables use medians of three alternating process pairs. The committed harness and results include source revisions, workloads, variation, memory checks and measurement limits. Later fixture dependency-injection and post-saturation checks were smoke-tested; the historical timings are identified separately. The tables also predate the bulk-cache counter and excluded-body read corrections, which were verified with regression tests.

Documentation and verification

Update the Sentry integration guide, Laravel porting guidance, package differences and upstream tracking notes. The guide covers configuration, instrumentation, OTLP correlation, child coroutines and transport behavior, and links to Sentry's documentation for general SDK usage.

Profiling remains disabled by default. The documentation warns that the current profiler can attribute other coroutines' work to a transaction; adoption of the proposed released SDK/Excimer APIs is tracked separately. This does not affect error reporting or tracing.

Reconcile applicable upstream source and test coverage, preserving Hypervel's coroutine and transport tests. Add regression coverage for the corrected lifetimes, configuration, request data, redirects, failures and cleanup. Validation includes affected package suites, formatting and source/type-fixture analysis, with Guzzle 7 and 8 compatibility coverage in CI.

View guided diff Turn on auto-fix

Note

Improve Sentry instrumentation with data-collection policies and coroutine isolation

  • Replaces the legacy send_default_pii behavior with a configurable data_collection policy in sentry.php. The policy now governs user info, cookies, headers, request/response bodies, SQL query parameters, queue payloads, and console command input, with sensitive-value filtering throughout.
  • Moves per-request and per-job state into CoroutineContext for coroutine isolation: queue jobs use nested job frames, scheduled check-ins store their state per coroutine, and notification spans use an owner stack so terminal events only finish the matching span. See QueueIntegration.php and NotificationsIntegration.php.
  • Rewrites Guzzle instrumentation in GuzzleHttpClientAspect.php to record spans and breadcrumbs when the request promise settles, including rejected requests and redirects, with optional slow-request origin tracing (250ms default threshold).
  • Reworks request conversion in HypervelRequestFetcher.php: manual PSR-7 construction replaces the Symfony bridge, with policy-controlled bodies, parsed-body selection by media type, and uploaded-file conversion.
  • Skips Sentry sends immediately when no DSN and no Spotlight are configured in HttpPoolTransport.php, and builds pooled transports from the ClientBuilder in HttpTransportPool.php.
  • Removes the process-wide bootstrap timestamp and bootstrap span from HTTP request transactions in Middleware.php, replacing them with request/response data collection during termination.
  • Behavioral Change: feature classes are renamed to integrations (QueueFeature → QueueIntegration, LogFeature → LogIntegration, etc.), send_default_pii is reported as IGNORED when data collection is configured, the Sentry SDK constraint moves from ^4.32 to ^4.34, and symfony/psr-http-message-bridge is replaced by vlucas/phpdotenv in composer.json.

Macroscope summarized b6550a3.

…ravel

Start the Sentry reconciliation against getsentry/sentry-laravel master
at 0537a1d5bfd583beb89001887af9cadd3116bccb (4.29.0 plus one merge).

The package now requires sentry/sentry ^4.34, the SDK release that
sentry-laravel builds on. Its manifest also declares hypervel/coordinator
and hypervel/core, which the event handler uses directly.

Five feature classes take their upstream names: LogIntegration,
CacheIntegration, QueueIntegration, NotificationsIntegration and
ConsoleSchedulingIntegration. The shipped features list follows
upstream's order and explains that applications can remove a feature
or add their own.

The provider regains upstream's explanatory comments, and the SDK
identifier comes from Version::SDK_IDENTIFIER directly instead of a
getter that only returned the constant. getSdkVersion() stays because
split installs can run a package version that differs from the
framework version.

The test base is now TestCase, matching upstream's name and structure.
ClientBuilderDecoratorTest is ported, and the provider tests that read
the client depend on testIsBound as upstream's do.

Validation: the Sentry suite under ParaTest, composer lint:fix and
composer analyse.
… sentry-laravel

Reconciles the shipped config, the data collection option, the config option
tests and the console commands with getsentry/sentry-laravel master at
0537a1d5bfd583beb89001887af9cadd3116bccb.

Config:
- Add the `data_collection` option (default null), passed through to the SDK.
- Default the additional SDK `integrations` to an empty list, as upstream does.
  The previous Transaction, FrameContextifier and Environment entries were
  installed even with `default_integrations` disabled.
- Remove the shipped ValidationException `ignore_exceptions` default. The
  framework exception handler already never reports it; explicit captures now
  reach Sentry unless the application lists it.
- Restore upstream comment wording, adapted to Hypervel's request lifecycle.

Commands:
- The about command shows the data collection status and marks
  `send_default_pii` as ignored once `data_collection` is set.
- `sentry:publish` reads `.env` entries with the dotenv parser instead of a
  line regex. Exported, spaced and multiline assignments are now recognized, so
  declining PII or performance monitoring overrides an existing value and an
  existing DSN is not requested again. A key counts as set only when its last
  assignment has a value: upstream treated an empty `SENTRY_HYPERVEL_DSN=` as
  set, skipped the DSN prompt and could finish with Sentry disabled.
- Type the test command's SDK logger and register the commands in upstream
  order.

Tests:
- Port the data collection config option test and move the container and
  integrations config option tests to upstream's placement, adding upstream's
  OTLP integration case.
- Adapt upstream's publish command regex tests to dotenv-valid dotted names,
  and add command-level regressions for exported, spaced, empty and multiline
  assignments.
- Cover default integrations, callable integrations, the shipped breadcrumb
  and tracing fallbacks, and ValidationException capture with and without
  `ignore_exceptions`.

The package now declares vlucas/phpdotenv, which the publish command uses
directly, and no longer requires hypervel/validation.

Verified with the Sentry suite under ParaTest, composer lint:fix and composer
analyse.
…aravel

Reconciles error events, request data, logging and the integration helpers
with getsentry/sentry-laravel master at
0537a1d5bfd583beb89001887af9cadd3116bccb.

Data collection:
- The auth event listeners and SetRequestIpMiddleware follow the data
  collection policy's user info setting instead of `send_default_pii`.
- SQL query breadcrumbs record query data through the SDK's database data
  collector. `sql_bindings` applies only when `data_collection` is not set.
- The request fetcher ports upstream's parsed body handling for both modes.
  Session, remember-me and XSRF cookies are filtered through upstream's
  SensitiveCookieFilter.
- The filter accepts integer cookie names. PHP stores numeric cookie names as
  integer array keys, which made a string-typed filter throw and fail event
  capture for any request carrying such a cookie.

Integrations:
- Rename ContextIntegration to HypervelContextIntegration, matching upstream.
- Narrow the model violation origin to `?array` and type its callbacks.
- LogsHandler imports Sentry's logger function.
- Restore upstream docblocks and add missing method titles.

Not ported:
- Octane event handlers. Each request runs in its own coroutine with its own
  scope and transaction name, and the request context flushes its events.
- SetRequestMiddleware. The fetcher converts the request only when an event
  needs it, instead of storing a PSR-7 copy in the worker-shared container.
- withRawQueryString(). The PSR-7 bridge already builds the URI from the
  received query string.
- sentryW3CTracingMeta(), deprecated upstream and always empty.
- Lumen routing tests.

Tests:
- Port upstream's request data collection, SetRequestIpMiddleware and request
  fetcher cases, adapted to real requests in the request context.
- Cover the auth listener, SQL query data and request IP middleware gating
  with data collection defaults and with user info disabled.
- Add a regression test for numeric cookie names.

Verified with the Sentry suite under ParaTest, composer lint:fix and composer
analyse.
…-laravel

Reconcile tracing, request and response span data, routing, views, database
spans and the traces sample rate job middleware with getsentry/sentry-laravel
master at 0537a1d5bfd583beb89001887af9cadd3116bccb.

Ported:
- With data collection configured, transactions record request headers,
  cookies and bodies, and response headers, cookies (filtered through
  SensitiveCookieFilter) and bodies.
- Query spans record parameters through DatabaseDataCollector; the tracing
  sql_bindings option applies only without data collection.
- Upstream tests for request and response data collection, the OTLP traces
  endpoint, MySQL URL connections, a missing tracing config key, database
  data collection, and trace propagation while tracing is disabled.

Defect corrections:
- Remove the app.bootstrap span. Workers boot once before serving requests,
  so on long-lived workers the span had a negative duration and
  middleware.handle started before its transaction.
- Let coroutine cancellation propagate through the tracing event handler.
  Query origin resolution reads files through hooked I/O, which can be
  cancelled; the handler previously swallowed the cancellation.
- The request fetcher no longer uses Symfony's PSR-7 bridge. The bridge
  decoded every JSON body on every sampled transaction and error event
  (about 7.5 ms per MB), but Sentry only reads that body for legacy error
  events within the size limit. The fetcher now builds the PSR-7 request
  directly and decodes only bodies the legacy collector can read.
- Uploads the application moved no longer drop events. The bridge opened
  each upload by its real path and threw once it was moved, and the SDK
  discards events whose processors throw. Uploads are now converted by
  path without being opened.

Adaptations:
- Testbench requests run in their own coroutine, so the propagation test
  reads the scope inside the route.
- Duplicate Hypervel tests are consolidated into the upstream tests, keeping
  their stronger assertions.
- The view engine test uses ParallelTesting::tempDir().
- The view engine registration drops a compatibility catch for Laravel
  versions before 5.4.

Validation: Sentry suite under ParaTest (467 tests), AfterEachTestSubscriber
tests, composer lint:fix and composer analyse.
…ravel

Bring the cache, Redis, queue and notification features in line with
getsentry/sentry-laravel master (0537a1d5bfd583beb89001887af9cadd3116bccb)
and fix span and scope handling for nested notifications and jobs.

Cache and Redis:
- Redis command tracing moves into CacheIntegration, as upstream has it,
  and RedisFeature is removed. Redis tracing, cache tracing and cache
  breadcrumbs keep independent switches; Redis events are enabled at boot
  only when Redis tracing is on.
- Failed-command spans, null timings, pool data and the coroutine ID are
  kept. The duplicate db.statement and duration fields are dropped, which
  makes 100 Redis spans about 5 KB smaller and quicker to serialize.
- Redis parameters follow the data collection policy (send_default_pii in
  legacy mode).
- Session keys are read from the request cookie before constructing the
  session store. Upstream's cache-backed session handling is not ported:
  Hypervel has no cache-backed session handlers.

Queue and notifications:
- Queued job arguments are collected on publish and process spans when
  data collection allows it.
- The notification breadcrumb listener is only registered when
  notification breadcrumbs are enabled.

Defects fixed:
- A skipped notification, or one that failed before sending, finished the
  span of the notification that sent it. Spans are now finished only by the
  notification attempt that started them, which also ignores the extra
  terminal event from channels that report a failure without throwing.
- A job run synchronously inside another job (sync chains, dispatchSync)
  popped the parent job's scope, lost its breadcrumbs and attached later
  spans to the wrong parent. Failure events could also finish the parent's
  span or transaction. Jobs are now tracked per coroutine; a failed job's
  scope stays available for exception reporting until its attempt has
  ended and the next job starts or its parent completes.

Tests:
- Port QueueDataCollectionTest. Publish spans come from the JobQueueing and
  JobQueued events, which the sync queue does not raise, so the upstream
  cases raise them through a test queue; a real sync push confirms the
  process span without a publish span.
- Merge the upstream cache and Redis tests with Hypervel's Redis tests.
- Notification tests use the real config keys; upstream sets keys that do
  not exist.

Verified with the Sentry suite under ParaTest, composer lint:fix and
composer analyse.
Bring the console, scheduling and storage features in line with
getsentry/sentry-laravel master (0537a1d5bfd583beb89001887af9cadd3116bccb)
and fix how scheduled task check-ins are stored and finished.

Console:
- Once the data_collection option is set, command breadcrumbs record the
  arguments and options that differ from their definition defaults, as
  separate input.arguments and input.options arrays. Values pass through
  the SDK's denylist filter, so names such as password, token or secret
  are sent as [Filtered]. Upstream sends the raw command line here, which
  would leak credentials under the documented migration settings.
- Without data_collection, raw input is still sent only when
  send_default_pii is enabled.

Scheduling:
- Upstream's useCacheStore() and its cache and Context hand-off of
  check-in IDs are not ported. Background tasks run in a coroutine of the
  scheduler process, which both starts and finishes their check-ins.

Defects fixed:
- Check-ins were kept in hidden application log context, which is copied
  into queued job payloads and scheduled subprocess environments. They now
  live in coroutine context and are removed when the task finishes.
- The finishing check-in changed the status of the in-progress check-in
  object. With asynchronous delivery, the start check-in could be sent as
  ok or error. The finish now sends a copy.

Tests:
- Port ConsoleExceptionScopePropagationTest.
- Merge the upstream scheduling tests: schedule override, the inspire
  command, background tasks run through schedule:run, and overlapping
  executions run concurrently.
- Rename tests to their upstream names where Hypervel duplicated them.
- Use dotted config keys in the console tests.

Docs describe command input and Redis parameters under data_collection.

Verified with the Sentry suite under ParaTest, composer lint:fix and
composer analyse.
…econciliation

# Conflicts:
#	tests/Sentry/Aspects/GuzzleHttpClientAspectTest.php
A custom driver that wraps another disk, such as Sentry's storage
decorator around an S3 or Google Cloud Storage disk, received its config
with the pool option stripped. The wrapped cloud disk then lost its
pool.fingerprint, pool.name and pool options, so it fell back to
automatic fingerprinting and default pool limits.

Custom drivers the manager doesn't pool now receive the full disk config,
including pool, so they can pass it on to Storage::build(). Custom drivers
the manager pools, and the built-in drivers, still receive the config
without pool, since the manager owns that pool. The custom filesystems
documentation describes the difference.

Verified with FilesystemManagerTest (the new regression fails without the
fix) and the Filesystem suite under ParaTest.
AOP proxies are generated under the bootstrap cache path, so stack frames
from a proxied vendor class, such as Guzzle's Client, report a file inside
the application. Code that sorts frames into application and vendor code
treated those frames as application code: the error page marked them as
application frames, and Telescope could report a proxied vendor class as
the caller of a query or request.

GenerateProxies now records the canonical source file of each ordinary
and replacement proxy once loaded-target validation succeeds, and exposes
the map through proxySources(). The entries are kept across bootstraps,
since already-loaded proxy classes stay in use, and are reset by
flushState(). The map is bounded by the number of proxied classes.

The error page's Frame::isFromVendor() and Telescope's stack trace caller
lookup classify a proxy frame by its source file, while still showing the
proxy frame's real file and line. Foundation now declares its existing
direct use of hypervel/di.

Verified with GenerateProxiesTest, FrameTest, PackageMetadataTest and the
new QueryWatcherTest case (each fails without the change), and the Di,
Foundation and Telescope suites under ParaTest.
Telescope's ClientRequestWatcher intercepts Guzzle's Client::transfer so
it also sees raw Guzzle clients and SDKs, not only the HTTP client's
events. It recorded an entry from on_stats, which Guzzle calls once per
redirect hop, so a followed redirect produced an entry for every hop. A
response Guzzle rejected (http_errors) was recorded without its response,
and a blanket catch hid recording failures.

The watcher now matches laravel/telescope 5.x (bedfc50a, checkpoint not
advanced), which records one entry per logical request:
- on_stats only keeps the last transfer stats and still calls the
  caller's own on_stats callback on every hop.
- One entry is recorded when the transfer settles, from the last prepared
  request (or the entry request without stats) and the final response:
  the fulfilled response or the response carried by the rejection.
  Rejections are passed on unchanged.
- duration is only set when Guzzle reports a transfer time.
- Recording errors surface, like upstream and the other watchers.
- Masking, size limits, hypervel_data, telescope_tags, ignore_hosts and
  telescope_enabled are unchanged.

Guzzle 7 and 8 carry a rejected response on different exceptions.
PendingRequest's existing extraction moves to
Hypervel\Http\Client\Guzzle\RejectedResponse::from() so the watcher and
Sentry's Guzzle aspect share it; responseFromException() delegates to it.

Tests:
- Restore upstream's HTTP client tests to their Http::fake() form.
- Add regressions for a followed redirect recorded once with the final
  response, an error response rejected by Guzzle, and recording failures
  surfacing from the transfer.
- Remove five Hypervel tests duplicated by the retained raw request,
  masking, failure and redirect coverage.

Verified with ClientRequestWatcherTest (the redirect regression fails on
the old watcher) and the Telescope and Http suites under ParaTest on
Guzzle 8.2. On Guzzle 7.15.5 the Telescope suite passes; the Http suite
segfaults in HttpClientDestinationPolicyTest from a Swoole 6.2.2 curl
hook use-after-free that also occurs without these changes.
Bring HTTP client spans and breadcrumbs in line with
getsentry/sentry-laravel master (0537a1d5bfd583beb89001887af9cadd3116bccb)
and fix how the Guzzle aspect records redirected and rejected transfers.

Instrumentation:
- Upstream's HttpClientIntegration feature is not ported. The aspect on
  Guzzle's Client::transfer instruments every Guzzle client, including the
  HTTP client and SDKs built on Guzzle, and its spans keep the
  auto.http.guzzle origin. The no_sentry_aspect option still opts a client
  or request out, and the README records the difference.
- The span and breadcrumb are recorded once, when the transfer settles,
  from the last prepared request and the final response. Previously
  on_stats recorded a breadcrumb for every redirect hop and finished the
  span with the first hop's response, and span data came from the request
  before Guzzle applied options such as query. The caller's on_stats still
  runs on every hop, rejections are passed on unchanged, and the Hub span
  is never replaced. Error responses rejected by Guzzle keep their status
  through the shared RejectedResponse helper.
- When both span tracing and breadcrumbs are off, the aspect still adds
  trace headers but leaves the caller's on_stats unwrapped.

Data collection:
- Legacy mode keeps its fields and adds http.request.body.size.
- With data_collection, spans and breadcrumbs add url.full and a filtered
  http.query, and spans add the SDK's request and response header, cookie
  and body collection.

Origin:
- Port upstream's http_client_requests_origin and
  http_client_requests_origin_threshold_ms options. Their config casts
  replace upstream's two consumer-coercion tests (REMOVED comments).
- A synchronous request over the threshold resolves its origin from 40
  frames when it settles, since its caller sits below the promise wait and
  the proxied Promise::wait() frames. An asynchronous request with a
  sampled span captures 64 frames when the transfer begins, because it can
  settle after its caller returns. The AWS SDK sends synchronous calls this
  way, below its middleware and the storage decorators. An SDK that queues
  the transfer itself reports the site that waits for it.
- resolveEventOrigin() accepts a backtrace captured earlier; other
  features keep their 20-frame capture.
- Generated AOP proxies are classified in or out of the application like
  their source files: after the client options are built, proxy paths are
  added to the SDK's include or exclude lists where the effective rules
  would classify a proxy differently from its source. An explicit include
  covering a proxy still wins.

Package metadata declares the direct guzzlehttp/promises dependency. The
CI Guzzle 7 job also runs the new HTTP client tests and Telescope's
ClientRequestWatcherTest. The Sentry docs describe HTTP client coverage,
the origin settings and no_sentry_aspect.

Tests:
- Port HttpClientIntegrationTest, HttpClientDataCollectionTest and the
  HttpClientOriginFixture, sending faked HTTP client requests in place of
  upstream's dispatched events.
- Add redirect, query option, rejected error response, async coroutine
  ownership, SDK origin and disabled-output regressions to
  GuzzleHttpClientAspectTest, and a combined Sentry and Telescope origin
  test.
- Add proxy classification cases to ServiceProviderTest, origin config
  casts to ConfigTest, and a decorated cloud disk pool regression to
  StorageIntegrationTest.

Verified with composer lint:fix, composer analyse, and under ParaTest on
Guzzle 8.2 the Sentry (520), Telescope, Filesystem, Http, Di, Foundation,
Saloon and ApiClient suites. The redirect, origin depth, SDK origin and
disabled-output regressions fail against the previous code. On Guzzle
7.15.5 the CI compatibility set (330 tests, 7 existing version-gated
skips) and the Sentry, Telescope and Filesystem suites pass, and caller
selection was checked against real cURL transfers on both families. The
Guzzle 7 Http suite segfaults in HttpClientDestinationPolicyTest from a
Swoole 6.2.2 curl hook use-after-free that also occurs without these
changes, so Guzzle 7 validation is not complete. Timing comparisons
belong to the later performance assessment.
Reconcile Sentry's Hub, child coroutine propagation, cancellation and
flushing with sentry/sentry 4.34.0 and getsentry/sentry-laravel master
(0537a1d5bfd583beb89001887af9cadd3116bccb).

Request inheritance:
- Child propagation cloned the parent's request into the child's
  application request context. Created children gained a request the
  framework does not give them, and forks lost the parent's request
  identity. Children now get a reference under
  HypervelRequestFetcher::REQUEST_CONTEXT_KEY, which only Sentry reads,
  through HypervelRequestFetcher::currentRequest() (the coroutine's own
  request first). Created grandchildren keep it; detached and delivery
  coroutines get none. The request fetcher and the cache session key use
  it.
- Without a request, the user IP set on Authenticated and Sanctum
  TokenAuthenticated events came from the container's default request, a
  fake 127.0.0.1. It now comes from currentRequest(), so no IP is set
  without a request. Created children previously read the cloned real
  request; they now read the inherited one, which the old make('request')
  reader would miss after the propagation change. Upstream records the
  console request's 127.0.0.1.

Hub:
- Match SDK 4.34.0: before_breadcrumb, traces_sampler and
  profiles_sampler failures are logged and fall back as the SDK does, and
  addAttachment() is added.
- Coroutine cancellation from those callbacks propagates unchanged.
- The class docblock requires comparing it with Sentry\State\Hub on every
  SDK update. Methods have titles, and withScope() and captureCheckIn()'s
  duration are natively mixed, since the SDK interface leaves them
  untyped.

drainEvents() is marked internal and the per-class span context prefix
is protected. Flushing is unchanged: runtime contexts started for HTTP
requests, jobs, scheduled tasks and WebSocket events flush logs, metrics
and the transport when they end, console commands flush on terminating,
and worker stop and exit drain with a bounded timeout, so upstream's
extra flush points are not needed.

Tests:
- Add Hub callback failure, cancellation and attachment tests, auth IP
  tests without a request and in a child coroutine, and propagation
  assertions for the application context, captured request data, fork
  identity and descendants.
- Two child-coroutine tests passed when the child threw, because a
  timed-out channel pop returned false; they now assert the popped
  result.
- Remove eight HttpPoolTransportTest tests that repeated the file's
  remaining acquisition, release, failure and close coverage, and
  CoroutineSafetyTest's fake context trait test. Replace the
  sentryConfigWith() helper with dotted config keys.

Verified with composer lint:fix, composer lint, composer analyse and the
Sentry suite under ParaTest (519 tests). Against the previous code, the
Hub failure-fallback and attachment tests, the no-request auth IP test
and the changed propagation tests fail. The cancellation cases pass
there, since the old Hub called the callbacks directly; they protect the
new catch paths. The child-coroutine auth IP test also passes there,
since the old propagation cloned the request; it fails with the new
propagation and the old reader.
php-cs-fixer's no_unused_imports rule keeps an import when its name
appears in any comment, ignoring case, so these imports survived
formatting although no code or docblock uses them.

Verified with composer lint and composer analyse, and the affected test
files pass.
Compare every getsentry/sentry-laravel source file and the config at
master (0537a1d5bfd583beb89001887af9cadd3116bccb) with Hypervel's Sentry
package, including files reconciled earlier.

Source:
- Add return types to the untyped closures in the provider bindings,
  routing dispatchers, FilesystemDecorator, LogIntegration's log
  creators and Integration::handles().
- Add the missing method titles to the model violation reporters,
  LogIntegration, WorksWithSpans and both handleBatch() methods.
- Remove the catch (BindingResolutionException) blocks around
  make('events') in the provider. The base service providers always
  register the event dispatcher, so they could never run.
- Restore upstream's method order in Integration and
  Tracing\Middleware, and its comments in Tracing\Middleware and
  BacktraceHelper. Correct the comment that blamed a missing compiled
  view path comment on a very old Laravel version.
- Note where upstream's TransactionFinisher and Lumen route name
  extraction are not ported: each request coroutine finishes its own
  transaction, and Hypervel has no Lumen routing.

Kept: parameterless Feature::onBoot() hooks that resolve from the
container, modelHasAttribute() (Hypervel's hasAttribute() also counts
casts), and no normalizeKeyOrKeys(), since the cache repository
dispatches resolved key lists. The README differences remain accurate.

Set the sentry-laravel sync checkpoint to the compared revision, with
notes on the provider and FEATURES mapping, the feature hook shape,
comparing Hub with the SDK's on every SDK update, the packages Hypervel
does not have, and AI agent monitoring, which is not ported yet and not
covered by the checkpoint.

Verified with the Sentry test suite under ParaTest, composer lint:fix
and composer analyse.
SentryTracesSampleRate resampled Hub::getTransaction(), the root of
whatever span was current. Job middleware runs after JobProcessing, so a
job dispatched synchronously during a request, or by another job,
changed the sampling of that enclosing transaction: with a rate of 0, a
whole request trace or parent job transaction was dropped. With queue
job spans disabled, a nested job did the same to its parent job's
transaction. getsentry/sentry-laravel master
(0537a1d5bfd583beb89001887af9cadd3116bccb) has the same code.

QueueIntegration's job frames now hold the span they started instead of
an ownsSpan flag, and the internal currentJobTransaction() returns the
running job's span only when it is a transaction. The middleware
resamples only that transaction, still never upsamples, and always
calls the next handler. Its hub-bound check is gone: the lookup does not
use the hub, and an inactive integration has no job frames.

Upstream's five sampling tests resampled a transaction no job started.
They keep their assertions and now start the transaction through
JobProcessing and finish it with JobProcessed. Dispatch tests cover a
root job's own transaction, a job inside another transaction and a
nested job without a span; the latter two fail on the previous code.

Verified with the middleware and queue integration tests, the Sentry
suite under ParaTest, composer lint:fix and composer analyse.
Compare the Laravel section of getsentry/sentry-docs at master
(4307693c8537fecfe123e80d50837c5c94979585) with the Hypervel Sentry page
once, keeping Hypervel integration guidance and linking Sentry's
documentation for general SDK usage instead of repeating it.

Added:
- Model violation reporters, linking browser traces with sentryMeta(),
  log batching with SENTRY_LOG_FLUSH_THRESHOLD, instrumentation toggles
  and spans in child coroutines.
- Scheduled task monitor options, and why tasks filtered with between,
  unlessBetween, when or skip report missed check-ins.
- That the job sampling middleware needs job transactions and leaves an
  enclosing transaction's sampling alone.
- Sending OpenTelemetry traces to Sentry over OTLP, and linking Sentry
  events to them with OTLPIntegration. OtlpIntegrationTest checks that
  captured events carry the active OpenTelemetry trace and span IDs.
- The data_collection option, custom features and client builder
  customization.

Corrected:
- The traces_sampler example used a closure, which blocks
  config:cache, and checked a route name that never matched, since HTTP
  transactions are named after the path when sampled.
- Validation exceptions are dropped by the exception handler, not by
  the Sentry config.
- Explicitly copied requests are shared with detached children, not
  cloned.
- Request headers are sent without send_default_pii, with sensitive
  header values filtered, and other event data may still contain
  personal information.

The porting guide gains a Sentry entry for the namespace, the
SENTRY_LARAVEL_DSN rename (without it Sentry stays disabled) and
SENTRY_LOG_LEVEL. The sentry-docs sync entry is removed: future
documentation changes accompany package changes.

Verified with OtlpIntegrationTest, the Sentry suite under ParaTest and
composer lint:fix.
Excimer samples whichever coroutine's PHP stack is running, so a
transaction profile includes work from other coroutines in the same
worker, such as concurrent requests. Profiling stays disabled by
default; the config and documentation now say so for both
profiles_sample_rate and profiles_sampler. Error reporting and tracing
are unaffected.

The todo entry records the fix once its dependencies are released:
the sentry/sentry per-transaction profiler factory
(getsentry/sentry-php#2253) and Excimer's sample filter
(https://gerrit.wikimedia.org/r/c/mediawiki/php/excimer/+/1354126),
with the attribution, lifecycle, cleanup and overhead checks needed
before the warning is removed.

Verified with composer lint:fix.
The service provider set the pooled transport directly on the client
builder, which takes precedence over the SDK's transport option, so a
transport passed through the configuration or set on the builder's
options was ignored. The pool also built its own HTTP clients, so the
http_client option, a client set on the builder, and the builder's
logger never reached the pooled transports.

The pooled transport is now the default transport option, installed
only when none is configured, so the SDK's precedence applies: a
transport set on the builder or in the options replaces it. Pooled
transports are created on first use from the customized builder's
options, HTTP client and logger; without a configured client, each
transport still gets its own SDK client identified as the Hypervel SDK.
The pool settings are only read and validated when the pool is used.

Sends without a DSN or Spotlight are skipped before borrowing a
transport or starting a delivery coroutine, as the SDK transport would
skip them anyway. Spotlight-only sends are still delivered
asynchronously.

The documentation now says to set a custom transport on the builder,
since objects in config/sentry.php prevent config caching, and that a
custom HTTP client keeps asynchronous delivery.

Verified with the Sentry suite under ParaTest, composer lint:fix and
composer analyse, plus mutation checks for the default-transport guard,
the early skip, the Spotlight path, options-level installation and the
builder's HTTP client.
When a coroutine test threw while child coroutines it started were still
blocked, the runner waited for those children until the test's time
limit and then reported only "This test was aborted after N seconds".
The assertion failure or error that caused the problem was lost.

If the test method returned or threw before the deadline and its
children are what reached the limit, the runner now rethrows the test's
own exception. Failures and errors are reported with their original
message. An exception the test expected would otherwise make PHPUnit
pass the test, so the runner keeps it and a post-condition fails the
test, naming the leftover children with the expected exception as the
cause. Skipped and incomplete tests never reach post-conditions, so
they keep the time-limit abort. Passing tests that leave children
running still time out, and children are not cancelled earlier than
before.

The time-limit fixture covers failures, errors, expected exceptions
and assertion failures, and skipped and incomplete tests, each with
children still running at the limit, and checks that tearDown() still
runs. Expected-exception tests whose children finish in time still
pass, with the children completing normally.

Verified with TimeLimitTest, composer lint:fix, composer analyse and
the full composer test:parallel suite, plus mutation checks that
disabling the rethrow or the skipped/incomplete exclusion fails the
new cases.
Six Sentry tests start child coroutines that wait on a channel until
the test releases them. The releases ran after the test's assertions,
so a failed assertion left the children blocked until the time limit.

The releases, and the joins where the test waits for the children, now
run in finally blocks, so a failure is reported straight away and the
children finish.

Verified with the five affected test files, composer lint:fix and the
full composer test:parallel suite.
Integrate the latest HTTP transport reuse, response-failure handling, database date-format caching, coroutine cleanup and provider configuration changes.

Retain the shared Guzzle rejection-response helper with the exception import required by the new fake transport paths. Combine Guzzle compatibility coverage and preserve filesystem wrapping coverage through the real Sentry decorator without duplicating the manager test.

Validated with 44,238 full-suite tests, 1,218 focused integration tests, 93 filesystem tests, formatting and both static-analysis passes. Guzzle 7 CI retains destination-policy coverage and reports the known native Swoole failure without exclusions.
The coroutine test failure-reporting fix is preserved as f40df6c on upstream-sync-framework-19. Remove it from the Sentry change set so it can be reviewed and published with the independent framework fixes. Keep the Sentry tests' exception-safe child cleanup here.

Restore the three runner files exactly to the current 0.4 versions; no Sentry source changes are included.
The server-process provider registered its start and stop loggers as event observers, but typed their callbacks to receive the event object. The dispatcher calls observers with the event name and payload, so every BeforeProcessHandle dispatch threw a TypeError inside AbstractProcess::bind() before the process's handle() method ran, and every AfterProcessHandle dispatch failed the same way.

Accept the observer arguments and pass the event from the payload to each listener.

Add a regression test that dispatches both events through a real dispatcher and asserts both log lines. It fails with the TypeError before the fix and passes after it. Formatting, static analysis and the ServerProcess and Signal suites pass.
The server process boots the application once, then forks every worker and process. Two pieces of logging state did not survive that model.

A deprecation raised while the server booted wrote a deprecations channel copied from the boot-time configuration. Workers rebuild configuration from their own environment, but the copied channel was never recorded for replay, so they lost it or kept the server's driver. Record the derivation through the configuration mutation tracker so each worker derives the channel again from its rebuilt configuration, and keep an explicitly configured channel. The tracker and ServiceProvider::configureUsing() now accept the configuration contract, since neither needs the concrete repository.

Log channels resolved during boot were inherited by every forked process. Buffered records were written once per process, and workers shared the server's handlers and their open resources. Before the server forks, close each resolved Monolog channel so buffered records are written once, then forget every channel so workers create their own.

Add regression tests for deprecation channel replay and for closing and forgetting channels before the fork, including buffered stacks and custom loggers without a Monolog lifecycle. Each fails before the fix. Formatting, static analysis and the Foundation, Support, Log, Integration/Log and Testbench exception suites pass.
The server process boots the application, then forks every worker and process from that state.

With the SDK's default cURL share handle, connections the server opened while booting were kept in it and reused by every forked process at once. Until a forked worker or custom process starts, the server process now sends without connection sharing. Each forked process then restores the setting. This runs after builder extenders, so an application that disables sharing keeps it disabled, and one that enables it gets it back after the fork. Console processes are unchanged.

Events and logs captured while the server booted were inherited and sent by every forked process. Send them once before the server forks. This runs after the log provider has closed buffered channels, so their records are included.

The Sentry log channels were written into configuration from boot-time values and replayed literally into workers. Compute them through configureUsing() so each worker derives them from its rebuilt configuration, keeping channels the application defines.

Add regression tests for the pre-fork send, including buffered log channels and a later flush that sends nothing again; for connection sharing across worker and custom-process starts and the application's chosen setting; for console processes; and for log channel replay. Each fails before the fix. Formatting, static analysis and the Sentry, OpenTelemetry, Log, Integration/Log, ServerProcess and Signal suites pass.
Bring in the merged Inertia, Slack, filesystem pooling and worker-lifecycle fixes while preserving Sentry instrumentation and coroutine-specific cleanup. The merge applies without conflicts and retains both sides of the test-state cleanup changes.

Validated the combined tree with the Sentry, Telescope, Filesystem, Inertia and PHPUnit-support tests, plus source and type static analysis.
Preserve the Sentry assessment harness under tests/Benchmarks with portable application setup, explicit smoke and measurement modes, and documented baseline comparisons. Keep generated applications and raw output outside the repository.

Cover HTTP instrumentation, transport saturation, request fetching, Guzzle spans and coroutine propagation. Own subprocess lifetimes, reject occupied ports, and fail runs when response or telemetry checks disagree.

Validate both revisions with HTTP and component smoke checks, instrumentation inventories, failure cleanup, PHPUnit discovery and PHP/shell syntax checks. Full performance measurements remain pending an idle-machine run.
Build PSR-7 headers together, retain the invalid-header fallback, and preserve numeric header names. Use direct comparisons for fixed sensitive cookie names and avoid repeated context lookups and array copies in Hub access.

Verify header preservation and cookie filtering alongside the complete Sentry suite and static analysis.
Quote the null channel name and verify cached configuration. Disable OPcache file-update protection for the freshly generated immutable benchmark files so workers measure cached views rather than repeated compilation.

Record runtime flags and a final garbage-collected resource snapshot outside timed workloads to distinguish retained state from collectible cycles.
Document paired HTTP and component results, request-collection optimizations, disabled instrumentation, pending Guzzle memory, and bounded transport behavior under saturation. Include absolute CPU costs, observed variation, runtime settings, and the corrected benchmark setup.
After recording both saturated waves and resource snapshots, remove the sink delay and require one error and one transaction from a single request on the same server. This catches a closed transport pool even when its counters have drained.

Inject the fixture controller services and distinguish historical timing results from these smoke-tested corrections. Verify normal recovery on both revisions and failure with a deliberately closed pool.
Bring in the Swoole 6.3.0 RC1 requirement and current runtime regressions while preserving the Sentry SDK minimum and expanded Guzzle compatibility coverage.

Verified the conflict-free merge with refreshed dependencies, formatting, source and type-fixture analysis, and the affected Sentry, HTTP, Telescope, DI, filesystem and lifecycle suites. A native AIO shutdown hang was reproduced independently and reported as swoole/swoole-src#6297; the affected suite passed on a subsequent run.
Refresh the Sentry snapshot verification date after the reviewed optimization work and current 0.4 merge. Track adoption of the released Swoole shutdown fix without adding a local workaround or an unreliable race test.
@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: hypervel/components/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 99b8dadd-72ee-4b03-868a-9aa0bdf86007

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@binaryfire

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review skipped: 170 files exceed the limit of 100.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@binaryfire

Copy link
Copy Markdown
Member Author

@cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented Oct 13, 2026

Copy link
Copy Markdown

@cubic-dev-ai review

@binaryfire I have started the AI code review. It will take a few minutes to complete.

Comment thread src/sentry/src/Features/CacheIntegration.php Outdated
Comment thread src/telescope/src/Watchers/ClientRequestWatcher.php
@greptile-apps

greptile-apps Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[High impact] Upgrades Sentry SDK and refactors instrumentation across the framework.

Preserve support for published feature lists before merging; the previous compatibility issue remains.

Fix All in Claude CodeFindings

  1. P1 Published features stop working ▶

Summary

This PR updates Sentry collection, tracing, and delivery for long-running workers.

  • Sentry collects request and event data according to configured policies.
  • Guzzle and Telescope record HTTP results when each transfer finishes.
  • Nested jobs and notifications keep their tracing state in their own coroutine.
  • Workers start with their own Sentry and logging resources.
  • Sentry instrumentation follows configured integrations and maps generated frames to source files.
  • Request collection and scope access do less repeated work.
  • The Sentry benchmark harness measures collection, tracing, and delivery costs.

Reviews (2) · Last reviewed commit: "Tighten Sentry test assertions and clari..." · Reviewed by Greptile

Comment on lines 192 to +199
'features' => [
CacheFeature::class,
QueueFeature::class,
NotificationsFeature::class,
LogFeature::class,
LogIntegration::class,
CacheIntegration::class,
QueueIntegration::class,
ConsoleIntegration::class,
ConsoleSchedulingFeature::class,
RedisFeature::class,
StorageIntegration::class,
],

// Exceptions that should not be reported to Sentry
'ignore_exceptions' => [
ValidationException::class,
NotificationsIntegration::class,
ConsoleSchedulingIntegration::class,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Published features stop working

Applications that already ran sentry:publish keep the deleted CacheFeature, QueueFeature, LogFeature, NotificationsFeature, ConsoleSchedulingFeature, and RedisFeature names in their features list. That list replaces these new defaults. registerFeatures() and bootFeatures() catch the missing-class errors and skip the affected features, removing cache, queue, notification, and scheduled-task telemetry. The Sentry log drivers are also no longer registered.

Preserve compatible wrappers for the old names, or translate old feature lists before resolving them. Keep Redis's independent switch when translating RedisFeature.

Fix in Claude Code Fix in Codex

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The 0.4 line is unreleased and intentionally breaking. These feature names are being aligned before its first release, so we are not adding compatibility wrappers or translation for older development snapshots. The shipped feature list contains the current classes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

That’s fair. Since the 0.4 line is unreleased and intentionally breaking, applications from development snapshots are not a supported compatibility target. The feature list is correctly aligned to the current integration class names, so this finding does not apply; no compatibility wrappers or translation are needed.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

19 issues found across 170 files

Confidence score: 3/5

  • FetchesStackTrace.php calls an API missing from some hypervel/di versions allowed by Telescope’s ^0.4 constraint, so trace collection can fail at runtime. Raise the minimum DI version.
  • ClientRequestWatcher.php can turn a successful HTTP response into a rejected promise if recording fails. Catch recording exceptions in both settlement handlers.
  • LogServiceProvider.php closes handlers without updating channel objects that long-lived services already hold, so those services can keep logging through pre-fork handlers. Refresh or replace retained channels after the fork.
  • GuzzleHttpClientAspect.php can add a breadcrumb to the wrong Sentry scope when a promise settles in another coroutine. Capture the originating hub or scope and record the breadcrumb there.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/telescope/src/Watchers/FetchesStackTrace.php">

<violation number="1" location="src/telescope/src/Watchers/FetchesStackTrace.php:27">
P2: This call requires an API that older `hypervel/di` releases allowed by Telescope's `^0.4` constraint do not provide, causing trace collection to fail with an undefined-method error. Raise the minimum DI version to the release adding `proxySources()` or retain compatibility with older versions.</violation>
</file>

<file name="tests/Sentry/ServiceProviderWithoutDsnTest.php">

<violation number="1" location="tests/Sentry/ServiceProviderWithoutDsnTest.php:60">
P3: These tests do not depend on `testIsBound` or its output, so the dependency can skip them when the producer is filtered out and unnecessarily couples their results. Remove the `Depends` attributes from all three tests.</violation>
</file>

<file name="docs/upstream-sync/sync.yaml">

<violation number="1" location="docs/upstream-sync/sync.yaml:278">
P2: This drops the dedicated `getsentry/sentry-docs` checkpoint, so applicable Sentry documentation updates will no longer be tracked. Restore that entry alongside the code-package entry.</violation>
</file>

<file name="src/telescope/src/Watchers/ClientRequestWatcher.php">

<violation number="1" location="src/telescope/src/Watchers/ClientRequestWatcher.php:86">
P2: A `recordTransfer()` failure here turns a successful HTTP response into a rejected promise; `Telescope::recordClientRequest()` can propagate exceptions from `afterRecordingHook`. Catch recording failures in both settlement handlers so Telescope cannot change the request result.</violation>
</file>

<file name="tests/Benchmarks/Sentry/Fixtures/app/app/Bench/StatsCallback.php">

<violation number="1" location="tests/Benchmarks/Sentry/Fixtures/app/app/Bench/StatsCallback.php:39">
P2: The stats endpoint fatals on PHP builds without the OPcache extension because both calls to `opcache_get_status()` are unconditional. Guard them with `function_exists()` or make OPcache an explicit benchmark prerequisite.</violation>
</file>

<file name="tests/Sentry/Http/HypervelRequestFetcherTest.php">

<violation number="1" location="tests/Sentry/Http/HypervelRequestFetcherTest.php:195">
P2: This helper leaves the request in `RequestContext` after fetching it, so later tests without a current request can inherit stale HTTP data. Forget the context in a `finally` block after the fetch.</violation>
</file>

<file name="src/sentry/src/SentryServiceProvider.php">

<violation number="1" location="src/sentry/src/SentryServiceProvider.php:45">
P2: This provider now depends on `hypervel/server-process` for its `BeforeProcessHandle` listener, but `src/sentry/composer.json` does not declare that runtime dependency. Add it to this package's `require` section so standalone installs include the fork-lifecycle integration.

(Based on your team's feedback about declaring split-package runtime dependencies.)</violation>

<violation number="2" location="src/sentry/src/SentryServiceProvider.php:687">
P2: `registerCoroutineContextPropagation` now stores the parent coroutine's `Request` instance directly (not a clone) in the child's Sentry context. The previous code cloned it. Because the request is shared and this key is not a `ReplicableContext` value, the child can later observe parent-side mutations (body consumption, header/session changes) when it captures an event. Clone the request before storing it to keep per-coroutine isolation.</violation>
</file>

<file name="tests/Benchmarks/Sentry/benchmark.php">

<violation number="1" location="tests/Benchmarks/Sentry/benchmark.php:262">
P2: Shutdown requires ext-posix through `posix_kill()`, but the documented prerequisites list PCNTL only; without ext-posix, cleanup fatals and can leave the benchmark processes running. Use `proc_terminate($process, $signal)` for both shutdown calls or document and check the additional extension.</violation>
</file>

<file name="tests/Benchmarks/Sentry/request-fetcher.php">

<violation number="1" location="tests/Benchmarks/Sentry/request-fetcher.php:70">
P2: `opcache_get_status()` is undefined when the optional OPcache extension is absent, aborting the benchmark before it starts. Guard this metadata check with `function_exists()`.</violation>

<violation number="2" location="tests/Benchmarks/Sentry/request-fetcher.php:108">
P2: The processor timing also includes a fresh `Event::createTransaction()` on every iteration, so `processor_us` does not isolate `processEvent()`. Move event construction outside the timed loop or report a matched construction baseline.</violation>
</file>

<file name="src/log/src/LogServiceProvider.php">

<violation number="1" location="src/log/src/LogServiceProvider.php:40">
P2: Closing these handlers does not update channel objects already returned by `channel()`: long-lived services that retained one keep using the pre-fork logger and bypass newly resolved worker handlers or worker-specific configuration. Rebuild or rebind retained channel references on worker startup before closing the old handlers.</violation>
</file>

<file name="tests/Sentry/Features/HttpClientDataCollectionTest.php">

<violation number="1" location="tests/Sentry/Features/HttpClientDataCollectionTest.php:29">
P2: These assertions cover only a literal `token` key, so they will not catch regressions that leak secrets under decoded or bracketed names, or in malformed and relative URLs. Add those query cases and verify unrelated URL bytes remain unchanged.

(Based on your team's feedback about sensitive URL query redaction.)</violation>
</file>

<file name="src/sentry/src/Aspects/GuzzleHttpClientAspect.php">

<violation number="1" location="src/sentry/src/Aspects/GuzzleHttpClientAspect.php:185">
P2: The three successive `setData()` calls in `recordTransfer` clobber each other: sentry-php's `Span::setData()` replaces the whole data array (which is why the old `finishSpan` merged `$span->getData()` before calling it). The response collector and then the origin collector overwrite the earlier `url`/`method`/`status_code` data, so most HTTP span data never gets sent. Merge with `getData()` before each `setData()` — for example `$span->setData(array_merge($span->getData(), $origin))` for the origin block — or build one combined array and set it once.</violation>

<violation number="2" location="src/sentry/src/Aspects/GuzzleHttpClientAspect.php:210">
P2: A promise settled from another coroutine records this breadcrumb on that coroutine's current Sentry scope, not the scope that created the request. Capture the originating hub or scope and add the breadcrumb there.</violation>
</file>

<file name="src/di/src/Bootstrap/GenerateProxies.php">

<violation number="1" location="src/di/src/Bootstrap/GenerateProxies.php:96">
P2: Regenerating a loaded class into the same proxy path with a different source overwrites the mapping, although PHP keeps the old proxy code loaded. Preserve the existing mapping for loaded classes so exception frames retain the correct vendor/main classification.</violation>
</file>

<file name="src/sentry/src/Features/ConsoleSchedulingIntegration.php">

<violation number="1" location="src/sentry/src/Features/ConsoleSchedulingIntegration.php:242">
P3: In-progress check-ins are stored in the coroutine-local map but, unlike the spans/scopes tracked via TracksPushedScopesAndSpans, there is no deferred cleanup: if a task coroutine ends without finishCheckIn() (e.g., a CanceledException propagates from Event::run() or the scheduler stops mid-task), the in-progress CheckIn stays in the context for the coroutine's lifetime and the monitor is never marked finished on Sentry's side. Since Hypervel's ScheduleRunCommand runs each task in a short-lived per-task coroutine the map is normally dropped with coroutine teardown, but the code documents a deliberately missing cache stage without a safeguard for aborted runs. Consider registering a Coroutine::defer that forgets CHECK_INS_CONTEXT_KEY (or sends an error check-in) when finishCheckIn() wasn't reached.</violation>
</file>

<file name="src/sentry/src/EventHandler.php">

<violation number="1" location="src/sentry/src/EventHandler.php:237">
P3: `beforeServerForkHandler` runs `drainEvents()` synchronously in the master process, and `drainEvents()` waits up to `max(1, http_timeout)` seconds (2s by default) for the transport to deliver. With an unreachable DSN or a saturated pool, every server boot stalls for the full timeout before forking. Bound the flush timeout here (or make it best-effort) so boot is not blocked on Sentry delivery.</violation>
</file>

<file name="src/sentry/src/Features/CacheIntegration.php">

<violation number="1" location="src/sentry/src/Features/CacheIntegration.php:326">
P2: `setData()` replaces the span's data map, so finishing a cache span wipes the `cache.key`/`cache.ttl` data that was set when the span started and keeps only `cache.hit` (and `cache.success` in the `KeyWritten`/`KeyWriteFailed` branch). The `count($finishedSpan->getData()['cache.key'] ...)` check just above reads the keys and then discards them. Merge the flag into the existing data instead so finished spans keep their keys.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic


return ! Str::contains($frame['file'], $this->ignoredPaths());
// A generated AOP proxy runs its source file's code, so the source decides whether the frame is ignored.
$file = GenerateProxies::proxySources()[$frame['file']] ?? $frame['file'];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This call requires an API that older hypervel/di releases allowed by Telescope's ^0.4 constraint do not provide, causing trace collection to fail with an undefined-method error. Raise the minimum DI version to the release adding proxySources() or retain compatibility with older versions.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/telescope/src/Watchers/FetchesStackTrace.php, line 27:

<comment>This call requires an API that older `hypervel/di` releases allowed by Telescope's `^0.4` constraint do not provide, causing trace collection to fail with an undefined-method error. Raise the minimum DI version to the release adding `proxySources()` or retain compatibility with older versions.</comment>

<file context>
@@ -22,7 +23,10 @@ protected function getCallerFromStackTrace(array $forgetLines = []): ?array
 
-            return ! Str::contains($frame['file'], $this->ignoredPaths());
+            // A generated AOP proxy runs its source file's code, so the source decides whether the frame is ignored.
+            $file = GenerateProxies::proxySources()[$frame['file']] ?? $frame['file'];
+
+            return ! Str::contains($file, $this->ignoredPaths());
</file context>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

There is no earlier stable 0.4 release to support. The DI API and this consumer will ship together in the first 0.4 release, so the existing ^0.4 constraint is correct.

getsentry/sentry-laravel:
branch: master
checked_through: null
checked_through: 0537a1d5bfd583beb89001887af9cadd3116bccb

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This drops the dedicated getsentry/sentry-docs checkpoint, so applicable Sentry documentation updates will no longer be tracked. Restore that entry alongside the code-package entry.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/upstream-sync/sync.yaml, line 278:

<comment>This drops the dedicated `getsentry/sentry-docs` checkpoint, so applicable Sentry documentation updates will no longer be tracked. Restore that entry alongside the code-package entry.</comment>

<file context>
@@ -275,13 +275,7 @@ hyperf/hyperf:
 getsentry/sentry-laravel:
   branch: master
-  checked_through: null
+  checked_through: 0537a1d5bfd583beb89001887af9cadd3116bccb
   last_reviewed_pr: null
-  sync_date: null
</file context>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Removing that queue entry is intentional: most changes in sentry-docs do not concern this integration. The Sentry page was reconciled directly, and b6550a3 adds a note to check the relevant Laravel documentation when major features or behavior change.

}
return $proceedingJoinPoint->process()->then(
function (ResponseInterface $response) use ($request, $options, $customTags, &$stats): ResponseInterface {
$this->recordTransfer($request, $stats, $response, $options, $customTags);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: A recordTransfer() failure here turns a successful HTTP response into a rejected promise; Telescope::recordClientRequest() can propagate exceptions from afterRecordingHook. Catch recording failures in both settlement handlers so Telescope cannot change the request result.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/telescope/src/Watchers/ClientRequestWatcher.php, line 86:

<comment>A `recordTransfer()` failure here turns a successful HTTP response into a rejected promise; `Telescope::recordClientRequest()` can propagate exceptions from `afterRecordingHook`. Catch recording failures in both settlement handlers so Telescope cannot change the request result.</comment>

<file context>
@@ -67,88 +68,69 @@ public function recordRequest(ProceedingJoinPoint $proceedingJoinPoint): mixed
-            }
+        return $proceedingJoinPoint->process()->then(
+            function (ResponseInterface $response) use ($request, $options, $customTags, &$stats): ResponseInterface {
+                $this->recordTransfer($request, $stats, $response, $options, $customTags);
 
-            return Create::rejectionFor($reason);
</file context>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Keeping the existing Telescope contract: recording hooks and filters propagate application exceptions. The upstream watcher does so too. A blanket catch would suppress those failures; the settlement-path tests explicitly preserve this behavior.

Comment on lines +39 to +40
'enabled' => (opcache_get_status(false) ?: [])['opcache_enabled'] ?? false,
'jit' => (opcache_get_status(false) ?: [])['jit']['on'] ?? false,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The stats endpoint fatals on PHP builds without the OPcache extension because both calls to opcache_get_status() are unconditional. Guard them with function_exists() or make OPcache an explicit benchmark prerequisite.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At tests/Benchmarks/Sentry/Fixtures/app/app/Bench/StatsCallback.php, line 39:

<comment>The stats endpoint fatals on PHP builds without the OPcache extension because both calls to `opcache_get_status()` are unconditional. Guard them with `function_exists()` or make OPcache an explicit benchmark prerequisite.</comment>

<file context>
@@ -0,0 +1,93 @@
+            'coroutines' => Coroutine::stats()['coroutine_num'],
+            'gc' => gc_status(),
+            'opcache' => [
+                'enabled' => (opcache_get_status(false) ?: [])['opcache_enabled'] ?? false,
+                'jit' => (opcache_get_status(false) ?: [])['jit']['on'] ?? false,
+            ],
</file context>
Suggested change
'enabled' => (opcache_get_status(false) ?: [])['opcache_enabled'] ?? false,
'jit' => (opcache_get_status(false) ?: [])['jit']['on'] ?? false,
'enabled' => function_exists('opcache_get_status') ? ((opcache_get_status(false) ?: [])['opcache_enabled'] ?? false) : false,
'jit' => function_exists('opcache_get_status') ? ((opcache_get_status(false) ?: [])['jit']['on'] ?? false) : false,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Documented OPcache as a benchmark prerequisite in b6550a3. The harness requires it for its runtime metadata; it does not need a second execution mode for builds without that extension.

Comment thread src/sentry/src/Tracing/Middleware.php
$this->assertTrue(app()->bound('sentry'));
}

#[Depends('testIsBound')]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: These tests do not depend on testIsBound or its output, so the dependency can skip them when the producer is filtered out and unnecessarily couples their results. Remove the Depends attributes from all three tests.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At tests/Sentry/ServiceProviderWithoutDsnTest.php, line 60:

<comment>These tests do not depend on `testIsBound` or its output, so the dependency can skip them when the producer is filtered out and unnecessarily couples their results. Remove the `Depends` attributes from all three tests.</comment>

<file context>
@@ -50,11 +57,13 @@ public function testIsBound(): void
         $this->assertTrue(app()->bound('sentry'));
     }
 
+    #[Depends('testIsBound')]
     public function testDsnIsNotSet(): void
     {
</file context>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

These attributes preserve the upstream tests prerequisite: the integration must be bound before its configuration is inspected. They do not transfer state between tests. Filtering out a declared prerequisite is expected PHPUnit behavior, not a reason to remove applicable upstream coverage.

Comment thread src/sentry/src/SentryHandler.php Outdated
Comment thread tests/Sentry/ConfigTest.php Outdated
// leave it. The application log context would carry it into queued jobs and subprocesses.
$checkIns = CoroutineContext::get(self::CHECK_INS_CONTEXT_KEY, []);
$checkIns[$cacheKey] = $checkIn;
CoroutineContext::set(self::CHECK_INS_CONTEXT_KEY, $checkIns);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: In-progress check-ins are stored in the coroutine-local map but, unlike the spans/scopes tracked via TracksPushedScopesAndSpans, there is no deferred cleanup: if a task coroutine ends without finishCheckIn() (e.g., a CanceledException propagates from Event::run() or the scheduler stops mid-task), the in-progress CheckIn stays in the context for the coroutine's lifetime and the monitor is never marked finished on Sentry's side. Since Hypervel's ScheduleRunCommand runs each task in a short-lived per-task coroutine the map is normally dropped with coroutine teardown, but the code documents a deliberately missing cache stage without a safeguard for aborted runs. Consider registering a Coroutine::defer that forgets CHECK_INS_CONTEXT_KEY (or sends an error check-in) when finishCheckIn() wasn't reached.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/sentry/src/Features/ConsoleSchedulingIntegration.php, line 242:

<comment>In-progress check-ins are stored in the coroutine-local map but, unlike the spans/scopes tracked via TracksPushedScopesAndSpans, there is no deferred cleanup: if a task coroutine ends without finishCheckIn() (e.g., a CanceledException propagates from Event::run() or the scheduler stops mid-task), the in-progress CheckIn stays in the context for the coroutine's lifetime and the monitor is never marked finished on Sentry's side. Since Hypervel's ScheduleRunCommand runs each task in a short-lived per-task coroutine the map is normally dropped with coroutine teardown, but the code documents a deliberately missing cache stage without a safeguard for aborted runs. Consider registering a Coroutine::defer that forgets CHECK_INS_CONTEXT_KEY (or sends an error check-in) when finishCheckIn() wasn't reached.</comment>

<file context>
@@ -212,11 +235,18 @@ private function startCheckIn(
+        // leave it. The application log context would carry it into queued jobs and subprocesses.
+        $checkIns = CoroutineContext::get(self::CHECK_INS_CONTEXT_KEY, []);
+        $checkIns[$cacheKey] = $checkIn;
+        CoroutineContext::set(self::CHECK_INS_CONTEXT_KEY, $checkIns);
 
         $this->sendCheckIn($checkIn);
</file context>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The check-in map is destroyed with its task coroutine, so an additional defer to forget it would duplicate cleanup. An interrupted task does not guarantee a final check-in; retaining an unfinished monitor is different from leaking worker state. No reliable-delivery or extra shutdown mechanism is being added here.

*/
protected function beforeServerForkHandler(BeforeServerFork $event): void
{
Integration::drainEvents();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: beforeServerForkHandler runs drainEvents() synchronously in the master process, and drainEvents() waits up to max(1, http_timeout) seconds (2s by default) for the transport to deliver. With an unreachable DSN or a saturated pool, every server boot stalls for the full timeout before forking. Bound the flush timeout here (or make it best-effort) so boot is not blocked on Sentry delivery.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/sentry/src/EventHandler.php, line 237:

<comment>`beforeServerForkHandler` runs `drainEvents()` synchronously in the master process, and `drainEvents()` waits up to `max(1, http_timeout)` seconds (2s by default) for the transport to deliver. With an unreachable DSN or a saturated pool, every server boot stalls for the full timeout before forking. Bound the flush timeout here (or make it best-effort) so boot is not blocked on Sentry delivery.</comment>

<file context>
@@ -204,6 +227,16 @@ protected function messageLoggedHandler(LogEvents\MessageLogged $logEntry): void
+     */
+    protected function beforeServerForkHandler(BeforeServerFork $event): void
+    {
+        Integration::drainEvents();
+    }
+
</file context>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The drain is already bounded by the configured HTTP timeout and returns immediately when no sends are pending. It runs once before forking to avoid duplicating buffered telemetry in children. A second timeout setting would not fix a missing bound; that bound already exists.

Count expected terminal events within each coroutine so a bulk read or write does not finish its span after the first key and accidentally pop an outer operation. Match deduplicated read results, per-key write results and whole-read failures.

Document the intentional request-local tracing limitation when a cache listener throws and its caller continues. Cover nested failover reads and writes, repeated keys, failure completion and parent restoration.

Validation: focused regressions fail before the fix; the Sentry suite, formatting and source/type analysis pass.
Check the SDK body policy before constructing the PSR-7 request. Preserve the distinct legacy and data-collection length rules, including unknown lengths and unlimited legacy bodies. Reuse eligible content for header fallback and legacy JSON parsing instead of reading a resource twice.

Cover excluded and eligible resource-backed bodies while retaining event, transaction, form and upload coverage. This avoids excluded reads without introducing a streaming abstraction or changing collection limits.

Validation: the excluded-body probe fails before the fix; focused fetcher tests, the Sentry suite, formatting and source/type analysis pass.
Verify the actual exception captured by console tests and isolate shipped-default assertions from ambient environment overrides. Describe the full batch included in log context and document benchmark extension prerequisites and event allocation in processor measurements.

Direct future integration updates to the relevant Sentry Laravel documentation when major features or behavior change, without tracking the unrelated documentation queue.

Validation: configuration tests reproduce the environment-dependent failure before the fix; affected test files, the full Sentry suite, formatting and source/type analysis pass.
@binaryfire
binaryfire merged commit bc94e7a into 0.4 Oct 13, 2026
90 of 95 checks passed
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.

1 participant