Skip to content

feat(metrics): add OTLP metrics support via getMeter - #13

Merged
Ryan Zhu (underthestars-zhy) merged 2 commits into
mainfrom
ryan/eng-1986-add-otlp-metrics-support-to-photon-aiotel-projects-counters
Jul 17, 2026
Merged

feat(metrics): add OTLP metrics support via getMeter#13
Ryan Zhu (underthestars-zhy) merged 2 commits into
mainfrom
ryan/eng-1986-add-otlp-metrics-support-to-photon-aiotel-projects-counters

Conversation

@underthestars-zhy

@underthestars-zhy Ryan Zhu (underthestars-zhy) commented Jul 17, 2026

Copy link
Copy Markdown
Member

Summary

  • OTLP metrics are now a first-class signal. setupOtel() builds a MeterProvider alongside the tracer and logger providers, wired to a PeriodicExportingMetricReader + OTLPMetricExporter when a metrics endpoint resolves. Metrics share the same resource (service.name / service.version / deployment.environment / custom resourceAttributes), the same no-endpoint local-dev behavior, and the same idempotency and shutdown guarantees as traces and logs.
  • New otel.getMeter(name, version?, options?) on the handle. The recommended way to create instruments: it always resolves through this setup's meter provider, so the same application code works in default global mode and in scoped (register: false) mode. There is intentionally no top-level getMeter() export — requiring the handle keeps initialization order explicit and avoids binding instruments to OpenTelemetry's no-op provider during module init.
  • OtelHandle now exposes the providers. getMeter(), meterProvider, tracerProvider, and loggerProvider all live on the handle; shutdown() now also drains the meter provider (final collection + export) through the existing Promise.allSettled path.
  • Signal-specific endpoints & headers, generalized. Endpoint/header resolution moved to a new src/otlp-config.ts that handles all three signals uniformly. Adds OTEL_EXPORTER_OTLP_METRICS_ENDPOINT and per-signal OTEL_EXPORTER_OTLP_{TRACES,LOGS,METRICS}_HEADERS (signal headers override generic env headers, which override code headers). Header parsing now uses OTel's parseKeyPairsIntoRecord, which percent-decodes keys/values.
  • Metric reader timing is configurable. OTEL_METRIC_EXPORT_INTERVAL / OTEL_METRIC_EXPORT_TIMEOUT (milliseconds; defaults 60000 / 30000). Invalid or non-positive values fall back to the defaults, and the timeout is capped at the interval.
  • Exporter self-exclusion covers metrics. The resolved metrics endpoint joins the set of OTLP URLs auto-excluded from fetch instrumentation, so exporter traffic doesn't recursively trace itself.
  • Docs. README, quickstart, configuration, setup, architecture, environment, and the API reference all cover metrics now, plus a new docs/guides/metrics.mdx documenting the setup-handle path, reusable-module Meter injection, shared-publisher usage, attribute/cardinality guidance, and shutdown.

Usage

import { setupOtel } from "@photon-ai/otel";

const otel = setupOtel({
  serviceName: "orders-service",
  serviceVersion: "1.2.0",
  endpoint: "http://localhost:4318", // optional; env var wins
});

// Create instruments AFTER setup, from the handle's provider —
// identical behavior in global and scoped (register: false) mode.
const ordersProcessed = otel
  .getMeter("orders")
  .createCounter("orders.processed", {
    description: "Orders processed by the service",
  });

ordersProcessed.add(1, { result: "success" });

await otel.shutdown(); // final metric collection + export

Reusable modules should accept a Meter rather than reach for the global provider — the application entry point owns setup and injects the meter:

import type { Meter } from "@opentelemetry/api";

export const createOrderMetrics = (meter: Meter) => ({
  processed: meter.createCounter("orders.processed"),
});

const orderMetrics = createOrderMetrics(otel.getMeter("orders"));

Changes

