Skip to content

🥅 server: bind account address to sentry instrumentation scopes#832

Merged
cruzdanilo merged 1 commit intomainfrom
id
Feb 24, 2026
Merged

🥅 server: bind account address to sentry instrumentation scopes#832
cruzdanilo merged 1 commit intomainfrom
id

Conversation

@cruzdanilo
Copy link
Member

@cruzdanilo cruzdanilo commented Feb 23, 2026


Open with Devin

Summary by CodeRabbit

  • Improvements

    • Monitoring now attaches account user context to per-account operations, ensuring errors and statuses are reported with the correct account identity and consolidated per-operation status.
  • Bug Fixes

    • Improved error reporting paths so captured exceptions include account context and overall activity status reflects aggregated failures.
  • Tests

    • Added and updated tests validating account user context is set across success, no-balance, and error scenarios.
  • Chores

    • Added a patch release note documenting the instrumentation scope change.

Greptile Summary

improved sentry error tracking by consistently binding account addresses to instrumentation scopes across all per-account server operations. previously, account context was inconsistently attached via tags; now user identity is set uniformly via setUser, enabling account-scoped diagnostics in sentry.

key changes

  • server/utils/keeper.ts: automatically extracts and validates account from span attributes, sets user context for all keeper operations
  • server/hooks/activity.ts: sets user context at request level (single account) and per-account operation level, adds user binding in error capture scopes, removes redundant exa.account tag
  • server/hooks/block.ts: wraps async operations with user context binding for proposals and withdrawals, ensures account context in error handlers
  • server/hooks/panda.ts: reorders validation to set user context before throwing errors
  • comprehensive test coverage verifying user context is set correctly across success, error, and edge-case paths

Confidence Score: 5/5

  • safe to merge - this is a monitoring instrumentation improvement with no functional changes to business logic
  • changes are limited to adding sentry user context bindings and removing redundant tags. all modifications preserve existing error handling and business logic. comprehensive test coverage verifies user context is set correctly across all code paths (success, error, no-balance scenarios). the refactoring in activity.ts simplifies status logic without changing behavior
  • no files require special attention

Important Files Changed

Filename Overview
server/hooks/activity.ts binds account user context to sentry scopes for activity operations, removes redundant exa.account tag, refactors status logic
server/hooks/block.ts binds account user context to sentry scopes for proposal and withdraw operations, removes redundant exa.account tag
server/hooks/panda.ts reorders checks to bind account user context before throwing errors
server/utils/keeper.ts extracts and validates account from span attributes, automatically binds user context for all keeper operations

Last reviewed commit: 6d5d477

@changeset-bot
Copy link

changeset-bot bot commented Feb 23, 2026

🦋 Changeset detected

Latest commit: 6d5d477

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@exactly/server Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Feb 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Binds account addresses into Sentry scopes across server hooks and utilities by adding withScope/setUser calls, replacing per-operation tags, adjusting error capture to include user context, and updating tests and a changeset for a patch release of @exactly/server.

Changes

Cohort / File(s) Summary
Changeset
.changeset/weak-dove-play.md
Adds a patch changeset entry for @exactly/server noting Sentry account binding.
Activity Hook
server/hooks/activity.ts
Replaces per-account tags with withScope/setUser usage, adjusts per-account Promise/error handling and captureException calls to attach user context; minor iteration/import tweaks.
Block Hook
server/hooks/block.ts
Wraps mutexed flows, scheduleMessage/scheduleWithdraw continuations, and error captures with withScope that sets Sentry user context before spans/processors run.
Panda Hook
server/hooks/panda.ts
Moves inactive-user check to after setUser({ id: account }) so Sentry user context is established before early rejections.
Keeper Utility
server/utils/keeper.ts
Adds safeParse(Address, ...) to extract account from span attributes and conditionally calls setUser when valid; updates imports to include Address and safeParse.
Activity Tests
server/test/hooks/activity.test.ts
Adds setUser assertions across success, no-balance, and error scenarios; imports setUser for verification.
Keeper Tests
server/test/utils/keeper.test.ts
Adds withScope interception helper (spyScopeSetUser), new "user identity" tests, and assertions verifying setUser behavior; imports adjusted.
Block Tests
server/test/hooks/block.test.ts
Adds withScope interception and assertions verifying setUser in proposal/withdraw flows; includes test helper and spy usage.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Hook as Server Hook
participant Mutex as Mutex/Coordinator
participant Sentry as Sentry SDK
participant Processor as Processing Logic
Hook->>Mutex: acquire lock / schedule operation
Mutex->>Sentry: withScope -> setUser({ id: account })
Sentry-->>Hook: scope established
Hook->>Processor: start span and perform work (proposal/withdraw/activity)
Processor-->>Hook: result / error
Hook->>Sentry: captureException or set span status (with user context)
Mutex->>Hook: release lock

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • nfmelendez
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main objective of the PR: binding account addresses to Sentry instrumentation scopes across the server codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch id

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 and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @cruzdanilo, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the server's Sentry integration by ensuring that the relevant account address is consistently associated with Sentry spans and error reports. This provides crucial user context for debugging and monitoring, allowing for quicker identification and resolution of account-specific issues. The changes involve updating Sentry calls in activity, block, and panda hooks, as well as the keeper utility, alongside corresponding test updates.

