Skip to content

feat(worker): add locality to hosted activity spans - #986

Merged
chrisdoc merged 2 commits into
mainfrom
feat/worker-geo-usage-metrics
Aug 11, 2026
Merged

feat(worker): add locality to hosted activity spans#986
chrisdoc merged 2 commits into
mainfrom
feat/worker-geo-usage-metrics

Conversation

@chrisdoc

@chrisdoc chrisdoc commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Primary changes

  • propagate bounded Cloudflare city, region, and country metadata to hosted Worker activity spans
  • retain the existing HMAC user pseudonym and colo correlation
  • add taxonomy attributes to Worker activity spans for trace-derived tool metrics
  • document the approximate geography fields and retention/access semantics

Reviewer walkthrough

  • packages/worker/src/worker-telemetry.ts extracts and bounds Cloudflare locality, region, and country metadata.
  • packages/worker/src/worker.ts forwards sanitized geography alongside the existing user hash and colo values.
  • packages/worker/src/worker-observer.ts applies defense-in-depth validation and emits taxonomy and geography attributes; focused tests cover extraction, propagation, and omission.
  • Documentation and the changeset describe approximate geography, retention and access semantics, dashboard aggregation, and the Worker release impact.

Correctness and invariants

  • Geography remains optional and bounded; invalid, oversized, identifier-shaped, or unavailable values are omitted.
  • The existing HMAC user pseudonym and Cloudflare colo correlation remain intact.
  • Raw IP addresses, exact location data, latitude/longitude, and postal codes are not emitted.

Testing and QA

  • mise exec -- npm run check
  • mise exec -- npm run check:types
  • mise exec -- npm run worker:dry-run
  • mise exec -- npm run test:pr
  • mise exec -- npm run test:performance
  • mise exec -- npm run check:changeset

This PR is the Worker-side prerequisite for the separate cardinality-conscious Collector span-metrics pipelines.

Summary by Sourcery

Propagate bounded Cloudflare IP-geolocation metadata into hosted Worker MCP activity spans and document the new privacy-reviewed telemetry fields and retention semantics.

New Features:

  • Expose approximate Cloudflare geography (city, region, country code) on Worker requests via a safe helper for use in telemetry.
  • Attach tool taxonomy attributes and approximate geography fields to Worker activity spans for trace-derived metrics aggregation.

Enhancements:

  • Validate and normalize Cloudflare geography metadata in the Worker observer to ensure bounded, safe span attributes.
  • Extend Worker observer options and wiring so locality information flows from incoming requests into span attributes.

Documentation:

  • Update telemetry dashboards documentation to describe new geography span attributes and recommended aggregation strategies that avoid series explosion.
  • Expand the telemetry data dictionary and privacy policy to cover approximate geography fields, clarify that they are not exact location data, and document retention and access constraints.

Tests:

  • Add unit tests for Cloudflare geography extraction, safe normalization of geography fields, and propagation into Worker observer span attributes and options.

✨ PR Description

Purpose: Add bounded Cloudflare IP-geolocation attributes (city, region, country) to hosted MCP activity spans for privacy-reviewed geographic usage aggregation.

Main changes:

  • Added safeGeoValue() and safeCountryCode() validation functions with regex patterns to sanitize and bound geographic fields in worker-observer.ts and worker-telemetry.ts
  • Extended WorkerToolObserverOptions interface with geoLocalityName, geoLocalityRegion, geoCountryCode fields and span attribute emission logic
  • Implemented getCloudflareGeography() function extracting bounded Cloudflare IP-geolocation from request metadata, integrated into MCP request handler

Generated by LinearB AI and added by gitStream.
AI-generated content may contain inaccuracies. Please verify before using.
💡 Tip: You can customize your AI Description using Guidelines

Summary by CodeRabbit

  • New Features

    • Hosted activity telemetry now includes sanitized, approximate city, region, and country information when available.
    • Geographic values are validated and limited to protect privacy; raw IP addresses and precise location data are not collected.
  • Documentation

    • Updated privacy, telemetry dashboard, and data dictionary documentation to describe geographic metadata, access controls, and 30-day retention.
    • Added guidance for grouping activity by approximate location.