File Change
src/otlp-config.ts New. resolveOtlpEndpoint / resolveOtlpHeaders / parseEnvHeaders / resolveMetricReaderTiming — signal-generic endpoint + header resolution for traces/logs/metrics, percent-decoding via parseKeyPairsIntoRecord, and metric reader timing with defaults, validation, and timeout-capped-at-interval.
src/setup.ts Build a MeterProvider with a PeriodicExportingMetricReader + OTLPMetricExporter; register it globally unless register: false; add getMeter() / meterProvider to OtelHandle; shut the meter provider down alongside traces/logs. Replace the inline endpoint/header helpers with otlp-config, add per-signal headers, and include the metrics endpoint in the fetch self-exclusion set.
package.json, bun.lock Add @opentelemetry/exporter-metrics-otlp-http + @opentelemetry/sdk-metrics; add the metrics keyword; refresh the package description.
tests/otlp-config.test.ts New (8 tests). Per-signal endpoint derivation & override, header precedence, percent-decoding / malformed-pair handling, and metric-timing defaults / validation / capping.
tests/setup.test.ts +1: getMeter delegates to this setup's provider; the no-endpoint case now asserts a real SdkMeterProvider and a usable counter; reset the global meter registry between tests.
tests/setup.scoped.test.ts Assert register: false leaves the global meter provider untouched (and register: true registers it), and that getMeter() resolves through the handle's own provider.
tests/integration/*, .github/workflows/integration.yml, collector-config.yaml Collector now runs a metrics pipeline (file/metrics); the e2e test emits a counter and asserts name / value / monotonicity / attributes / shared resource off the collector's real output.
README.md, docs/* Document metrics end to end; new docs/guides/metrics.mdx.

Test plan

  • bun run test95 unit tests pass (9 new offline)
  • bun run build — tsdown ESM (28.32 kB) + DTS (12.24 kB) build succeeds
  • bun x ultracite check src tests — clean (22 files)
  • bun run test:integration — real OTLP/HTTP round-trip including a counter assertion; requires Docker, runs in CI

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Medium Risk
Touches core bootstrap (global/scoped providers, shutdown, OTLP config) and adds a new export pipeline; behavior is well-tested but mis-ordered instrument creation or env misconfiguration could still affect production telemetry.

Overview
Adds OTLP metrics as a third signal alongside traces and logs. setupOtel() now builds a MeterProvider with a periodic OTLP exporter when a metrics endpoint resolves, registers it globally by default (or keeps it private in scoped mode), and shutdown() drains metrics with the other providers.

The OtelHandle gains getMeter(name) and meterProvider so apps create instruments from the setup-bound provider without relying on early global metrics.getMeter() calls.

src/otlp-config.ts centralizes endpoint/header resolution for traces, logs, and metrics (including OTEL_EXPORTER_OTLP_METRICS_*, per-signal headers, and OTEL_METRIC_EXPORT_INTERVAL / TIMEOUT). Fetch self-exclusion now includes the metrics exporter URL.

Dependencies, docs (including docs/guides/metrics.mdx), unit tests, and the Docker collector integration test are extended to assert metric export end-to-end.

Reviewed by Cursor Bugbot for commit 983d0d3. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features
    • Added OpenTelemetry metrics support alongside traces and logs.
    • Added otel.getMeter() plus meter-provider support in both global and scoped modes.
    • Added configurable metrics OTLP endpoints, headers, and export timing.
  • Documentation
    • Expanded guides, quickstarts, API, and configuration docs to cover the full traces/logs/metrics setup and shutdown flow.
  • Tests
    • Extended integration tests and collector config to validate exported metrics (including values and resource/attribute checks).
    • Added OTLP metrics configuration helper tests for endpoint/header/timing precedence.

Adds a metrics signal to `setupOtel()` using the OpenTelemetry SDK
metrics
package and OTLP/HTTP exporter. The returned `OtelHandle` now exposes
`getMeter(name, version?)` and `meterProvider` so callers can create
instruments without touching the global API.

Key changes:
- New `src/otlp-config.ts` centralises endpoint, header, and periodic
  reader timing resolution for all three signals, replacing ad-hoc logic
  scattered across setup code.
- `setupOtel()` registers a `M
Copilot AI review requested due to automatic review settings July 17, 2026 01:52

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 02320ca2-22f0-4790-9b89-37f5a7ffde8d

📥 Commits

Reviewing files that changed from the base of the PR and between b29d381 and 983d0d3.

📒 Files selected for processing (2)
  • src/otlp-config.ts
  • tests/otlp-config.test.ts

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.


📝 Walkthrough

Walkthrough

setupOtel() now configures traces, logs, and metrics through OTLP/HTTP, exposes meter access and provider shutdown through OtelHandle, adds signal-specific configuration helpers, expands documentation, and validates metrics through unit and collector integration tests.

Changes

Metrics support

Layer / File(s) Summary
Metrics pipeline and OTLP configuration
src/otlp-config.ts, src/setup.ts, package.json
Adds metrics endpoint/header resolution, export timing, MeterProvider wiring, getMeter(), provider registration, fetch exclusions, and meter shutdown.
Configuration and provider validation
tests/otlp-config.test.ts, tests/setup*.test.ts
Tests endpoint/header precedence, timing validation, meter delegation, no-endpoint behavior, and scoped/global provider registration.
Collector metrics round trip
tests/integration/*, .github/workflows/integration.yml
Adds collector metrics output, parsing and assertions, workflow diagnostics, and integration documentation.
Public API and onboarding documentation
README.md, docs/concepts/*, docs/index.mdx, docs/quickstart.mdx
Documents metrics setup, meter access, OTLP paths, scoped providers, and shutdown usage.
Metrics guides and configuration reference
docs/guides/*, docs/reference/*, docs/configuration.mdx
Documents injected meters, exporter configuration, precedence rules, provider behavior, resource attributes, and final export handling.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested labels: release

Suggested reviewers: copilot

Poem

I’m a rabbit with metrics to spare,
Counters hop through the OTLP air.
Meters bloom when setup is done,
Three signals now travel as one.
At shutdown, the last beans are sent—
Thump, thump, telemetry well spent!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately highlights the main change: adding OTLP metrics support via getMeter.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ryan/eng-1986-add-otlp-metrics-support-to-photon-aiotel-projects-counters

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/otlp-config.ts (1)

32-36: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add JSDoc for the exported configuration helpers.

As per coding guidelines, “Include JSDoc comments for exported functions and classes.”

Also applies to: 38-51, 53-63, 76-91

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/otlp-config.ts` around lines 32 - 36, Add JSDoc comments to each exported
configuration helper in this module, including parseEnvHeaders and the helpers
at the referenced ranges. Document each function’s purpose, parameters, and
return value accurately without changing its implementation.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/index.mdx`:
- Line 46: Update the setupOtel() documentation to state that it installs global
OpenTelemetry providers by default, and explicitly mention that register: false
keeps the tracer, logger, and meter providers scoped. Preserve the existing list
of runtime components.

In `@src/otlp-config.ts`:
- Around line 47-50: Update the generic endpoint selection in the OTLP
configuration flow to use a falsy fallback so an empty
OTEL_EXPORTER_OTLP_ENDPOINT uses base; add a regression test covering the
empty-string environment value and expected endpoint behavior.

---

Nitpick comments:
In `@src/otlp-config.ts`:
- Around line 32-36: Add JSDoc comments to each exported configuration helper in
this module, including parseEnvHeaders and the helpers at the referenced ranges.
Document each function’s purpose, parameters, and return value accurately
without changing its implementation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b29534c0-2ccf-4c7b-866d-deeac32157d2

📥 Commits

Reviewing files that changed from the base of the PR and between 4f76fa8 and b29d381.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • .github/workflows/integration.yml
  • README.md
  • docs/concepts/architecture.mdx
  • docs/configuration.mdx
  • docs/guides/metrics.mdx
  • docs/guides/setup.mdx
  • docs/index.mdx
  • docs/quickstart.mdx
  • docs/reference/api.mdx
  • docs/reference/environment.mdx
  • package.json
  • src/otlp-config.ts
  • src/setup.ts
  • tests/integration/README.md
  • tests/integration/collector-config.yaml
  • tests/integration/otel-collector.test.ts
  • tests/otlp-config.test.ts
  • tests/setup.scoped.test.ts
  • tests/setup.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Cursor Bugbot
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Prefer interface for defining object shapes in TypeScript rather than type aliases

**/*.{ts,tsx}: Use explicit types for function parameters and return values when they enhance clarity
Prefer unknown over any when the type is genuinely unknown
Use as const const assertions for immutable values and literal types
Leverage TypeScript type narrowing instead of type assertions
Use meaningful variable names instead of magic numbers; extract descriptive constants
Use arrow functions for callbacks and short functions
Prefer for...of loops over .forEach() and indexed for loops
Use optional chaining (?.) and nullish coalescing (??) for safer property access
Prefer template literals over string concatenation
Use destructuring for object and array assignments
Use const by default, let only when reassignment is needed, and never use var
Always await promises in async functions and use the return value
Use async/await syntax instead of promise chains for better readability
Handle errors appropriately in async code with try-catch blocks
Don't use async functions as Promise executors

Files:

  • tests/setup.scoped.test.ts
  • tests/otlp-config.test.ts
  • src/otlp-config.ts
  • tests/setup.test.ts
  • tests/integration/otel-collector.test.ts
  • src/setup.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{js,jsx,ts,tsx}: Use camelCase for variable and function names in JavaScript/TypeScript
Use PascalCase for class and component names in JavaScript/TypeScript
Always use async/await for promise handling instead of .then() chains
Include JSDoc comments for exported functions and classes
Use meaningful variable names that clearly describe their purpose
Avoid deeply nested conditionals; use early returns or guard clauses instead
Use const by default, let when reassignment is needed, avoid var

**/*.{js,jsx,ts,tsx}: Remove console.log, debugger, and alert statements from production code
Throw Error objects with descriptive messages, not strings or other values
Use try-catch blocks meaningfully; don't catch errors just to rethrow them
Prefer early returns over nested conditionals for error cases
Keep functions focused and under reasonable cognitive complexity limits
Extract complex conditions into well-named boolean variables
Use early returns to reduce nesting
Prefer simple conditionals over nested ternary operators
Group related code together and separate concerns
Add rel="noopener" when using target="_blank" on links
Avoid dangerouslySetInnerHTML unless absolutely necessary
Don't use eval() or assign directly to document.cookie
Validate and sanitize user input
Avoid spread syntax in accumulators within loops
Use top-level regex literals instead of creating them in loops
Prefer specific imports over namespace imports
Avoid barrel files (index files that re-export everything)
Use proper image components (for example, Next.js <Image>) over <img> tags
Use next/head or the App Router metadata API for head elements
Use Server Components for async data fetching instead of async Client Components

Files:

  • tests/setup.scoped.test.ts
  • tests/otlp-config.test.ts
  • src/otlp-config.ts
  • tests/setup.test.ts
  • tests/integration/otel-collector.test.ts
  • src/setup.ts
**/*.test.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Write unit tests for all public functions and components