Highlights

  • Sentry User Context: Implemented explicit binding of account addresses to Sentry instrumentation scopes using setUser({ id: account }) to provide better user context for error tracking and performance monitoring across various server operations.
  • Refactored Error Handling: Adjusted the Promise.allSettled pattern in server/hooks/activity.ts to Promise.all with individual try...catch blocks, ensuring that all account-related operations are attempted and their errors are properly captured and attributed to the specific account in Sentry.
  • Sentry Tag Optimization: Removed redundant scope.setTag("exa.account", account) calls where scope.setUser({ id: account }) already provides the necessary user identification for Sentry.
  • Improved Test Coverage: Added new test assertions to verify that Sentry's setUser function is correctly called with the appropriate account ID in activity, block, and keeper related tests.
Changelog
  • .changeset/weak-dove-play.md
    • Added a new changeset file for the patch.
  • server/hooks/activity.ts
    • Imported setUser from @sentry/node.
    • Added a setUser call for single account processing if only one account is present.
    • Refactored Promise.allSettled to Promise.all with try...catch for individual account processing to ensure all operations are attempted.
    • Added scope.setUser({ id: account }) within the retry logic for poke account operations.
    • Added scope.setUser({ id: account }) within the NoBalance error handling scope.
    • Removed scope.setTag("exa.account", account) as setUser now handles user identification.
  • server/hooks/block.ts
    • Removed scope.setTag("exa.account", account) from processProposal and processWithdraw.
    • Wrapped startSpan calls in scheduleMessage and scheduleWithdraw with withScope and scope.setUser({ id: account }) to ensure user context is set for mutex acquisition and withdraw processing.
  • server/hooks/panda.ts
    • Reordered the if (!user.isActive) check to occur after setUser({ id: account }) to ensure the user context is set before a potential error is thrown.
  • server/test/hooks/activity.test.ts
    • Imported setUser from @sentry/node.
    • Added expect(setUser).toHaveBeenCalledWith({ id: account }) assertions to multiple test cases to verify Sentry user context setting.
  • server/test/hooks/block.test.ts
    • Imported Scope from @sentry/core.
    • Added beforeEach(() => vi.spyOn(Scope.prototype, "setUser")) to mock setUser calls.
    • Added expect(Scope.prototype.setUser).toHaveBeenCalledWith({ id: bobAccount }) assertions to various proposal and withdraw test cases.
  • server/test/utils/keeper.test.ts
    • Imported Scope from @sentry/core.
    • Added beforeEach(() => vi.spyOn(Scope.prototype, "setUser")) to mock setUser calls.
    • Added new test cases under a "user identity" describe block to specifically test setUser calls based on the presence and validity of the account attribute.
    • Added expect(Scope.prototype.setUser).not.toHaveBeenCalled() and expect(Scope.prototype.setUser).toHaveBeenCalledWith({ id: account }) assertions.
  • server/utils/keeper.ts
    • Imported safeParse and Address from valibot.
    • Added logic within exaSend to safely parse the account attribute from spanOptions and set the Sentry user if it's a valid address.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

gemini-code-assist[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

@sentry
Copy link

sentry bot commented Feb 23, 2026

Codecov Report

❌ Patch coverage is 95.65217% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.51%. Comparing base (c95ac58) to head (6d5d477).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
server/hooks/block.ts 92.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #832      +/-   ##
==========================================
+ Coverage   69.09%   69.51%   +0.41%     
==========================================
  Files         211      211              
  Lines        7503     7662     +159     
  Branches     2406     2471      +65     
==========================================
+ Hits         5184     5326     +142     
- Misses       2098     2113      +15     
- Partials      221      223       +2     
Flag Coverage Δ
e2e 69.26% <82.60%> (+0.41%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/test/utils/keeper.test.ts (1)

9-9: ⚠️ Potential issue | 🟠 Major

Add missing beforeEach import from vitest.

beforeEach is used on line 340 but not imported, causing a compile error.

Suggested fix
-import { afterEach, describe, expect, inject, it, vi } from "vitest";
+import { afterEach, beforeEach, describe, expect, inject, it, vi } from "vitest";

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b2b810 and 47d74e2.

📒 Files selected for processing (8)
  • .changeset/weak-dove-play.md
  • server/hooks/activity.ts
  • server/hooks/block.ts
  • server/hooks/panda.ts
  • server/test/hooks/activity.test.ts
  • server/test/hooks/block.test.ts
  • server/test/utils/keeper.test.ts
  • server/utils/keeper.ts

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (4)
server/test/utils/keeper.test.ts (1)

353-367: ⚠️ Potential issue | 🟡 Minor

Type the setUser spy to avoid implicit any.

vi.fn() defaults to any, which weakens the test type safety and can drift from Sentry’s signature. Consider typing it to the scope method directly.

♻️ Suggested fix
-  const setUser = vi.fn();
+  const setUser = vi.fn<Parameters<Parameters<typeof withScope>[0]>[0]["setUser"]>();
What is the TypeScript signature of Scope#setUser in `@sentry/node` v10.35.0?

As per coding guidelines "Avoid using the any type; leverage TypeScript's type system for type safety".

server/test/hooks/block.test.ts (1)

2220-2234: ⚠️ Potential issue | 🟡 Minor

Type the setUser spy to avoid implicit any.

vi.fn() defaults to any, which weakens the test type safety and can drift from Sentry’s signature. Consider typing it to the scope method directly.

♻️ Suggested fix
-  const setUser = vi.fn();
+  const setUser = vi.fn<Parameters<Parameters<typeof withScope>[0]>[0]["setUser"]>();
What is the TypeScript signature of Scope#setUser in `@sentry/node` v10.35.0?

As per coding guidelines "Avoid using the any type; leverage TypeScript's type system for type safety".

server/hooks/activity.ts (2)

112-112: 🧹 Nitpick | 🔵 Trivial

Avoid calling Object.keys twice on the same value.

This repeats the computation on a single line and violates the guideline that values used in multiple places should be stored once.

♻️ Suggested fix
-    if (Object.keys(accounts).length === 1) setUser({ id: v.parse(Address, Object.keys(accounts)[0]) });
+    const accountIds = Object.keys(accounts);
+    if (accountIds.length === 1) setUser({ id: v.parse(Address, accountIds[0]) });

As per coding guidelines "Do not extract a value into a variable or logic into a function unless it is used in two or more places; keep single-use values and functions inline".


139-143: 🧹 Nitpick | 🔵 Trivial

Pass trace data as an object instead of destructuring fields.

You only destructure to rewrap the same fields. Keeping the object avoids a guideline violation and is more direct.

♻️ Suggested fix
-    const { "sentry-trace": sentryTrace, baggage } = getTraceData();
+    const traceData = getTraceData();
@@
-        continueTrace({ sentryTrace, baggage }, () =>
+        continueTrace(traceData, () =>

As per coding guidelines "Do not destructure object fields only to pass them individually; use the object directly (e.g., await db.insert(accounts).values({ id: crypto.randomUUID(), email: c.req.valid("json").email }))".


ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f165261 and 0417603.

📒 Files selected for processing (8)
  • .changeset/weak-dove-play.md
  • server/hooks/activity.ts
  • server/hooks/block.ts
  • server/hooks/panda.ts
  • server/test/hooks/activity.test.ts
  • server/test/hooks/block.test.ts
  • server/test/utils/keeper.test.ts
  • server/utils/keeper.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
server/test/utils/keeper.test.ts (1)

5-5: ⚠️ Potential issue | 🟡 Minor

Type the setUser spy to match Sentry’s Scope#setUser signature.

Untyped vi.fn() defaults to any and hides the User | null parameter / Scope return.

🔧 Proposed fix
-  const setUser = vi.fn();
+  const setUser = vi.fn<Parameters<Parameters<typeof withScope>[0]>[0]["setUser"]>();
What is the TypeScript signature of Scope#setUser in `@sentry/node` v10?

As per coding guidelines "Avoid using the any type; leverage TypeScript's type system for type safety".

Also applies to: 16-16, 353-366

server/test/hooks/block.test.ts (1)

7-7: ⚠️ Potential issue | 🟡 Minor

Type the setUser spy to match Sentry’s Scope#setUser signature.

Untyped vi.fn() defaults to any and hides the User | null parameter / Scope return.

🔧 Proposed fix
-  const setUser = vi.fn();
+  const setUser = vi.fn<Parameters<Parameters<typeof withScope>[0]>[0]["setUser"]>();
What is the TypeScript signature of Scope#setUser in `@sentry/node` v10?

As per coding guidelines "Avoid using the any type; leverage TypeScript's type system for type safety".

Also applies to: 59-59, 2220-2233


ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0417603 and 6d5d477.

📒 Files selected for processing (8)
  • .changeset/weak-dove-play.md
  • server/hooks/activity.ts
  • server/hooks/block.ts
  • server/hooks/panda.ts
  • server/test/hooks/activity.test.ts
  • server/test/hooks/block.test.ts
  • server/test/utils/keeper.test.ts
  • server/utils/keeper.ts

@cruzdanilo cruzdanilo merged commit 6d5d477 into main Feb 24, 2026
14 checks passed
@cruzdanilo cruzdanilo deleted the id branch February 24, 2026 22:33
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