@assert-app

assert-app Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review on Assert →

5 clusters identified

Merge candidate is preparing...

@sourcery-ai sourcery-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.

Sorry @chrisdoc, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@sourcery-ai

sourcery-ai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds validated Cloudflare IP-geolocation locality/region/country metadata to hosted Worker MCP activity spans, propagates it through the Worker observer, enriches span attributes with tool taxonomy and geography, and documents the new telemetry and privacy semantics.

Sequence diagram for propagating Cloudflare geography into Worker activity spans

sequenceDiagram
  actor Client
  participant Worker as serveMcpRequest
  participant Telemetry as worker_telemetry
  participant ObserverFactory as createWorkerToolObserver
  participant Span as WorkerObservationSpan

  Client->>Worker: HTTP request
  Worker->>Telemetry: getCloudflareGeography(request)
  Telemetry-->>Worker: CloudflareGeography
  Worker->>Telemetry: createWorkerUserHash(apiKey)
  Telemetry-->>Worker: userHash
  Worker->>Telemetry: getCloudflareColo(request)
  Telemetry-->>Worker: cloudflareColo
  Worker->>ObserverFactory: createWorkerToolObserver({ userHash, cloudflareColo, geoLocalityName, geoLocalityRegion, geoCountryCode })
  ObserverFactory-->>Worker: ToolObservationScope
  Worker->>Span: span.setAttribute("user.hash", userHash)
  Worker->>Span: span.setAttribute("cloudflare.colo", cloudflareColo)
  Worker->>Span: span.setAttribute("geo.locality.name", geoLocalityName)
  Worker->>Span: span.setAttribute("geo.locality.region", geoLocalityRegion)
  Worker->>Span: span.setAttribute("geo.country.code", geoCountryCode)
  Worker->>Span: span.setAttribute("hevy.feature", taxonomy.feature)
  Worker->>Span: span.setAttribute("mcp.tool.kind", taxonomy.kind)
  Worker->>Span: span.setAttribute("mcp.tool.operation", taxonomy.operation)
  Worker->>Span: span.end()
Loading

File-Level Changes

Change Details Files
Introduce a safe Cloudflare geography extractor for Worker requests and use it when creating the observer.
  • Extend the Cloudflare request metadata type to include city, region, and country fields.
  • Add CloudflareGeography interface and helper to normalize and bound geography values using length and regex constraints.
  • Implement getCloudflareGeography with defensive try/catch and no impact on MCP behavior when metadata is missing or invalid.
  • Wire getCloudflareGeography into serveMcpRequest and pass the derived locality and country fields in WorkerToolObserverOptions.
packages/worker/src/worker-telemetry.ts
packages/worker/src/worker.ts
Propagate and validate geography and taxonomy fields into span attributes in the Worker tool observer.
  • Add MAX_GEO_VALUE_LENGTH and new safe regex patterns for country codes and geography values.
  • Extend WorkerToolObserverOptions with geoLocalityName, geoLocalityRegion, and geoCountryCode options.
  • Implement safeGeoValue and safeCountryCode validators and apply them when constructing the observer scope.
  • Set new span attributes for hevy.feature, mcp.tool.kind, mcp.tool.operation, and geography fields when present and valid.
packages/worker/src/worker-observer.ts
Add tests covering Cloudflare geography extraction, observer propagation, and attribute scrubbing behavior.
  • Extend worker-telemetry tests to assert bounded approximate geography extraction and exclusion of client identifiers, including malformed field handling.
  • Extend worker-observer tests to verify taxonomy and geography attributes are applied to spans only when valid, and dropped when malformed.
  • Update worker tests to assert that the handler passes user hash, colo, and geography through to the observer options.
packages/worker/src/worker-telemetry.test.ts
packages/worker/src/worker-observer.test.ts
packages/worker/src/worker.test.ts
Document the new geography attributes, their usage in dashboards, and updated privacy semantics.
  • Update telemetry dashboards documentation to describe geo.locality.name, geo.locality.region, geo.country.code, and new TraceQL metric profiles that avoid series explosion.
  • Extend the telemetry data dictionary with the new geography fields and describe their approximate, IP-derived nature.
  • Refine privacy policy to include approximate geography in hosted activity spans and clarify retention for spans containing user hash, colo, or geography.
  • Strengthen privacy guidance on not collecting exact location data, lat/long, or postal codes.
  • Add a changeset entry for the worker package describing the new bounded locality/region/country attributes for usage aggregation.
docs/telemetry-dashboards.md
docs/telemetry-data-dictionary.md
docs/privacy-policy.md
.changeset/quiet-cities-usage.md

Possibly linked issues

  • #telemetry-Cloudflare-colo-user-spans: PR implements user.hash + cloudflare.colo on MCP activity spans with tests/docs, plus extra approximate geo attributes.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Cloudflare Worker preview

@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Bundle Report

Bundle size has no change ✅

@deepsource-io

deepsource-io Bot commented Aug 11, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 0b03a0d...e410c0e on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
JavaScript Aug 11, 2026 7:19p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

Comment thread packages/worker/src/worker-telemetry.ts

@gitar-bot gitar-bot 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.

Gitar has auto-approved this PR (configure)

@gitar-bot gitar-bot Bot added the gitar-approved Added by Gitar label Aug 11, 2026
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

feat(worker): add bounded Cloudflare locality to hosted activity spans

✨ Enhancement 📝 Documentation 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Propagate bounded Cloudflare city/region/country metadata into hosted Worker activity spans.
• Add tool taxonomy attributes to spans to support trace-derived usage metrics.
• Document privacy/retention semantics and validate sanitization via focused Worker tests.
Diagram

graph TD
  req{{"Cloudflare Request"}} --> handler["worker.ts handler"] --> ctx["worker-telemetry.ts"] --> obs["worker-observer.ts"] --> backend[("Trace backend")]
  docs["Telemetry docs"] -. "retention + semantics" .-> backend

  subgraph Legend
    direction LR
    _ext{{"External"}} ~~~ _mod["Module"] ~~~ _db[("Storage")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Adopt OpenTelemetry geo semantic conventions
  • ➕ Improves interoperability with off-the-shelf OTel tooling and processors
  • ➕ Reduces custom attribute taxonomy to maintain long-term
  • ➖ May require renaming existing attributes and updating existing dashboards/queries
  • ➖ Could still need compatibility aliases during migration
2. Derive geography only in the collector (omit from Worker spans)
  • ➕ Keeps edge spans minimal and avoids any geo data at the source
  • ➕ Centralizes sanitization/allow-listing in one place
  • ➖ Collector may not reliably have the same Cloudflare geolocation fields
  • ➖ Harder to correlate with per-request spans if geo is not attached early

Recommendation: The current approach is sound for the stated goal (Worker-side prerequisite for trace-derived metrics) because it bounds and sanitizes values at the source and keeps geo optional. The main strategic consideration is naming: if long-term interoperability matters, consider mapping/aliasing these fields to OTel geo semantic conventions (either directly or via a collector translation stage) to avoid a future rename cost.

Files changed (10) +232 / -24

Enhancement (3) +99 / -1
worker-observer.tsAttach bounded geo and taxonomy attributes to activity spans +43/-0

Attach bounded geo and taxonomy attributes to activity spans

• Adds optional observer options for locality/region/country and sanitizes them with strict patterns/length limits. Emits the geo attributes and tool taxonomy onto hosted activity spans when present, alongside existing 'user.hash' and 'cloudflare.colo'.

packages/worker/src/worker-observer.ts

worker-telemetry.tsImplement bounded Cloudflare geography extraction helper +47/-0

Implement bounded Cloudflare geography extraction helper

• Adds 'getCloudflareGeography()' to read 'request.cf' geolocation fields and return a sanitized '{ localityName, localityRegion, countryCode }' object. Uses trimming/whitespace normalization plus strict patterns and fail-closed error handling to keep metadata optional and safe.

packages/worker/src/worker-telemetry.ts

worker.tsPropagate Cloudflare geography into observer creation +9/-1

Propagate Cloudflare geography into observer creation

• Wires 'getCloudflareGeography(request)' into the MCP request handler and forwards the sanitized locality/region/country fields when creating the Worker tool observer. Preserves existing user hash and colo behavior.

packages/worker/src/worker.ts

Tests (3) +90 / -3
worker-observer.test.tsTest emission and sanitization of taxonomy + geo span attributes +33/-0

Test emission and sanitization of taxonomy + geo span attributes

• Extends observer tests to assert taxonomy attributes ('hevy.feature', 'mcp.tool.kind', 'mcp.tool.operation') and new geo attributes are set on spans. Adds negative assertions to ensure malformed city/country values are dropped.

packages/worker/src/worker-observer.test.ts

worker-telemetry.test.tsAdd tests for bounded Cloudflare geography extraction +39/-1

Add tests for bounded Cloudflare geography extraction

• Introduces unit tests for 'getCloudflareGeography()' to ensure it returns only bounded city/region/country fields and never leaks client identifiers. Verifies malformed fields are dropped while preserving valid ones.

packages/worker/src/worker-telemetry.test.ts

worker.test.tsVerify handler passes geo fields into observer options +18/-2

Verify handler passes geo fields into observer options

• Updates the integration-style Worker test to include Cloudflare city/region/country on the request and asserts they are forwarded to the observer. Keeps existing assertions for deterministic user hash and colo propagation.

packages/worker/src/worker.test.ts

Documentation (3) +38 / -20
privacy-policy.mdDocument approximate geography in hosted activity spans +8/-6

Document approximate geography in hosted activity spans

• Updates the privacy policy to include bounded Cloudflare IP-geolocation fields (city/region/country) alongside existing pseudonymous hash and colo. Clarifies 30-day retention and maintainer/on-call access semantics for spans containing these attributes.

docs/privacy-policy.md

telemetry-dashboards.mdUpdate TraceQL examples to group by locality fields +19/-9

Update TraceQL examples to group by locality fields

• Expands dashboard documentation to describe new 'span.geo.*' attributes and shifts the example aggregation from user/colo to user/locality. Adds guidance for separate metric profiles to avoid 'user_hash × tool × locality' cardinality explosion.

docs/telemetry-dashboards.md

telemetry-data-dictionary.mdAdd geo attribute definitions and safety constraints +11/-5

Add geo attribute definitions and safety constraints

• Adds 'geo.locality.name', 'geo.locality.region', and 'geo.country.code' to the data dictionary with bounded/approximate semantics. Tightens the 'never send' list to explicitly exclude latitude/longitude and postal codes.

docs/telemetry-data-dictionary.md

Other (1) +5 / -0
quiet-cities-usage.mdAdd changeset for bounded locality span attributes +5/-0

Add changeset for bounded locality span attributes

• Introduces a patch changeset describing the addition of bounded Cloudflare locality/region/country attributes to hosted activity spans for privacy-reviewed aggregation.

.changeset/quiet-cities-usage.md

@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.10345% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.94%. Comparing base (0b03a0d) to head (e410c0e).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
packages/worker/src/worker-observer.ts 88.88% 0 Missing and 1 partial ⚠️
packages/worker/src/worker-telemetry.ts 94.73% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #986      +/-   ##
==========================================
+ Coverage   73.75%   73.94%   +0.18%     
==========================================
  Files          92       92              
  Lines        6107     6136      +29     
  Branches     1733     1744      +11     
==========================================
+ Hits         4504     4537      +33     
+ Misses       1023     1018       -5     
- Partials      580      581       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Unit Test Results

  1 files   78 suites   24s ⏱️
792 tests 792 ✅ 0 💤 0 ❌
799 runs  799 ✅ 0 💤 0 ❌

Results for commit e410c0e.

♻️ This comment has been updated with latest results.

@gitstream-cm gitstream-cm Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✨ PR Review

The PR is well-structured: validation helpers are guarded by tight allow-list regexes, the changeset and privacy-policy updates are present, and test coverage exercises both valid and malformed inputs. Two concrete issues are worth addressing before merge.

2 issues detected:

🐞 Bug - Each geo field is evaluated by `safeGeoValue` twice — once in the conditional and once as the spread value — so a mutable accessor could produce different results on the second call. 🛠️

Details: safeGeoValue is invoked twice for each of cf?.city and cf?.region: once for the truthiness guard and once to produce the value. If cf exposes a getter that could change between accesses (e.g. a Proxy-wrapped object), the second call could return undefined even though the first returned a truthy string, silently dropping the field. Beyond correctness, the double invocation needlessly repeats the trim, whitespace-collapse, and Unicode regex for every request.

File: packages/worker/src/worker-telemetry.ts (43-51)

🛠️ A suggested code correction is included in the review comments.

🧹 Maintainability - The same security-sensitive normalisation/validation function is duplicated across two files with no shared source of truth, so future changes to the regex or length cap must be applied in two places.

Details: safeGeoValue (and its associated GEO_VALUE_PATTERN / MAX_GEO_VALUE_LENGTH constants) is defined independently in both worker-telemetry.ts and worker-observer.ts. The two copies are currently byte-for-byte identical, but they will silently diverge if the allow-list regex or the length cap is ever tightened. Because this is security-relevant input sanitisation, a silent divergence could lead to one path accepting values that the other rejects.

File: packages/worker/src/worker-telemetry.ts (23-30)

Generated by LinearB AI and added by gitStream.
AI-generated content may contain inaccuracies. Please verify before using.
💡 Tip: You can customize your AI Review using Guidelines Learn how

Comment on lines +43 to +51
return {
...(safeGeoValue(cf?.city)
? { localityName: safeGeoValue(cf?.city) }
: {}),
...(safeGeoValue(cf?.region)
? { localityRegion: safeGeoValue(cf?.region) }
: {}),
...(countryCode ? { countryCode } : {}),
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🐞 Bug - Double safeGeoValue Call: Store the result of each safeGeoValue call in a local variable and reuse it:

const localityName = safeGeoValue(cf?.city);
const localityRegion = safeGeoValue(cf?.region);
return {
  ...(localityName ? { localityName } : {}),
  ...(localityRegion ? { localityRegion } : {}),
  ...(countryCode ? { countryCode } : {}),
};
Suggested change
return {
...(safeGeoValue(cf?.city)
? { localityName: safeGeoValue(cf?.city) }
: {}),
...(safeGeoValue(cf?.region)
? { localityRegion: safeGeoValue(cf?.region) }
: {}),
...(countryCode ? { countryCode } : {}),
};
const localityName = safeGeoValue(cf?.city);
const localityRegion = safeGeoValue(cf?.region);
return {
...(localityName ? { localityName } : {}),
...(localityRegion ? { localityRegion } : {}),
...(countryCode ? { countryCode } : {}),
};
Is this review accurate? Use 👍 or 👎 to rate it

If you want to tell us more, use /gs feedback e.g. /gs feedback this review doesn't make sense, I disagree, and it keeps repeating over and over

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 11, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Duplicate geo sanitization ✓ Resolved 🐞 Bug ➹ Performance
Description
getCloudflareGeography() calls safeGeoValue(cf?.city) and safeGeoValue(cf?.region) twice each
(condition + assignment), doubling whitespace normalization/regex work and re-reading request.cf
properties. This adds avoidable overhead and makes behavior depend on repeated property reads.
Code

packages/worker/src/worker-telemetry.ts[R44-49]

+			...(safeGeoValue(cf?.city)
+				? { localityName: safeGeoValue(cf?.city) }
+				: {}),
+			...(safeGeoValue(cf?.region)
+				? { localityRegion: safeGeoValue(cf?.region) }
+				: {}),
Evidence
The implementation spreads objects using safeGeoValue(cf?.city) / safeGeoValue(cf?.region) in
both the conditional and the value position, resulting in duplicated sanitization and repeated
property reads.

packages/worker/src/worker-telemetry.ts[36-51]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`getCloudflareGeography()` sanitizes `cf.city` and `cf.region` twice (once to check truthiness and again to assign), which duplicates work and repeats property access.
## Issue Context
This helper is called on the request path in `serveMcpRequest()`, so even small inefficiencies are multiplied across traffic.
## Fix Focus Areas
- Cache sanitized values once and reuse them:
- packages/worker/src/worker-telemetry.ts[36-51]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Geo normalization before cap ✓ Resolved 🐞 Bug ➹ Performance
Description
safeGeoValue() trims and collapses whitespace before checking the 64-character bound, so
unexpectedly large strings still incur a full scan and intermediate allocation even though they will
be rejected. This is defensive-performance debt introduced with the new geo sanitization helpers.
Code

packages/worker/src/worker-observer.ts[R231-234]

+	if (typeof value !== "string") return undefined;
+	const normalized = value.trim().replace(/\s+/gu, " ");
+	return normalized.length <= MAX_GEO_VALUE_LENGTH &&
+		SAFE_GEO_VALUE_PATTERN.test(normalized)
Evidence
Both new safeGeoValue() implementations normalize via trim().replace(...) before applying the
normalized.length <= 64 bound, meaning oversized inputs still pay the normalization cost prior to
rejection.

packages/worker/src/worker-observer.ts[230-237]
packages/worker/src/worker-telemetry.ts[23-30]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`safeGeoValue()` does `trim()` and `replace(/\s+/g, ...)` before verifying that the value is within a reasonable size, which can do avoidable work if a caller ever provides a very large string.
## Issue Context
Although Cloudflare-provided geography values are expected to be small, these helpers are part of a library surface and are also used in request handling. An early rejection guard improves robustness and keeps the sanitization strictly bounded in work.
## Fix Focus Areas
- Add a conservative pre-check on the *original* string length (or slice before normalization), then run normalization + regex:
- packages/worker/src/worker-observer.ts[230-237]
- packages/worker/src/worker-telemetry.ts[23-30]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can group findings by type and pick your Finding display, from Minimal to Full

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread packages/worker/src/worker-telemetry.ts Outdated
Comment thread packages/worker/src/worker-observer.ts Outdated
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The worker now extracts bounded Cloudflare locality, region, and country metadata, validates it, and adds it to hosted activity spans. Tests cover propagation and rejection rules. Privacy, dashboard, data dictionary, and release documentation define the telemetry policy.

Changes

Cloudflare geography telemetry

Layer / File(s) Summary
Geography extraction and validation
packages/worker/src/worker-telemetry.ts, packages/worker/src/worker-telemetry.test.ts
Adds bounded, normalized Cloudflare locality, region, and country extraction. Tests cover valid, malformed, oversized, and identifier-shaped values.
Observer propagation and span attributes
packages/worker/src/worker.ts, packages/worker/src/worker-observer.ts, packages/worker/src/*.test.ts
Passes geography metadata into the observer and emits only validated fields on activity spans.
Telemetry policy and release documentation
docs/privacy-policy.md, docs/telemetry-dashboards.md, docs/telemetry-data-dictionary.md, .changeset/quiet-cities-usage.md
Documents geography dimensions, retention, prohibited data, dashboard queries, and the worker patch release.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant serveMcpRequest
  participant getCloudflareGeography
  participant WorkerToolObserver
  participant HostedActivitySpan
  Client->>serveMcpRequest: MCP request
  serveMcpRequest->>getCloudflareGeography: Read Cloudflare geography metadata
  getCloudflareGeography-->>serveMcpRequest: Sanitized locality, region, and country
  serveMcpRequest->>WorkerToolObserver: Provide observer geography options
  WorkerToolObserver->>HostedActivitySpan: Emit validated geography attributes
Loading

Possibly related PRs

  • chrisdoc/hevy-mcp#982: Extends the same worker telemetry and observer functionality with Cloudflare geography metadata.

Suggested reviewers: gitar-bot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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 clearly and concisely describes the main change: adding locality metadata to hosted Worker activity spans.
✨ 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 feat/worker-geo-usage-metrics

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/telemetry-dashboards.md`:
- Around line 47-52: In docs/telemetry-dashboards.md lines 47-52, either remove
user_hash from persisted metric labels or define a 30-day retention and
restricted-access rule for the trace-derived user activity profile. In
docs/privacy-policy.md lines 48-51, disclose the same retention and access
policy for any metric that persists user_hash with geography; keep both
documents consistent.

In `@packages/worker/src/worker-telemetry.ts`:
- Around line 5-29: Reject identifier-shaped numeric geography values such as
IPv4 addresses, coordinates, and numeric postal codes before safeGeoValue
returns them; apply the same validation in
packages/worker/src/worker-observer.ts lines 20-24 and keep its sanitizer at
lines 230-243 aligned with the extraction sanitizer. Add corresponding rejection
tests in packages/worker/src/worker-telemetry.test.ts lines 54-66 and
span-attribute omission tests in packages/worker/src/worker-observer.test.ts
lines 132-156.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 008f3202-b5a5-4441-b0f7-97c0c81161d3

📥 Commits

Reviewing files that changed from the base of the PR and between 0b03a0d and 641f575.

📒 Files selected for processing (10)
  • .changeset/quiet-cities-usage.md
  • docs/privacy-policy.md
  • docs/telemetry-dashboards.md
  • docs/telemetry-data-dictionary.md
  • packages/worker/src/worker-observer.test.ts
  • packages/worker/src/worker-observer.ts
  • packages/worker/src/worker-telemetry.test.ts
  • packages/worker/src/worker-telemetry.ts
  • packages/worker/src/worker.test.ts
  • packages/worker/src/worker.ts

Comment thread docs/telemetry-dashboards.md
Comment thread packages/worker/src/worker-telemetry.ts
@chrisdoc

Copy link
Copy Markdown
Owner Author

Addressed the review concerns in commit e410c0e9ad909bd9861d2b4f631487a0c12fb231:

  • cached sanitized geography values and shared the sanitizer between extraction and observer defense-in-depth paths;
  • reject identifier-shaped IPv4/coordinate/postal-code values and oversized inputs;
  • added regression tests for sanitization, getter single-read behavior, and span omission;
  • documented 30-day retention and restricted access for user_hash + locality usage metrics.

Validation passed: npm run check, npm run check:types, npm run worker:dry-run, npm run test:pr, npm run test:performance, and npm run check:changeset.

@gitar-bot

gitar-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 1 resolved / 1 findings

Adds bounded Cloudflare IP-geolocation metadata to hosted Worker activity spans with privacy-safe normalization, addressing the redundant safeGeoValue calls. No issues found.

Auto-approved: No blocking issues found.
Please see Auto-approve Docs for details on setting custom approval criteria.

✅ 1 resolved
Quality: Redundant safeGeoValue calls in getCloudflareGeography

📄 packages/worker/src/worker-telemetry.ts:43-51
In getCloudflareGeography, safeGeoValue(cf?.city) and safeGeoValue(cf?.region) are each invoked twice — once in the ternary condition and again to produce the value. This re-runs the trim/regex normalization needlessly. Compute each into a local const once and spread based on truthiness, e.g. const localityName = safeGeoValue(cf?.city); then ...(localityName ? { localityName } : {}).

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

@chrisdoc
chrisdoc merged commit e338d8a into main Aug 11, 2026
24 checks passed
@chrisdoc
chrisdoc deleted the feat/worker-geo-usage-metrics branch August 11, 2026 19:22
@github-actions github-actions Bot mentioned this pull request Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant