meta(changelog): Update changelog for 11.0.0 - #24620
Conversation
…24111) Ports the plain browser and bundler E2E apps to the default span-streaming lifecycle, while retaining `default-browser-static` as representative coverage for the legacy static lifecycle. Streamed trace assertions collect spans across envelopes where needed and check low-cardinality segment names. Fixes #23806 --------- Co-authored-by: OpenAI Codex <codex@openai.com>
Removes the `traceLifecycle: 'static'` pin and rewrites the specs against streamed spans. Fastify names both its request span and its middie route-handler span after the route, so the spec tells them apart by attribute rather than by name. Ref: #23801 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…4124) Guard `loadModule`'s default `existingModule` parameter with a `typeof module` check so calling it from the ESM build degrades to `undefined` instead of throwing `ReferenceError: module is not defined`, since default parameters are evaluated before the function's try/catch. Fixes #24117 Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…streaming (#24106) Removes the `traceLifecycle: 'static'` pin and rewrites the specs against streamed spans. Ref: #23801 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ng (#24102) Removes the `traceLifecycle: 'static'` pin and rewrites the specs against streamed spans. Ref: #23801 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Mastra agent traces now come from the SDK: `invoke_agent`, `chat`, and `execute_tool` spans, on by default for Node-family SDKs. Ref JS-3455 (dup JSSDK-21). **How it attaches** - Hooks the `Mastra` constructor and registers our exporter via `registerExporter()`. - If the app already has observability, we attach to it. - If it doesn’t, we bootstrap a pipeline from `@mastra/observability` when that package is already installed. We never add it ourselves. - You can also register the exporter by hand. **What we emit** - Only ops, names, and attributes from [sentry-conventions](https://getsentry.github.io/sentry-conventions/ops/#gen_ai). - Span types with no conventional op (`workflow_step`, `memory_operation`, `processor_run`, `scorer_*`) are dropped; children re-parent onto the nearest exported ancestor. - `model_inference` is dropped too. Mastra nests `model_generation > model_step > model_inference`, and the inference span repeats the generation’s model/usage — exporting both produced a duplicate nested `chat` per step. `model_generation` is the `gen_ai.chat` span. Note to reviewer: 🗯️ This diff is large mostly because of Node integration tests and the @mastra/* yarn.lock bump, not because the SDK surface is huge. The product decisions are in three files: - packages/server-utils/src/ai/mastra/utils.ts — span names and gen_ai.* attributes - packages/server-utils/src/ai/mastra/index.ts — exporter (drop unmapped types, re-parent children) - packages/server-utils/src/integrations/mastra.ts — constructor attach, bootstrap, skip duplicate provider wrapping If you are checking that auto-instrumentation actually hooks Mastra, also glance at packages/server-utils/src/orchestrion/config/mastra.ts --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
) ## What Removes the `traceLifecycle: 'static'` pin from the client and server configs, and rewrites the transaction specs as streamed span specs. Assertions on children of a segment now accumulate the trace with `collectStreamedSpans`. Two names change under span streaming, and the specs follow the value into the attributes instead: the outgoing `http.client` span is named after the domain (`GET localhost`), and a mysql span is named after its query summary (`SELECT`). ## Why Span streaming is the default, so the default E2E suite should exercise it. Static coverage lives in the new `astro-7-static` app (#24075). Ref: #23809
#24091) `handleRequest` hard-defaulted `trackClientIp` to `false`, so the global `dataCollection.userInfo` could never switch client IP collection on. An integration option is only meant to win when the user actually sets it. It is now `options.trackClientIp ?? dataCollection.userInfo`. Heads up, this changes the default. Astro apps that leave both alone start reporting `user.ip_address`, matching `userInfo`'s documented default of `true` and what the other server SDKs do. Fixes #24086 --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…act Router hooks (#24014) Adds a new `@sentry/react/react-router` entry point so React Router users no longer have to thread `useLocation`/`useNavigationType`/`matchRoutes`/`createRoutesFromChildren` into `reactRouterBrowserTracingIntegration` — the entry pulls them from `react-router` and supplies them as defaults. `react-router` is declared as an **optional** peer dependency (v6/v7/v8); the import lives only in the separate entry, so the main `@sentry/react` barrel stays free of `react-router` for plain-React and CJS/SSR consumers. Under the hood, the React Router instrumentation was reworked to stop sharing state through ambient module-scope variables. All integration configuration (the router hooks, `stripBasename`, `enableAsyncRouteHandlers`, `lazyRouteTimeout`, `lazyRouteManifest`, `basename`, and the navigation-instrumentation flag) now lives in a single per-client `ReactRouterConfig` held in a `WeakMap<Client, …>` and threaded explicitly through the call chain. A direct consequence, **for every `@sentry/react` routing setup — not just the new entry** — is that the order in which you add the browser tracing integration and wrap your routes no longer matters. Previously `Sentry.init()` had to run before your routes were wrapped (`withSentryReactRouterV6Routing`, `wrapReactRouterRouting`, `wrapUseRoutes`, `wrapCreateBrowserRouter`, …), and wrapping earlier silently produced uninstrumented routes. Wrapping now reads its config when the router renders (or is created), so wrapping at module-evaluation time before `Sentry.init()` still instruments correctly. ### Root cause The pre-existing design shared the integration's config with the routing wrappers through module-scope globals, which were per-module rather than per-client (so multiple Sentry clients clobbered each other) and coupled wrap-order to init-order (wrapping routes before `Sentry.init()` produced uninstrumented components). ### Decisions - **A separate entry point, not a main-barrel auto-import.** `@sentry/react` ships CJS and is consumed server-side (Next.js, SSR, Jest). A static `import from 'react-router'` in the main barrel would crash every consumer that doesn't have `react-router` installed; a dynamic import would violate the repo's static-analyzability policy. A dedicated entry (the same pattern `@sentry/solid/solidrouter` already uses) keeps the main barrel import-free. - **Per-client config instead of module globals.** Moving config onto the client (following the existing `WeakMap<Client>` pattern in the same file) makes it per-client-correct, makes `basename` per-router via a shallow copy instead of a last-writer-wins global, and — because the wrappers read config at render/creation time via a Rules-of-Hooks-safe outer/inner split — makes wrapping order-independent w.r.t. `Sentry.init()` for all routing APIs. - `initializeRouterUtils`/`_stripBasename` and the `CLIENTS_WITH_INSTRUMENT_NAVIGATION` WeakSet were folded into the threaded config; the integration is composed with `extendIntegration`. - `useEffect` is no longer used internally (React's own effect hook is used); it stays an optional, ignored option for backwards compatibility. The existing `@sentry/react` unit suite passes unchanged; new e2e apps for React Router v6, v7 and v8 (`react-router-{6,7,8}-router-entry`) exercise the zero-config setup end-to-end and type-check the `@sentry/react/react-router` API against each major via `tsc --noEmit`; a v11 `MIGRATION.md` entry documents the new entry point and the order-independence. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Sigrid <32902192+s1gr1d@users.noreply.github.com> Co-authored-by: Charly Gomez <charly.gomez1310@gmail.com>
Removes the deprecated, **unused** Express patch APIs from `@sentry/core/server`: - the `patchExpressModule` helper and the whole `integrations/express/*` module tree - the `ExpressIntegrationOptions`, `ExpressMiddleware`, and `ExpressErrorMiddleware` types These were kept behind a "deprecated, kept until the next major" comment, but nothing actually uses them: there is no runtime caller of `patchExpressModule` anywhere in the repo, and `@sentry/server-utils` ships its own Express integration (`expressIntegration`) with its own local copies of the middleware types. The only references left were core's own unit tests, which are removed alongside the source. Marked with `!` because these were exported symbols on the public `@sentry/core/server` entrypoint, even though they were deprecated and unused. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…24030) Replaces the throw with a debug.warn plus pass-through to the user's handler. Cloudflare's wrapper never blocks the request when the client is missing, this brings Deno in line. One correction to the issue's mechanism from testing it: Sentry.close() doesn't actually leave getClient() undefined, it flushes and disables but the client stays bound, and a failed second init leaves the first client bound too, so neither path hit this throw. What does reproduce it is a directly constructed client, new DenoClient plus client.init() installs the Deno.serve patch without ever calling setCurrentClient, so the patch is live with no client bound and every request 500s. Either way the wrapper shouldn't be the thing that kills the request. Trade-off stated plainly: this turns a loud failure into a silent one, the warn only shows with debug enabled, and Cloudflare's equivalent doesn't warn at all there. Two tests, own file since they wipe the carrier: a plain request comes back 200, and a throwing handler still yields a 500 rather than a crash. Both assert Deno.serve was actually patched first so they can't pass vacuously. Closes #23894
…entry (#23282) > The majority of additional code lines are tests Agents are different, they need special care on top of the wrangler config. Because they are marked as DurableObjects, but have a different wrapper, they need extra hacks within the Vite plugin. ### Issue Before, specific type of import/exports didn't work properly and caused manual instrumentation again: https://github.com/sergical/cuzz/blob/4ec6080865e37491ff240007bbe886f907ad1de7/src/worker/index.ts#L11-L15 ### Solution(s) #### First one We had to add a `agent` return inside `resolveWrapperKind`, to correctly identify Agents in there as well, so we know when to add `instrumentAgentWithSentry` instead of `instrumentDurableObjectWithSentry` #### Second one When a class gets exported right away, we need to change that export: ```js export { MyAgent, MyDo } from './do' ``` needs to be changed to the following in order to wrap it: ```js import { MyAgent as __SENTRY_REEXPORT_MyAgent__, MyDo as __SENTRY_REEXPORT_MyDo__ } from './do' const __SENTRY_WRAPPED_MyDurableObject__ = __SENTRY__.instrumentDurableObjectWithSentry((env) => ({}), __SENTRY_REEXPORT_MyDurableObject__); export { __SENTRY_WRAPPED_MyAgent__ as MyAgent, __SENTRY_WRAPPED_MyDo__ as MyDo } from './do' ``` This should fix what was needed before: getsentry/sentry-docs#18944 --- Clanker description The Vite auto-instrument transform only wrapped classes declared in the entry module. An entry that just aggregates its Durable Objects, Agents and Workflows from other files — `import { MyAgent } from './agent'; export { MyAgent }`, or `export { MyAgent } from './agent'` — got no instrumentation at all, leaving manual `instrument*WithSentry` wrapping as the only option. An import binding cannot be reassigned, so instead of renaming a declaration the transform now re-points those specifiers at a fresh wrapper binding and rebuilds the export statement, carrying unrelated specifiers over verbatim. Agent detection was blind to the same shapes: `collectAgentCandidates` only considered local classes, so a re-exported Agent was never even offered to the cross-module base-class walk that already knew how to resolve it. Star re-exports (`export * from './do'`) still cannot be wrapped — they name no binding — so the warning stays, reworded to say that rather than blaming re-exports in general. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Andrei <168741329+andreiborza@users.noreply.github.com>
Register wasm modules when apps load via `fetch → arrayBuffer → WebAssembly.instantiate/compile`, not only streaming APIs. - Patch `Response.prototype.arrayBuffer` and `bytes` so wasm buffers remember their fetch URL - Wrap `WebAssembly.instantiate` and `compile` to register modules after byte-based loads - Workers get non-streaming registration too, through the shared `patchWebAssembly(register)` - Add tests for tagged-buffer registration, untagged-buffer negative case, argument forwarding, and a browser test for `fetch → arrayBuffer → instantiate` Registration only. Frames from buffer-compiled modules still carry `wasm://wasm/<hash>`, which nothing matches against the registered image, so `debug_meta` does not attach yet. That half is #23781. ## Implementation - `patchWasmResponse.ts` tags wasm buffers in a `WeakMap` when read from a wasm-like `Response`; `getWasmSourceUrl()` resolves them at instantiate/compile time - Response patching goes through `fill()`, so a frozen `Response.prototype` cannot break `Sentry.init()` - `patchWebAssembly()` orchestrates response patching, non-streaming hooks, then streaming hooks (unchanged behavior) - Non-streaming hooks forward every argument and never let a registration failure reject the caller's promise - Skip registration when `instantiate` receives an already-compiled `WebAssembly.Module` --------- Co-authored-by: Andrei Borza <andrei.borza@sentry.io>
Removes the `traceLifecycle: 'static'` pin and rewrites the specs against streamed spans. Ref: #23801 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…d astro-5 specs (#24113) ## What Puts `url.template`, `url.path` and `url.full` back on the client pageload assertions in `astro-4/tests/tracing.static`, `astro-5/tests/tracing.static` and `astro-5/tests/tracing.serverIslands`, as streamed span attributes. ## Why The span-streaming ports (#24079, #24078) dropped them by accident. Lukas spotted the same drop in astro-6 on #24077, where it is fixed in place; these two apps had already merged. Ref: #23809
## What Removes the `traceLifecycle: 'static'` pin from the client and server configs, and rewrites the transaction specs as streamed span specs. Assertions on children of a segment now accumulate the trace with `collectStreamedSpans`. Two names change under span streaming, and the specs follow the value into the attributes instead: the outgoing `http.client` span is named after the domain (`GET localhost`), and the browser `resource.link` span for a server island is too. ## Why Span streaming is the default, so the default E2E suite should exercise it. Static coverage moves to the new `astro-7-static` app (#24075). Ref: #23809
This is technically breaking, I think, but I'm not even sure 🤔 any code that used to work before this should still work I believe. I guess it is only breaking if you add a handler manually that is typed in a way that `attributes` are optional, but now they aren't. It is already today always present, so really just a type change to ensure you don't need to guard this all the time in the handler.
…URLs (#24120) - [x] If you've added code that should be tested, please add tests. - [x] Ensure your code lints and the test suite passes (`yarn lint`) & (`yarn test`). - [ ] Link an issue if there is one related to your pull request. If no issue is linked, one will be auto-generated and linked. --- `instrumentEmberAppInstanceForPerformance` calls `routerService.recognize(url)` at three sites without guarding it. `recognize()` throws for URLs the router cannot resolve. Most notably it asserts: ``` Assertion Failed: You must pass a url that begins with the application's rootURL "/" ``` whenever the URL is not prefixed with the app's `rootURL` — which is the case under Ember's `none` location, the one `@ember/test-helpers` installs. Since the first call happens inside `browserTracingIntegration`'s `afterAllSetup`, the assertion propagates out of integration setup and takes down every acceptance test that boots the app. Reproducing it needs nothing exotic — an app that calls `instrumentAppInstancePerformance(appInstance)` and has any acceptance test using `visit()` will hit it. Host apps currently have to work around it by skipping instrumentation under test entirely, which also costs them tracing coverage in tests. ### The fix All three call sites are *already written* for a missing `routeInfo`: ```ts name: routeInfo ? `route:${routeInfo.name}` : /* …URL fallback… */ // pageload span _getRouteUrlAttributes(client, url, routeInfo?.params) // routeWillChange routeInfo?.params ?? transition.to?.params // routeDidChange ``` The only thing missing is that `recognize()` *throws* instead of returning `undefined`. This routes the three sites through a small `_recognizeURL()` helper that catches and degrades to `undefined`, matching the intent the surrounding code already expresses. No intended behavior changes — a URL the router can resolve behaves exactly as before. ### Notes - Follows the existing `_getLocationURL` convention in this module (`// Only exported for testing`). - Return type is derived as `ReturnType<RouterService['recognize']> | undefined`, so no new imports. - Tests are appended to `tests/instrument-router-location.test.ts`, which already covers this module: recognized URL passes through, the `rootURL` assertion yields `undefined`, and any other throw yields `undefined`. - Happy to add a `DEBUG_BUILD`-gated debug log in the `catch` if you'd prefer the failure be visible rather than silent — left it out to keep the change minimal. Affects the v2 addon introduced in #23252; still present on `develop` and in `11.0.0-beta.1`.
…el refs) (#24183) Both notes named the exact condition for their own removal, and both conditions passed when the engine floor moved to Node `>=20.19.0` in 4f03438. `createHrTimer()`'s TODO was conditioned on dropping Node 8; `process.hrtime.bigint()` has been available since Node 10.7. `getTimeMs()` still returns an integer `number` of milliseconds — BigInt division truncates toward zero and the elapsed delta is never negative, so it yields the same value the previous `Math.floor(seconds * 1e3 + nanoSeconds / 1e6)` did. `_channelSubs` existed only to hold a reference around nodejs/node#42170, which mattered while Node 18.18.0 was supported. It is replaced by the `_isInstrumented` boolean its own comment proposed. Nothing else in the repo read the array — the only uses were the `.length` guard and the `.push()`. The flag is now set before subscribing rather than after, so a re-entrant call cannot double-subscribe; the old `.length` check only flipped after the first push, making this equal-or-stricter. Fixes #24182 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JGK2ap3HHAy2WuDwoS8yTW Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Mastra integration failed to auto-bootstrap `@mastra/observability`
under ESM when the app configured no observability of its own.
`loadMastraObservability()` anchored module resolution on
`process.cwd()` and the CJS `require.cache`, but an ESM-loaded
`@mastra/core` never lands in that cache, leaving cwd as the only
anchor. When the app's dependencies don't live under cwd — a
serverless/monorepo layout, or concretely the per-suite temp install the
integration tests use — resolution failed silently: no exporter was
attached and no `gen_ai` spans were produced, so the ESM `scenario-auto`
test timed out.
The runtime injection hook already receives the resolved `file:` URL of
every module it instruments and was throwing it away. We now record it
on `__SENTRY_ORCHESTRION__.runtimeFiles` and let the Mastra integration
anchor `@mastra/observability` resolution on the app's actual
`@mastra/core` file. This works under both ESM and CJS and no longer
depends on cwd; the CJS-cache and cwd lookups remain as fallbacks.
Decisions:
- Anchor on `@mastra/core`, not `@mastra/observability`. The latter is
never loaded in the failing case — that is precisely why the SDK has to
bootstrap it — so there is nothing to hook there. `@mastra/core` is
always loaded when the integration runs.
- Fix it in SDK code rather than by changing the scenario child's cwd in
the test runner. The cwd approach would have papered over a real
production gap (ESM apps whose cwd is not the app directory, e.g.
serverless) that the code's own comments already acknowledged.
This also moves `@mastra/core` and `@mastra/observability` out of the
`node-integration-tests` workspace `devDependencies` into the runner's
per-suite `additionalDependencies`. `@mastra/core` declares
`engines.node >= 22.13`, so a workspace `devDependency` broke `yarn
install` on the Node 20.19 CI matrix even though the suite is gated
behind `conditionalTest({ min: 22 })`. Installing them into the test's
temp dir at run time means Node 20 never resolves them, while Node 22+
runs get them exactly as before.
_Root cause_: cwd/`require.cache`-based resolution has no valid anchor
for an optional peer dependency of an ESM-loaded module when the app's
`node_modules` is not under `process.cwd()`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Exercise Fastify 3, 4, and 5 with span streaming, preserving request, error, and propagation coverage across all three versions. part of #23800 Co-authored-by: GPT-6 <codex@openai.com>
Exercise Effect 3 and 4 with span streaming, preserving manual and nested span coverage. part of #23800 Co-authored-by: GPT-6 <codex@openai.com>
…24164) Exercise Firebase with span streaming and remove the static-lifecycle pin from the Electron profiling fixture. Preload Sentry's instrumentation hook in the Firebase emulator, which loads firebase-functions before the handler module initializes Sentry. This re-enables the HTTP, error, and Firestore-trigger tests with assertions for streamed function segments and error correlation. part of #23800 Co-authored-by: GPT-6 <codex@openai.com>
…24104) Removes the `traceLifecycle: 'static'` pin and rewrites the specs against streamed spans. The specs that documented microservice spans arriving as standalone transactions now document them arriving as segment spans of their own traces; the underlying missing trace propagation is unchanged. Ref: #23801 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Preserve transaction-based Express coverage in a dedicated static-lifecycle app before migrating the original app to span streaming. part of #23800 Co-authored-by: GPT-6 <codex@openai.com>
Exercise Elysia on Node with span streaming, including its low-cardinality handler names and error correlation. part of #23800 Co-authored-by: GPT-6 <codex@openai.com>
Removes the `traceLifecycle: 'static'` pin and rewrites the specs against streamed spans. The queue span is named `process test-queue` under span streaming, the other way around from the transaction name. Streamed spans carry no breadcrumbs, so `/check-isolation` reports which of the processor's breadcrumbs leaked into its isolation scope as a span attribute. Ref: #23801 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Adds a SvelteKit 2 E2E app mirroring nextjs-otlp, where the app owns the OpenTelemetry setup and the specs assert that errors, logs and metrics link to the active OpenTelemetry trace while spans and metrics keep exporting over OTLP.
Removes the `traceLifecycle: 'static'` pin and rewrites the specs against streamed spans. Two things did not port mechanically. `waitForStreamedSpan` gains the optional `timestamp` parameter `waitForEnvelopeItem` already has, because the `app_creation` span is emitted at startup, before any spec can start listening. And streamed spans carry no breadcrumbs, so `/test-schedule-isolation` reports whether a scheduled task's breadcrumb leaked into its isolation scope as a span attribute, the same way the Next.js middleware specs do. Ref: #23801 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Exercise Hono across its supported runtimes with span streaming, preserving middleware, route naming, internal request, and error correlation coverage. part of #23800 Co-authored-by: GPT-6 <codex@openai.com>
|
🚧 Skipped: PR exceeds review size limit. Please split into smaller PRs and re-run. |
|
bugbot moch |
| - fix(cloudflare): Capture telemetry from untraced Durable Object RPC calls ([#24447](https://github.com/getsentry/sentry-javascript/pull/24447)) | ||
| - fix(cloudflare): Enforce flush timeout across Workflow lifecycle ([#24483](https://github.com/getsentry/sentry-javascript/pull/24483)) | ||
| - fix(cloudflare): Instrument namespaces returned by jurisdiction() ([#24450](https://github.com/getsentry/sentry-javascript/pull/24450)) | ||
| - fix(cloudflare,deno,node): Align types of vercelai ([#22343](https://github.com/getsentry/sentry-javascript/pull/22343)) |
There was a problem hiding this comment.
I wonder if we should add changes which were already backported, like this
There was a problem hiding this comment.
probably not these should be covered by the v10 changelogs
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit b74a617. Configure here.
|
|
||
| ### How To Upgrade | ||
|
|
||
| Please carefully read through the [migration guide](https://docs.sentry.io/platforms/javascript/migration/v10-to-v11/) in the Sentry docs on how to upgrade from version 10 to version 11. Make sure to select your specific platform/framework in the top left corner. |
481a0b8 to
c4a6112
Compare
|
🚧 Skipped: PR exceeds review size limit. Please split into smaller PRs and re-run. |
c4a6112 to
878ae98
Compare
|
🚧 Skipped: PR exceeds review size limit. Please split into smaller PRs and re-run. |
878ae98 to
9536994
Compare
|
🚧 Skipped: PR exceeds review size limit. Please split into smaller PRs and re-run. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
9536994 to
ed38fe7
Compare
|
🚧 Skipped: PR exceeds review size limit. Please split into smaller PRs and re-run. |


Closes: #22056