Files:

  • tests/setup.scoped.test.ts
  • tests/otlp-config.test.ts
  • tests/setup.test.ts
  • tests/integration/otel-collector.test.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{test,spec}.{js,jsx,ts,tsx}: Write assertions inside it() or test() blocks
Avoid done callbacks in async tests; use async/await instead
Don't use .only or .skip in committed code
Keep test suites reasonably flat; avoid excessive describe nesting

Files:

  • tests/setup.scoped.test.ts
  • tests/otlp-config.test.ts
  • tests/setup.test.ts
  • tests/integration/otel-collector.test.ts
🪛 ast-grep (0.44.1)
tests/integration/otel-collector.test.ts

[warning] 38-38: Do not use Math.random() to generate security-sensitive values such as tokens, secrets, passwords, API keys, salts, nonces, OTPs, or session IDs. Math.random() is not cryptographically secure and is predictable. Use crypto.randomBytes()/crypto.randomUUID() (Node) or crypto.getRandomValues() (Web Crypto) instead.
Context: Math.random()
Note: [CWE-330] Use of Insufficiently Random Values.

(insecure-random-security-token-typescript)

🪛 LanguageTool
docs/index.mdx

[style] ~99-~99: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...s) when wrapping business operations. - Use Metrics when creatin...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

docs/guides/setup.mdx

[style] ~47-~47: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...dProcessor` with an OTLP log exporter. When a metrics endpoint is resolved, setup c...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~182-~182: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: .... 3. Shuts down the logger provider. 4. Shuts down the meter provider, performing a f...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

README.md

[grammar] ~273-~273: Use a hyphen to join words.
Context: ...pans nest across the boundary). - Auto fetch instrumentation defaults off (wr...

(QB_NEW_EN_HYPHEN)

🔇 Additional comments (13)
tests/integration/collector-config.yaml (1)

23-24: LGTM!

Also applies to: 38-40

tests/integration/otel-collector.test.ts (1)

35-44: LGTM!

Also applies to: 95-113, 133-139, 243-273, 304-304, 320-325, 383-385, 440-450

tests/integration/README.md (1)

5-8: LGTM!

Also applies to: 31-31, 47-51

.github/workflows/integration.yml (1)

76-77: LGTM!

README.md (1)

7-8: LGTM!

Also applies to: 29-96, 156-163, 250-250, 261-278

docs/concepts/architecture.mdx (1)

8-14: LGTM!

Also applies to: 27-27, 41-58, 76-76, 91-91

docs/index.mdx (1)

10-11: LGTM!

Also applies to: 23-23, 36-40, 42-45, 47-50, 99-99

docs/quickstart.mdx (1)

3-6: LGTM!

Also applies to: 31-31, 43-43, 53-78, 100-100, 121-121, 136-188

docs/guides/metrics.mdx (1)

1-203: LGTM!

docs/guides/setup.mdx (1)

3-3: LGTM!

Also applies to: 19-57, 78-102, 177-184

docs/reference/api.mdx (1)

27-27: LGTM!

Also applies to: 44-44, 53-53, 62-87

docs/configuration.mdx (1)

69-83: LGTM!

Also applies to: 106-115, 240-250

docs/reference/environment.mdx (1)

17-21: LGTM!

Also applies to: 37-37, 65-74, 88-108, 179-184

Comment thread docs/index.mdx
2. `withSpan()` uses the global tracer provider to create application spans.
3. `createLogger()` uses the global logger provider and active context so logs inside spans carry trace correlation.
4. Fetch auto-instrumentation (native undici on Node when its optional packages are installed, otherwise the `globalThis.fetch` wrap that Bun always uses) makes outbound `fetch` carry W3C trace context to downstream services.
1. `setupOtel()` installs the global OpenTelemetry runtime pieces: context manager, propagator, tracer provider, logger provider, and meter provider.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Qualify global provider registration.

Line 46 implies setupOtel() always installs global providers, but register: false deliberately keeps tracer/logger/meter providers scoped. Say “by default” and mention scoped mode.

Proposed fix
-1. `setupOtel()` installs the global OpenTelemetry runtime pieces: context manager, propagator, tracer provider, logger provider, and meter provider.
+1. By default, `setupOtel()` installs the global OpenTelemetry runtime pieces: context manager, propagator, tracer provider, logger provider, and meter provider. With `register: false`, the signal providers remain scoped to the returned handle.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
1. `setupOtel()` installs the global OpenTelemetry runtime pieces: context manager, propagator, tracer provider, logger provider, and meter provider.
1. By default, `setupOtel()` installs the global OpenTelemetry runtime pieces: context manager, propagator, tracer provider, logger provider, and meter provider. With `register: false`, the signal providers remain scoped to the returned handle.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/index.mdx` at line 46, Update the setupOtel() documentation to state
that it installs global OpenTelemetry providers by default, and explicitly
mention that register: false keeps the tracer, logger, and meter providers
scoped. Preserve the existing list of runtime components.

Comment thread src/otlp-config.ts Outdated
`??` treats an empty string as a valid value, causing an empty
endpoint to override the code-supplied base URL. Switch to `||`
so an empty `OTEL_EXPORTER_OTLP_ENDPOINT` correctly falls back.
Copilot AI review requested due to automatic review settings July 17, 2026 02:01

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@underthestars-zhy
Ryan Zhu (underthestars-zhy) merged commit d49b3cd into main Jul 17, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release Fight on!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants